diff --git a/src/include/mt32emu/Enumerations.h b/src/include/mt32emu/Enumerations.h index bb580ca5b..3cbfdd4c8 100644 --- a/src/include/mt32emu/Enumerations.h +++ b/src/include/mt32emu/Enumerations.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/include/mt32emu/Types.h b/src/include/mt32emu/Types.h index f70e4795c..12e454750 100644 --- a/src/include/mt32emu/Types.h +++ b/src/include/mt32emu/Types.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/include/mt32emu/c_interface/c_interface.h b/src/include/mt32emu/c_interface/c_interface.h index 2ca3a3b04..5653c9051 100644 --- a/src/include/mt32emu/c_interface/c_interface.h +++ b/src/include/mt32emu/c_interface/c_interface.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -24,7 +24,9 @@ #include "c_types.h" #undef MT32EMU_EXPORT +#undef MT32EMU_EXPORT_V #define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT #ifdef __cplusplus extern "C" { @@ -35,24 +37,28 @@ extern "C" { /* === Interface handling === */ /** Returns mt32emu_service_i interface. */ -MT32EMU_EXPORT mt32emu_service_i mt32emu_get_service_i(); +MT32EMU_EXPORT mt32emu_service_i MT32EMU_C_CALL mt32emu_get_service_i(void); #if MT32EMU_EXPORTS_TYPE == 2 #undef MT32EMU_EXPORT +#undef MT32EMU_EXPORT_V #define MT32EMU_EXPORT +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT #endif /** * Returns the version ID of mt32emu_report_handler_i interface the library has been compiled with. * This allows a client to fall-back gracefully instead of silently not receiving expected event reports. */ -MT32EMU_EXPORT mt32emu_report_handler_version mt32emu_get_supported_report_handler_version(); +MT32EMU_EXPORT mt32emu_report_handler_version MT32EMU_C_CALL mt32emu_get_supported_report_handler_version(void); /** * Returns the version ID of mt32emu_midi_receiver_version_i interface the library has been compiled with. * This allows a client to fall-back gracefully instead of silently not receiving expected MIDI messages. */ -MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version(); +MT32EMU_EXPORT mt32emu_midi_receiver_version MT32EMU_C_CALL mt32emu_get_supported_midi_receiver_version(void); + +/* === Utility === */ /** * Returns library version as an integer in format: 0x00MMmmpp, where: @@ -60,67 +66,149 @@ MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver * mm - minor version number * pp - patch number */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_library_version_int(); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_library_version_int(void); /** * Returns library version as a C-string in format: "MAJOR.MINOR.PATCH". */ -MT32EMU_EXPORT const char *mt32emu_get_library_version_string(); +MT32EMU_EXPORT const char * MT32EMU_C_CALL mt32emu_get_library_version_string(void); /** * Returns output sample rate used in emulation of stereo analog circuitry of hardware units for particular analog_output_mode. * See comment for mt32emu_analog_output_mode. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode); /** * Returns the value of analog_output_mode for which the output signal may retain its full frequency spectrum * at the sample rate specified by the target_samplerate argument. * See comment for mt32emu_analog_output_mode. */ -MT32EMU_EXPORT mt32emu_analog_output_mode mt32emu_get_best_analog_output_mode(const double target_samplerate); +MT32EMU_EXPORT mt32emu_analog_output_mode MT32EMU_C_CALL mt32emu_get_best_analog_output_mode(const double target_samplerate); + +/* === ROM handling === */ + +/** + * Retrieves a list of identifiers (as C-strings) of supported machines. Argument machine_ids points to the array of size + * machine_ids_size to be filled. + * Returns the number of identifiers available for retrieval. The size of the target array to be allocated can be found + * by passing NULL in argument machine_ids; argument machine_ids_size is ignored in this case. + */ +MT32EMU_EXPORT_V(2.5) size_t MT32EMU_C_CALL mt32emu_get_machine_ids(const char **machine_ids, size_t machine_ids_size); +/** + * Retrieves a list of identifiers (as C-strings) of supported ROM images. Argument rom_ids points to the array of size + * rom_ids_size to be filled. Optional argument machine_id can be used to indicate a specific machine to retrieve ROM identifiers + * for; if NULL, identifiers of all the ROM images supported by the emulation engine are retrieved. + * Returns the number of ROM identifiers available for retrieval. The size of the target array to be allocated can be found + * by passing NULL in argument rom_ids; argument rom_ids_size is ignored in this case. If argument machine_id contains + * an unrecognised value, 0 is returned. + */ +MT32EMU_EXPORT_V(2.5) size_t MT32EMU_C_CALL mt32emu_get_rom_ids(const char **rom_ids, size_t rom_ids_size, const char *machine_id); + +/** + * Identifies a ROM image the provided data array contains by its SHA1 digest. Optional argument machine_id can be used to indicate + * a specific machine to identify the ROM image for; if NULL, the ROM image is identified for any supported machine. + * A mt32emu_rom_info structure supplied in argument rom_info is filled in accordance with the provided ROM image; unused fields + * are filled with NULLs. If the content of the ROM image is not identified successfully (e.g. when the ROM image is incompatible + * with the specified machine), all fields of rom_info are filled with NULLs. + * Returns MT32EMU_RC_OK upon success or a negative error code otherwise. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_data(mt32emu_rom_info *rom_info, const mt32emu_bit8u *data, size_t data_size, const char *machine_id); +/** + * Loads the content of the file specified by argument filename and identifies a ROM image the file contains by its SHA1 digest. + * Optional argument machine_id can be used to indicate a specific machine to identify the ROM image for; if NULL, the ROM image + * is identified for any supported machine. + * A mt32emu_rom_info structure supplied in argument rom_info is filled in accordance with the provided ROM image; unused fields + * are filled with NULLs. If the content of the file is not identified successfully (e.g. when the ROM image is incompatible + * with the specified machine), all fields of rom_info are filled with NULLs. + * Returns MT32EMU_RC_OK upon success or a negative error code otherwise. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_file(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id); /* == Context-dependent functions == */ /** Initialises a new emulation context and installs custom report handler if non-NULL. */ -MT32EMU_EXPORT mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data); +MT32EMU_EXPORT mt32emu_context MT32EMU_C_CALL mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data); /** Closes and destroys emulation context. */ -MT32EMU_EXPORT void mt32emu_free_context(mt32emu_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_free_context(mt32emu_context context); /** - * Adds new ROM identified by its SHA1 digest to the emulation context replacing previously added ROM of the same type if any. - * Argument sha1_digest can be NULL, in this case the digest will be computed using the actual ROM data. + * Adds a new full ROM data image identified by its SHA1 digest to the emulation context replacing previously added ROM of the same + * type if any. Argument sha1_digest can be NULL, in this case the digest will be computed using the actual ROM data. * If sha1_digest is set to non-NULL, it is assumed being correct and will not be recomputed. - * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * The provided data array is NOT copied and used directly for efficiency. The caller should not deallocate it while the emulation + * context is referring to the ROM data. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). * Returns positive value upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); /** - * Loads a ROM file, identify it by SHA1 digest, and adds it to the emulation context replacing previously added ROM of the same type if any. - * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * Loads a ROM file that contains a full ROM data image, identifies it by the SHA1 digest, and adds it to the emulation context + * replacing previously added ROM of the same type if any. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). * Returns positive value upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_file(mt32emu_context context, const char *filename); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_file(mt32emu_context context, const char *filename); + +/** + * Merges a pair of compatible ROM data image parts into a full image and adds it to the emulation context replacing previously + * added ROM of the same type if any. Each partial image is identified by its SHA1 digest. Arguments partN_sha1_digest can be NULL, + * in this case the digest will be computed using the actual ROM data. If a non-NULL SHA1 value is provided, it is assumed being + * correct and will not be recomputed. The provided data arrays may be deallocated as soon as the function completes. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_data(mt32emu_context context, const mt32emu_bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const mt32emu_bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest); + +/** + * Loads a pair of files that contains compatible parts of a full ROM image, identifies them by the SHA1 digest, merges these + * parts into a full ROM image and adds it to the emulation context replacing previously added ROM of the same type if any. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_files(mt32emu_context context, const char *part1_filename, const char *part2_filename); + +/** + * Loads a file that contains a ROM image of a specific machine, identifies it by the SHA1 digest, and adds it to the emulation + * context. The ROM image can only be identified successfully if it is compatible with the specified machine. + * Full and partial ROM images are supported and handled according to the following rules: + * - a file with any compatible ROM image is added if none (of the same type) exists in the emulation context; + * - a file with any compatible ROM image replaces any image of the same type that is incompatible with the specified machine; + * - a file with a full ROM image replaces the previously added partial ROM of the same type; + * - a file with a partial ROM image is merged with the previously added ROM image if pairable; + * - otherwise, the file is ignored. + * The described behaviour allows the caller application to traverse a directory with ROM files attempting to add each one in turn. + * As soon as both the full control and the full PCM ROM images are added and / or merged, the iteration can be stopped. + * This function doesn't immediately change the state of already opened synth. Newly added ROMs will take effect upon next call of + * mt32emu_open_synth(). + * Returns a positive value in case changes have been made, MT32EMU_RC_OK if the file has been ignored or a negative error code + * upon failure. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_add_machine_rom_file(mt32emu_context context, const char *machine_id, const char *filename); /** * Fills in mt32emu_rom_info structure with identifiers and descriptions of control and PCM ROM files identified and added to the synth context. * If one of the ROM files is not loaded and identified yet, NULL is returned in the corresponding fields of the mt32emu_rom_info structure. */ -MT32EMU_EXPORT void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info); /** * Allows to override the default maximum number of partials playing simultaneously within the emulation session. * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count); /** * Allows to override the default mode for emulation of analogue circuitry of the hardware units within the emulation session. * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); /** * Allows to convert the synthesiser output to any desired sample rate. The samplerate conversion @@ -131,7 +219,7 @@ MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, cons * This function doesn't immediately change the state of already opened synth. * Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate); /** * Several samplerate conversion quality options are provided which allow to trade-off the conversion speed vs. @@ -140,66 +228,79 @@ MT32EMU_EXPORT void mt32emu_set_stereo_output_samplerate(mt32emu_context context * This function doesn't immediately change the state of already opened synth. * Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); /** * Selects new type of the wave generator and renderer to be used during subsequent calls to mt32emu_open_synth(). * By default, MT32EMU_RT_BIT16S is selected. * See mt32emu_renderer_type for details. */ -MT32EMU_EXPORT void mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type); /** * Returns previously selected type of the wave generator and renderer. * See mt32emu_renderer_type for details. */ -MT32EMU_EXPORT mt32emu_renderer_type mt32emu_get_selected_renderer_type(mt32emu_context context); +MT32EMU_EXPORT mt32emu_renderer_type MT32EMU_C_CALL mt32emu_get_selected_renderer_type(mt32emu_context context); /** * Prepares the emulation context to receive MIDI messages and produce output audio data using aforehand added set of ROMs, * and optionally set the maximum partial count and the analog output mode. * Returns MT32EMU_RC_OK upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_open_synth(mt32emu_const_context context); /** Closes the emulation context freeing allocated resources. Added ROMs remain unaffected and ready for reuse. */ -MT32EMU_EXPORT void mt32emu_close_synth(mt32emu_const_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_close_synth(mt32emu_const_context context); /** Returns true if the synth is in completely initialized state, otherwise returns false. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_open(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_open(mt32emu_const_context context); /** * Returns actual sample rate of the fully processed output stereo signal. * If samplerate conversion is used (i.e. when mt32emu_set_stereo_output_samplerate() has been invoked with a non-zero value), * the returned value is the desired output samplerate rounded down to the closest integer. - * Otherwise, the output samplerate is choosen depending on the emulation mode of stereo analog circuitry of hardware units. + * Otherwise, the output samplerate is chosen depending on the emulation mode of stereo analog circuitry of hardware units. * See comment for mt32emu_analog_output_mode for more info. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context); /** * Returns the number of samples produced at the internal synth sample rate (32000 Hz) * that correspond to the given number of samples at the output sample rate. * Intended to facilitate audio time synchronisation. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp); /** * Returns the number of samples produced at the output sample rate * that correspond to the given number of samples at the internal synth sample rate (32000 Hz). * Intended to facilitate audio time synchronisation. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); /** All the enqueued events are processed by the synth immediately. */ -MT32EMU_EXPORT void mt32emu_flush_midi_queue(mt32emu_const_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_flush_midi_queue(mt32emu_const_context context); /** * Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified. * The queue is flushed before reallocation. * Returns the actual queue size being used. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size); + +/** + * Configures the SysEx storage of the internal MIDI event queue. + * Supplying 0 in the storage_buffer_size argument makes the SysEx data stored + * in multiple dynamically allocated buffers per MIDI event. These buffers are only disposed + * when a new MIDI event replaces the SysEx event in the queue, thus never on the rendering thread. + * This is the default behaviour. + * In contrast, when a positive value is specified, SysEx data will be stored in a single preallocated buffer, + * which makes this kind of storage safe for use in a realtime thread. Additionally, the space retained + * by a SysEx event, that has been processed and thus is no longer necessary, is disposed instantly. + * Note, the queue is flushed and recreated in the process so that its size remains intact. + */ +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); /** * Installs custom MIDI receiver object intended for receiving MIDI messages generated by MIDI stream parser. @@ -207,13 +308,13 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_co * By default, parsed short MIDI messages and System Exclusive messages are sent to the synth input MIDI queue. * This function allows to override default behaviour. If midi_receiver argument is set to NULL, the default behaviour is restored. */ -MT32EMU_EXPORT void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); /** * Returns current value of the global counter of samples rendered since the synth was created (at the native sample rate 32000 Hz). * This method helps to compute accurate timestamp of a MIDI message to use with the methods below. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context); /* Enqueues a MIDI event for subsequent playback. * The MIDI event will be processed not before the specified timestamp. @@ -230,7 +331,7 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). */ -MT32EMU_EXPORT void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); /** * Parses a block of raw MIDI bytes and enqueues parsed MIDI messages to play at specified time. @@ -238,31 +339,31 @@ MT32EMU_EXPORT void mt32emu_parse_stream(mt32emu_const_context context, const mt * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). */ -MT32EMU_EXPORT void mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); /** * Enqueues a single mt32emu_bit32u-encoded short MIDI message with full processing ASAP. * The short MIDI message may contain no status byte, the running status is used in this case. * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. */ -MT32EMU_EXPORT void mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message); /** * Enqueues a single mt32emu_bit32u-encoded short MIDI message to play at specified time with full processing. * The short MIDI message may contain no status byte, the running status is used in this case. * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. */ -MT32EMU_EXPORT void mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); /** Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg); /** Enqueues a single well formed System Exclusive MIDI message to be processed ASAP. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** Enqueues a single short MIDI message to play at specified time. The message must contain a status byte. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); /** Enqueues a single well formed System Exclusive MIDI message to play at specified time. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); /* WARNING: * The methods below don't ensure minimum 1-sample delay between sequential MIDI events, @@ -274,66 +375,73 @@ MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context c * Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg); /** * Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); /** * Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** * Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** Allows to disable wet reverb output altogether. */ -MT32EMU_EXPORT void mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); /** Returns whether wet reverb output is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_enabled(mt32emu_const_context context); /** * Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. * This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state * of the reverb model is reset to default. */ -MT32EMU_EXPORT void mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); /** Returns whether reverb settings are overridden. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_overridden(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_overridden(mt32emu_const_context context); /** * Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. * Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. * When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced * (these include CM-32L and LAPC-I). */ -MT32EMU_EXPORT void mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); /** Returns whether reverb is in old MT-32 compatibility mode. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context); /** Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context); + +/** + * If enabled, reverb buffers for all modes are kept around allocated all the time to avoid memory + * allocating/freeing in the rendering thread, which may be required for realtime operation. + * Otherwise, reverb buffers that are not in use are deleted to save memory (the default behaviour). + */ +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_preallocate_reverb_memory(mt32emu_const_context context, const mt32emu_boolean enabled); /** Sets new DAC input mode. See mt32emu_dac_input_mode for details. */ -MT32EMU_EXPORT void mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode); /** Returns current DAC input mode. See mt32emu_dac_input_mode for details. */ -MT32EMU_EXPORT mt32emu_dac_input_mode mt32emu_get_dac_input_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_dac_input_mode MT32EMU_C_CALL mt32emu_get_dac_input_mode(mt32emu_const_context context); /** Sets new MIDI delay mode. See mt32emu_midi_delay_mode for details. */ -MT32EMU_EXPORT void mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); /** Returns current MIDI delay mode. See mt32emu_midi_delay_mode for details. */ -MT32EMU_EXPORT mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_midi_delay_mode MT32EMU_C_CALL mt32emu_get_midi_delay_mode(mt32emu_const_context context); /** * Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume, * it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with mt32emu_set_reverb_output_gain() * it offers to the user a capability to control the gain of reverb and non-reverb output channels independently. */ -MT32EMU_EXPORT void mt32emu_set_output_gain(mt32emu_const_context context, float gain); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_output_gain(mt32emu_const_context context, float gain); /** Returns current output gain factor for synth output channels. */ -MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context); +MT32EMU_EXPORT float MT32EMU_C_CALL mt32emu_get_output_gain(mt32emu_const_context context); /** * Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output @@ -345,14 +453,34 @@ MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context); * there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 * of that for LA32 analogue output. This factor is applied to the reverb output gain. */ -MT32EMU_EXPORT void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain); /** Returns current output gain factor for reverb wet output channels. */ -MT32EMU_EXPORT float mt32emu_get_reverb_output_gain(mt32emu_const_context context); +MT32EMU_EXPORT float MT32EMU_C_CALL mt32emu_get_reverb_output_gain(mt32emu_const_context context); + +/** + * Sets (or removes) an override for the current volume (output level) on a specific part. + * When the part volume is overridden, the MIDI controller Volume (7) on the MIDI channel this part is assigned to + * has no effect on the output level of this part. Similarly, the output level value set on this part via a SysEx that + * modifies the Patch temp structure is disregarded. + * To enable the override mode, argument volumeOverride should be in range 0..100, setting a value outside this range + * disables the previously set override, if any. + * Note: Setting volumeOverride to 0 mutes the part completely, meaning no sound is generated at all. + * This is unlike the behaviour of real devices - setting 0 volume on a part may leave it still producing + * sound at a very low level. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u volume_override); +/** + * Returns the overridden volume previously set on a specific part; a value outside the range 0..100 means no override + * is currently in effect. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_bit8u MT32EMU_C_CALL mt32emu_get_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number); /** Swaps left and right output channels. */ -MT32EMU_EXPORT void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether left and right output channels are swapped. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context); /** * Allows to toggle the NiceAmpRamp mode. @@ -362,9 +490,36 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_ * We also prefer the quality improvement over the emulation accuracy, * so this mode is enabled by default. */ -MT32EMU_EXPORT void mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether NiceAmpRamp mode is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context); + +/** + * Allows to toggle the NicePanning mode. + * Despite the Roland's manual specifies allowed panpot values in range 0-14, + * the LA-32 only receives 3-bit pan setting in fact. In particular, this + * makes it impossible to set the "middle" panning for a single partial. + * In the NicePanning mode, we enlarge the pan setting accuracy to 4 bits + * making it smoother thus sacrificing the emulation accuracy. + * This mode is disabled by default. + */ +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_panning_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +/** Returns whether NicePanning mode is enabled. */ +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_panning_enabled(mt32emu_const_context context); + +/** + * Allows to toggle the NicePartialMixing mode. + * LA-32 is known to mix partials either in-phase (so that they are added) + * or in counter-phase (so that they are subtracted instead). + * In some cases, this quirk isn't highly desired because a pair of closely + * sounding partials may occasionally cancel out. + * In the NicePartialMixing mode, the mixing is always performed in-phase, + * thus making the behaviour more predictable. + * This mode is disabled by default. + */ +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_partial_mixing_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +/** Returns whether NicePartialMixing mode is enabled. */ +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_partial_mixing_enabled(mt32emu_const_context context); /** * Renders samples to the specified output stream as if they were sampled at the analog stereo output at the desired sample rate. @@ -372,9 +527,9 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_co * mode of analog circuitry emulation. See mt32emu_analog_output_mode. * The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering. */ -MT32EMU_EXPORT void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); /** Same as above but outputs to a float stereo stream. */ -MT32EMU_EXPORT void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len); /** * Renders samples to the specified output streams as if they appeared at the DAC entrance. @@ -382,25 +537,25 @@ MT32EMU_EXPORT void mt32emu_render_float(mt32emu_const_context context, float *s * NULL may be specified in place of any or all of the stream buffers to skip it. * The length is in samples, not bytes. Uses NATIVE byte ordering. */ -MT32EMU_EXPORT void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); /** Same as above but outputs to float streams. */ -MT32EMU_EXPORT void mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); /** Returns true when there is at least one active partial, otherwise false. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_has_active_partials(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_has_active_partials(mt32emu_const_context context); /** Returns true if mt32emu_has_active_partials() returns true, or reverb is (somewhat unreliably) detected as being active. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_active(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_active(mt32emu_const_context context); /** Returns the maximum number of partials playing simultaneously. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_partial_count(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_partial_count(mt32emu_const_context context); /** * Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1, * total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active * non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_part_states(mt32emu_const_context context); /** * Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials @@ -408,7 +563,7 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context cont * The array must be large enough to accommodate states of all the partials. * @see getPartialCount() */ -MT32EMU_EXPORT void mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states); /** * Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough @@ -416,16 +571,71 @@ MT32EMU_EXPORT void mt32emu_get_partial_states(mt32emu_const_context context, mt * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. * Returns the number of currently playing notes on the specified part. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); /** * Returns name of the patch set on the specified part. * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + * The returned value is a null-terminated string which is guaranteed to remain valid until the next call to one of functions + * that perform sample rendering or immediate SysEx processing (e.g. mt32emu_play_sysex_now). */ -MT32EMU_EXPORT const char *mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number); +MT32EMU_EXPORT const char * MT32EMU_C_CALL mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number); + +/** + * Retrieves the name of the sound group the timbre identified by arguments timbre_group and timbre_number is associated with. + * Values 0-3 of timbre_group correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + * For all but the RHYTHM timbre bank, allowed values of timbre_number are in range 0-63. The number of timbres + * contained in the RHYTHM bank depends on the used control ROM version. + * The argument sound_group_name must point to an array of at least 8 characters. The result is a null-terminated string. + * Returns whether the specified timbre has been found and the result written in sound_group_name. + */ +MT32EMU_EXPORT_V(2.7) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_group_name(mt32emu_const_context context, char *sound_group_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); +/** + * Retrieves the name of the timbre identified by arguments timbre_group and timbre_number. + * Values 0-3 of timbre_group correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + * For all but the RHYTHM timbre bank, allowed values of timbre_number are in range 0-63. The number of timbres + * contained in the RHYTHM bank depends on the used control ROM version. + * The argument sound_name must point to an array of at least 11 characters. The result is a null-terminated string. + * Returns whether the specified timbre has been found and the result written in sound_name. + */ +MT32EMU_EXPORT_V(2.7) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_name(mt32emu_const_context context, char *sound_name, mt32emu_bit8u timbreGroup, mt32emu_bit8u timbreNumber); /** Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). */ -MT32EMU_EXPORT void mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); + +/** + * Retrieves the current state of the emulated MT-32 display facilities. + * Typically, the state is updated during the rendering. When that happens, a related callback from mt32emu_report_handler_i_v1 + * is invoked. However, there might be no need to invoke this method after each update, e.g. when the render buffer is just + * a few milliseconds long. + * The argument target_buffer must point to an array of at least 21 characters. The result is a null-terminated string. + * The argument narrow_lcd enables a condensed representation of the displayed information in some cases. This is mainly intended + * to route the result to a hardware LCD that is only 16 characters wide. Automatic scrolling of longer strings is not supported. + * Returns whether the MIDI MESSAGE LED is ON and fills the target_buffer parameter. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_display_state(mt32emu_const_context context, char *target_buffer, const mt32emu_boolean narrow_lcd); + +/** + * Resets the emulated LCD to the main mode (Master Volume). This has the same effect as pressing the Master Volume button + * while the display shows some other message. Useful for the new-gen devices as those require a special Display Reset SysEx + * to return to the main mode e.g. from showing a custom display message or a checksum error. + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_main_display_mode(mt32emu_const_context context); + +/** + * Permits to select an arbitrary display emulation model that does not necessarily match the actual behaviour implemented + * in the control ROM version being used. + * Invoking this method with the argument set to true forces emulation of the old-gen MT-32 display features. + * Otherwise, emulation of the new-gen devices is enforced (these include CM-32L and LAPC-I as if these were connected to an LCD). + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_display_compatibility(mt32emu_const_context context, mt32emu_boolean old_mt32_compatibility_enabled); +/** Returns whether the currently configured features of the emulated display are compatible with the old-gen MT-32 devices. */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_is_display_old_mt32_compatible(mt32emu_const_context context); +/** + * Returns whether the emulated display features configured by default depending on the actual control ROM version + * are compatible with the old-gen MT-32 devices. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_display_old_mt32_compatible(mt32emu_const_context context); #ifdef __cplusplus } // extern "C" diff --git a/src/include/mt32emu/c_interface/c_types.h b/src/include/mt32emu/c_interface/c_types.h index db612e282..8928bfeae 100644 --- a/src/include/mt32emu/c_interface/c_types.h +++ b/src/include/mt32emu/c_interface/c_types.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -27,6 +27,12 @@ #include "../Enumerations.h" #undef MT32EMU_C_ENUMERATIONS +#ifdef _WIN32 +# define MT32EMU_C_CALL __cdecl +#else +# define MT32EMU_C_CALL +#endif + typedef unsigned int mt32emu_bit32u; typedef signed int mt32emu_bit32s; typedef unsigned short int mt32emu_bit16u; @@ -45,6 +51,8 @@ typedef enum { MT32EMU_RC_OK = 0, MT32EMU_RC_ADDED_CONTROL_ROM = 1, MT32EMU_RC_ADDED_PCM_ROM = 2, + MT32EMU_RC_ADDED_PARTIAL_CONTROL_ROM = 3, + MT32EMU_RC_ADDED_PARTIAL_PCM_ROM = 4, /* Definite error occurred. */ MT32EMU_RC_ROM_NOT_IDENTIFIED = -1, @@ -53,6 +61,8 @@ typedef enum { MT32EMU_RC_MISSING_ROMS = -4, MT32EMU_RC_NOT_OPENED = -5, MT32EMU_RC_QUEUE_FULL = -6, + MT32EMU_RC_ROMS_NOT_PAIRABLE = -7, + MT32EMU_RC_MACHINE_NOT_IDENTIFIED = -8, /* Undefined error occurred. */ MT32EMU_RC_FAILED = -100 @@ -107,7 +117,8 @@ typedef struct { /** Report handler interface versions */ typedef enum { MT32EMU_REPORT_HANDLER_VERSION_0 = 0, - MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_0 + MT32EMU_REPORT_HANDLER_VERSION_1 = 1, + MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_1 } mt32emu_report_handler_version; /** MIDI receiver interface versions */ @@ -121,7 +132,11 @@ typedef enum { MT32EMU_SERVICE_VERSION_0 = 0, MT32EMU_SERVICE_VERSION_1 = 1, MT32EMU_SERVICE_VERSION_2 = 2, - MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_2 + MT32EMU_SERVICE_VERSION_3 = 3, + MT32EMU_SERVICE_VERSION_4 = 4, + MT32EMU_SERVICE_VERSION_5 = 5, + MT32EMU_SERVICE_VERSION_6 = 6, + MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_6 } mt32emu_service_version; /* === Report Handler Interface === */ @@ -129,42 +144,59 @@ typedef enum { typedef union mt32emu_report_handler_i mt32emu_report_handler_i; /** Interface for handling reported events (initial version) */ -typedef struct { - /** Returns the actual interface version ID */ - mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); - - /** Callback for debug messages, in vprintf() format */ - void (*printDebug)(void *instance_data, const char *fmt, va_list list); - /** Callbacks for reporting errors */ - void (*onErrorControlROM)(void *instance_data); - void (*onErrorPCMROM)(void *instance_data); - /** Callback for reporting about displaying a new custom message on LCD */ - void (*showLCDMessage)(void *instance_data, const char *message); - /** Callback for reporting actual processing of a MIDI message */ - void (*onMIDIMessagePlayed)(void *instance_data); +#define MT32EMU_REPORT_HANDLER_I_V0 \ + /** Returns the actual interface version ID */ \ + mt32emu_report_handler_version (MT32EMU_C_CALL *getVersionID)(mt32emu_report_handler_i i); \ +\ + /** Callback for debug messages, in vprintf() format */ \ + void (MT32EMU_C_CALL *printDebug)(void *instance_data, const char *fmt, va_list list); \ + /** Callbacks for reporting errors */ \ + void (MT32EMU_C_CALL *onErrorControlROM)(void *instance_data); \ + void (MT32EMU_C_CALL *onErrorPCMROM)(void *instance_data); \ + /** Callback for reporting about displaying a new custom message on LCD */ \ + void (MT32EMU_C_CALL *showLCDMessage)(void *instance_data, const char *message); \ + /** Callback for reporting actual processing of a MIDI message */ \ + void (MT32EMU_C_CALL *onMIDIMessagePlayed)(void *instance_data); \ /** * Callback for reporting an overflow of the input MIDI queue. * Returns MT32EMU_BOOL_TRUE if a recovery action was taken * and yet another attempt to enqueue the MIDI event is desired. - */ - mt32emu_boolean (*onMIDIQueueOverflow)(void *instance_data); + */ \ + mt32emu_boolean (MT32EMU_C_CALL *onMIDIQueueOverflow)(void *instance_data); \ /** * Callback invoked when a System Realtime MIDI message is detected in functions * mt32emu_parse_stream and mt32emu_play_short_message and the likes. - */ - void (*onMIDISystemRealtime)(void *instance_data, mt32emu_bit8u system_realtime); - /** Callbacks for reporting system events */ - void (*onDeviceReset)(void *instance_data); - void (*onDeviceReconfig)(void *instance_data); - /** Callbacks for reporting changes of reverb settings */ - void (*onNewReverbMode)(void *instance_data, mt32emu_bit8u mode); - void (*onNewReverbTime)(void *instance_data, mt32emu_bit8u time); - void (*onNewReverbLevel)(void *instance_data, mt32emu_bit8u level); - /** Callbacks for reporting various information */ - void (*onPolyStateChanged)(void *instance_data, mt32emu_bit8u part_num); - void (*onProgramChanged)(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name); + */ \ + void (MT32EMU_C_CALL *onMIDISystemRealtime)(void *instance_data, mt32emu_bit8u system_realtime); \ + /** Callbacks for reporting system events */ \ + void (MT32EMU_C_CALL *onDeviceReset)(void *instance_data); \ + void (MT32EMU_C_CALL *onDeviceReconfig)(void *instance_data); \ + /** Callbacks for reporting changes of reverb settings */ \ + void (MT32EMU_C_CALL *onNewReverbMode)(void *instance_data, mt32emu_bit8u mode); \ + void (MT32EMU_C_CALL *onNewReverbTime)(void *instance_data, mt32emu_bit8u time); \ + void (MT32EMU_C_CALL *onNewReverbLevel)(void *instance_data, mt32emu_bit8u level); \ + /** Callbacks for reporting various information */ \ + void (MT32EMU_C_CALL *onPolyStateChanged)(void *instance_data, mt32emu_bit8u part_num); \ + void (MT32EMU_C_CALL *onProgramChanged)(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name); + +#define MT32EMU_REPORT_HANDLER_I_V1 \ + /** + * Invoked to signal about a change of the emulated LCD state. Use mt32emu_get_display_state to retrieve the actual data. + * This callback will not be invoked on further changes, until the client retrieves the LCD state. + */ \ + void (MT32EMU_C_CALL *onLCDStateUpdated)(void *instance_data); \ + /** Invoked when the emulated MIDI MESSAGE LED changes state. The led_state parameter represents whether the LED is ON. */ \ + void (MT32EMU_C_CALL *onMidiMessageLEDStateUpdated)(void *instance_data, mt32emu_boolean led_state); + +typedef struct { + MT32EMU_REPORT_HANDLER_I_V0 } mt32emu_report_handler_i_v0; +typedef struct { + MT32EMU_REPORT_HANDLER_I_V0 + MT32EMU_REPORT_HANDLER_I_V1 +} mt32emu_report_handler_i_v1; + /** * Extensible interface for handling reported events. * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. @@ -172,8 +204,12 @@ typedef struct { */ union mt32emu_report_handler_i { const mt32emu_report_handler_i_v0 *v0; + const mt32emu_report_handler_i_v1 *v1; }; +#undef MT32EMU_REPORT_HANDLER_I_V0 +#undef MT32EMU_REPORT_HANDLER_I_V1 + /* === MIDI Receiver Interface === */ typedef union mt32emu_midi_receiver_i mt32emu_midi_receiver_i; @@ -181,16 +217,16 @@ typedef union mt32emu_midi_receiver_i mt32emu_midi_receiver_i; /** Interface for receiving MIDI messages generated by MIDI stream parser (initial version) */ typedef struct { /** Returns the actual interface version ID */ - mt32emu_midi_receiver_version (*getVersionID)(mt32emu_midi_receiver_i i); + mt32emu_midi_receiver_version (MT32EMU_C_CALL *getVersionID)(mt32emu_midi_receiver_i i); /** Invoked when a complete short MIDI message is parsed in the input MIDI stream. */ - void (*handleShortMessage)(void *instance_data, const mt32emu_bit32u message); + void (MT32EMU_C_CALL *handleShortMessage)(void *instance_data, const mt32emu_bit32u message); /** Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream. */ - void (*handleSysex)(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length); + void (MT32EMU_C_CALL *handleSysex)(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length); /** Invoked when a System Realtime MIDI message is parsed in the input MIDI stream. */ - void (*handleSystemRealtimeMessage)(void *instance_data, const mt32emu_bit8u realtime); + void (MT32EMU_C_CALL *handleSystemRealtimeMessage)(void *instance_data, const mt32emu_bit8u realtime); } mt32emu_midi_receiver_i_v0; /** @@ -215,93 +251,124 @@ typedef union mt32emu_service_i mt32emu_service_i; */ #define MT32EMU_SERVICE_I_V0 \ /** Returns the actual interface version ID */ \ - mt32emu_service_version (*getVersionID)(mt32emu_service_i i); \ - mt32emu_report_handler_version (*getSupportedReportHandlerVersionID)(); \ - mt32emu_midi_receiver_version (*getSupportedMIDIReceiverVersionID)(); \ + mt32emu_service_version (MT32EMU_C_CALL *getVersionID)(mt32emu_service_i i); \ + mt32emu_report_handler_version (MT32EMU_C_CALL *getSupportedReportHandlerVersionID)(void); \ + mt32emu_midi_receiver_version (MT32EMU_C_CALL *getSupportedMIDIReceiverVersionID)(void); \ \ - mt32emu_bit32u (*getLibraryVersionInt)(); \ - const char *(*getLibraryVersionString)(); \ + mt32emu_bit32u (MT32EMU_C_CALL *getLibraryVersionInt)(void); \ + const char *(MT32EMU_C_CALL *getLibraryVersionString)(void); \ \ - mt32emu_bit32u (*getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); \ + mt32emu_bit32u (MT32EMU_C_CALL *getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); \ \ - mt32emu_context (*createContext)(mt32emu_report_handler_i report_handler, void *instance_data); \ - void (*freeContext)(mt32emu_context context); \ - mt32emu_return_code (*addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); \ - mt32emu_return_code (*addROMFile)(mt32emu_context context, const char *filename); \ - void (*getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); \ - void (*setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); \ - void (*setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); \ - mt32emu_return_code (*openSynth)(mt32emu_const_context context); \ - void (*closeSynth)(mt32emu_const_context context); \ - mt32emu_boolean (*isOpen)(mt32emu_const_context context); \ - mt32emu_bit32u (*getActualStereoOutputSamplerate)(mt32emu_const_context context); \ - void (*flushMIDIQueue)(mt32emu_const_context context); \ - mt32emu_bit32u (*setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); \ - void (*setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); \ + mt32emu_context (MT32EMU_C_CALL *createContext)(mt32emu_report_handler_i report_handler, void *instance_data); \ + void (MT32EMU_C_CALL *freeContext)(mt32emu_context context); \ + mt32emu_return_code (MT32EMU_C_CALL *addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); \ + mt32emu_return_code (MT32EMU_C_CALL *addROMFile)(mt32emu_context context, const char *filename); \ + void (MT32EMU_C_CALL *getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); \ + void (MT32EMU_C_CALL *setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); \ + void (MT32EMU_C_CALL *setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); \ + mt32emu_return_code (MT32EMU_C_CALL *openSynth)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *closeSynth)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isOpen)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getActualStereoOutputSamplerate)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *flushMIDIQueue)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); \ + void (MT32EMU_C_CALL *setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); \ \ - void (*parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); \ - void (*parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); \ - void (*playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); \ - void (*playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); \ - mt32emu_return_code (*playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); \ - mt32emu_return_code (*playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ - mt32emu_return_code (*playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); \ - mt32emu_return_code (*playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); \ + void (MT32EMU_C_CALL *parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); \ + void (MT32EMU_C_CALL *parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); \ + void (MT32EMU_C_CALL *playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); \ + void (MT32EMU_C_CALL *playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); \ + mt32emu_return_code (MT32EMU_C_CALL *playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); \ + mt32emu_return_code (MT32EMU_C_CALL *playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + mt32emu_return_code (MT32EMU_C_CALL *playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); \ + mt32emu_return_code (MT32EMU_C_CALL *playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); \ \ - void (*playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); \ - void (*playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); \ - void (*playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ - void (*writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); \ + void (MT32EMU_C_CALL *playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); \ + void (MT32EMU_C_CALL *playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ \ - void (*setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); \ - mt32emu_boolean (*isReverbEnabled)(mt32emu_const_context context); \ - void (*setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); \ - mt32emu_boolean (*isReverbOverridden)(mt32emu_const_context context); \ - void (*setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); \ - mt32emu_boolean (*isMT32ReverbCompatibilityMode)(mt32emu_const_context context); \ - mt32emu_boolean (*isDefaultReverbMT32Compatible)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isReverbEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); \ + mt32emu_boolean (MT32EMU_C_CALL *isReverbOverridden)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); \ + mt32emu_boolean (MT32EMU_C_CALL *isMT32ReverbCompatibilityMode)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isDefaultReverbMT32Compatible)(mt32emu_const_context context); \ \ - void (*setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); \ - mt32emu_dac_input_mode (*getDACInputMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); \ + mt32emu_dac_input_mode (MT32EMU_C_CALL *getDACInputMode)(mt32emu_const_context context); \ \ - void (*setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); \ - mt32emu_midi_delay_mode (*getMIDIDelayMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); \ + mt32emu_midi_delay_mode (MT32EMU_C_CALL *getMIDIDelayMode)(mt32emu_const_context context); \ \ - void (*setOutputGain)(mt32emu_const_context context, float gain); \ - float (*getOutputGain)(mt32emu_const_context context); \ - void (*setReverbOutputGain)(mt32emu_const_context context, float gain); \ - float (*getReverbOutputGain)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setOutputGain)(mt32emu_const_context context, float gain); \ + float (MT32EMU_C_CALL *getOutputGain)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbOutputGain)(mt32emu_const_context context, float gain); \ + float (MT32EMU_C_CALL *getReverbOutputGain)(mt32emu_const_context context); \ \ - void (*setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isReversedStereoEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isReversedStereoEnabled)(mt32emu_const_context context); \ \ - void (*renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); \ - void (*renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); \ - void (*renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); \ - void (*renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); \ \ - mt32emu_boolean (*hasActivePartials)(mt32emu_const_context context); \ - mt32emu_boolean (*isActive)(mt32emu_const_context context); \ - mt32emu_bit32u (*getPartialCount)(mt32emu_const_context context); \ - mt32emu_bit32u (*getPartStates)(mt32emu_const_context context); \ - void (*getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); \ - mt32emu_bit32u (*getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); \ - const char *(*getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); \ - void (*readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); + mt32emu_boolean (MT32EMU_C_CALL *hasActivePartials)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isActive)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPartialCount)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPartStates)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); \ + const char *(MT32EMU_C_CALL *getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); \ + void (MT32EMU_C_CALL *readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); #define MT32EMU_SERVICE_I_V1 \ - mt32emu_analog_output_mode (*getBestAnalogOutputMode)(const double target_samplerate); \ - void (*setStereoOutputSampleRate)(mt32emu_context context, const double samplerate); \ - void (*setSamplerateConversionQuality)(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); \ - void (*selectRendererType)(mt32emu_context context, mt32emu_renderer_type renderer_type); \ - mt32emu_renderer_type (*getSelectedRendererType)(mt32emu_context context); \ - mt32emu_bit32u (*convertOutputToSynthTimestamp)(mt32emu_const_context context, mt32emu_bit32u output_timestamp); \ - mt32emu_bit32u (*convertSynthToOutputTimestamp)(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); + mt32emu_analog_output_mode (MT32EMU_C_CALL *getBestAnalogOutputMode)(const double target_samplerate); \ + void (MT32EMU_C_CALL *setStereoOutputSampleRate)(mt32emu_context context, const double samplerate); \ + void (MT32EMU_C_CALL *setSamplerateConversionQuality)(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); \ + void (MT32EMU_C_CALL *selectRendererType)(mt32emu_context context, mt32emu_renderer_type renderer_type); \ + mt32emu_renderer_type (MT32EMU_C_CALL *getSelectedRendererType)(mt32emu_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *convertOutputToSynthTimestamp)(mt32emu_const_context context, mt32emu_bit32u output_timestamp); \ + mt32emu_bit32u (MT32EMU_C_CALL *convertSynthToOutputTimestamp)(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); #define MT32EMU_SERVICE_I_V2 \ - mt32emu_bit32u (*getInternalRenderedSampleCount)(mt32emu_const_context context); \ - void (*setNiceAmpRampEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isNiceAmpRampEnabled)(mt32emu_const_context context); + mt32emu_bit32u (MT32EMU_C_CALL *getInternalRenderedSampleCount)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setNiceAmpRampEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNiceAmpRampEnabled)(mt32emu_const_context context); + +#define MT32EMU_SERVICE_I_V3 \ + void (MT32EMU_C_CALL *setNicePanningEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNicePanningEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setNicePartialMixingEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNicePartialMixingEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *preallocateReverbMemory)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + void (MT32EMU_C_CALL *configureMIDIEventQueueSysexStorage)(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); + +#define MT32EMU_SERVICE_I_V4 \ + size_t (MT32EMU_C_CALL *getMachineIDs)(const char **machine_ids, size_t machine_ids_size); \ + size_t (MT32EMU_C_CALL *getROMIDs)(const char **rom_ids, size_t rom_ids_size, const char *machine_id); \ + mt32emu_return_code (MT32EMU_C_CALL *identifyROMData)(mt32emu_rom_info *rom_info, const mt32emu_bit8u *data, size_t data_size, const char *machine_id); \ + mt32emu_return_code (MT32EMU_C_CALL *identifyROMFile)(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id); \ +\ + mt32emu_return_code (MT32EMU_C_CALL *mergeAndAddROMData)(mt32emu_context context, const mt32emu_bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const mt32emu_bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest); \ + mt32emu_return_code (MT32EMU_C_CALL *mergeAndAddROMFiles)(mt32emu_context context, const char *part1_filename, const char *part2_filename); \ + mt32emu_return_code (MT32EMU_C_CALL *addMachineROMFile)(mt32emu_context context, const char *machine_id, const char *filename); + +#define MT32EMU_SERVICE_I_V5 \ + mt32emu_boolean (MT32EMU_C_CALL *getDisplayState)(mt32emu_const_context context, char *target_buffer, const mt32emu_boolean narrow_lcd); \ + void (MT32EMU_C_CALL *setMainDisplayMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setDisplayCompatibility)(mt32emu_const_context context, mt32emu_boolean old_mt32_compatibility_enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isDisplayOldMT32Compatible)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isDefaultDisplayOldMT32Compatible)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setPartVolumeOverride)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u volume_override); \ + mt32emu_bit8u (MT32EMU_C_CALL *getPartVolumeOverride)(mt32emu_const_context context, mt32emu_bit8u part_number); + +#define MT32EMU_SERVICE_I_V6 \ + mt32emu_boolean (MT32EMU_C_CALL *getSoundGroupName)(mt32emu_const_context context, char *sound_group_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); \ + mt32emu_boolean (MT32EMU_C_CALL *getSoundName)(mt32emu_const_context context, char *sound_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); typedef struct { MT32EMU_SERVICE_I_V0 @@ -318,6 +385,40 @@ typedef struct { MT32EMU_SERVICE_I_V2 } mt32emu_service_i_v2; +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 +} mt32emu_service_i_v3; + +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 +} mt32emu_service_i_v4; + +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 + MT32EMU_SERVICE_I_V5 +} mt32emu_service_i_v5; + +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 + MT32EMU_SERVICE_I_V5 + MT32EMU_SERVICE_I_V6 +} mt32emu_service_i_v6; + /** * Extensible interface for all the library services. * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. @@ -327,10 +428,18 @@ union mt32emu_service_i { const mt32emu_service_i_v0 *v0; const mt32emu_service_i_v1 *v1; const mt32emu_service_i_v2 *v2; + const mt32emu_service_i_v3 *v3; + const mt32emu_service_i_v4 *v4; + const mt32emu_service_i_v5 *v5; + const mt32emu_service_i_v6 *v6; }; #undef MT32EMU_SERVICE_I_V0 #undef MT32EMU_SERVICE_I_V1 #undef MT32EMU_SERVICE_I_V2 +#undef MT32EMU_SERVICE_I_V3 +#undef MT32EMU_SERVICE_I_V4 +#undef MT32EMU_SERVICE_I_V5 +#undef MT32EMU_SERVICE_I_V6 #endif /* #ifndef MT32EMU_C_TYPES_H */ diff --git a/src/include/mt32emu/c_interface/cpp_interface.h b/src/include/mt32emu/c_interface/cpp_interface.h index 3b02c0325..d22897b74 100644 --- a/src/include/mt32emu/c_interface/cpp_interface.h +++ b/src/include/mt32emu/c_interface/cpp_interface.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,10 +41,17 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_library_version_string i.v0->getLibraryVersionString #define mt32emu_get_stereo_output_samplerate i.v0->getStereoOutputSamplerate #define mt32emu_get_best_analog_output_mode iV1()->getBestAnalogOutputMode +#define mt32emu_get_machine_ids iV4()->getMachineIDs +#define mt32emu_get_rom_ids iV4()->getROMIDs +#define mt32emu_identify_rom_data iV4()->identifyROMData +#define mt32emu_identify_rom_file iV4()->identifyROMFile #define mt32emu_create_context i.v0->createContext #define mt32emu_free_context i.v0->freeContext #define mt32emu_add_rom_data i.v0->addROMData #define mt32emu_add_rom_file i.v0->addROMFile +#define mt32emu_merge_and_add_rom_data iV4()->mergeAndAddROMData +#define mt32emu_merge_and_add_rom_files iV4()->mergeAndAddROMFiles +#define mt32emu_add_machine_rom_file iV4()->addMachineROMFile #define mt32emu_get_rom_info i.v0->getROMInfo #define mt32emu_set_partial_count i.v0->setPartialCount #define mt32emu_set_analog_output_mode i.v0->setAnalogOutputMode @@ -60,6 +67,7 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_convert_synth_to_output_timestamp iV1()->convertSynthToOutputTimestamp #define mt32emu_flush_midi_queue i.v0->flushMIDIQueue #define mt32emu_set_midi_event_queue_size i.v0->setMIDIEventQueueSize +#define mt32emu_configure_midi_event_queue_sysex_storage iV3()->configureMIDIEventQueueSysexStorage #define mt32emu_set_midi_receiver i.v0->setMIDIReceiver #define mt32emu_get_internal_rendered_sample_count iV2()->getInternalRenderedSampleCount #define mt32emu_parse_stream i.v0->parseStream @@ -81,6 +89,7 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_set_reverb_compatibility_mode i.v0->setReverbCompatibilityMode #define mt32emu_is_mt32_reverb_compatibility_mode i.v0->isMT32ReverbCompatibilityMode #define mt32emu_is_default_reverb_mt32_compatible i.v0->isDefaultReverbMT32Compatible +#define mt32emu_preallocate_reverb_memory iV3()->preallocateReverbMemory #define mt32emu_set_dac_input_mode i.v0->setDACInputMode #define mt32emu_get_dac_input_mode i.v0->getDACInputMode #define mt32emu_set_midi_delay_mode i.v0->setMIDIDelayMode @@ -89,10 +98,16 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_output_gain i.v0->getOutputGain #define mt32emu_set_reverb_output_gain i.v0->setReverbOutputGain #define mt32emu_get_reverb_output_gain i.v0->getReverbOutputGain +#define mt32emu_set_part_volume_override iV5()->setPartVolumeOverride +#define mt32emu_get_part_volume_override iV5()->getPartVolumeOverride #define mt32emu_set_reversed_stereo_enabled i.v0->setReversedStereoEnabled #define mt32emu_is_reversed_stereo_enabled i.v0->isReversedStereoEnabled #define mt32emu_set_nice_amp_ramp_enabled iV2()->setNiceAmpRampEnabled #define mt32emu_is_nice_amp_ramp_enabled iV2()->isNiceAmpRampEnabled +#define mt32emu_set_nice_panning_enabled iV3()->setNicePanningEnabled +#define mt32emu_is_nice_panning_enabled iV3()->isNicePanningEnabled +#define mt32emu_set_nice_partial_mixing_enabled iV3()->setNicePartialMixingEnabled +#define mt32emu_is_nice_partial_mixing_enabled iV3()->isNicePartialMixingEnabled #define mt32emu_render_bit16s i.v0->renderBit16s #define mt32emu_render_float i.v0->renderFloat #define mt32emu_render_bit16s_streams i.v0->renderBit16sStreams @@ -104,7 +119,14 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_partial_states i.v0->getPartialStates #define mt32emu_get_playing_notes i.v0->getPlayingNotes #define mt32emu_get_patch_name i.v0->getPatchName +#define mt32emu_get_sound_group_name iV6()->getSoundGroupName +#define mt32emu_get_sound_name iV6()->getSoundName #define mt32emu_read_memory i.v0->readMemory +#define mt32emu_get_display_state iV5()->getDisplayState +#define mt32emu_set_main_display_mode iV5()->setMainDisplayMode +#define mt32emu_set_display_compatibility iV5()->setDisplayCompatibility +#define mt32emu_is_display_old_mt32_compatible iV5()->isDisplayOldMT32Compatible +#define mt32emu_is_default_display_old_mt32_compatible iV5()->isDefaultDisplayOldMT32Compatible #else // #if MT32EMU_API_TYPE == 2 @@ -117,7 +139,7 @@ namespace MT32Emu { namespace CppInterfaceImpl { static const mt32emu_report_handler_i NULL_REPORT_HANDLER = { NULL }; -static mt32emu_report_handler_i getReportHandlerThunk(); +static mt32emu_report_handler_i getReportHandlerThunk(mt32emu_report_handler_version); static mt32emu_midi_receiver_i getMidiReceiverThunk(); } @@ -130,8 +152,8 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk(); * See c_types.h and c_interface.h for description of the corresponding interface methods. */ -// Defines the interface for handling reported events. -// Corresponds to the current version of mt32emu_report_handler_i interface. +// Defines the interface for handling reported events (initial version). +// Corresponds to the mt32emu_report_handler_i_v0 interface. class IReportHandler { public: virtual void printDebug(const char *fmt, va_list list) = 0; @@ -153,6 +175,17 @@ protected: ~IReportHandler() {} }; +// Extends IReportHandler, so that the client may supply callbacks for reporting signals about updated display state. +// Corresponds to the mt32emu_report_handler_i_v1 interface. +class IReportHandlerV1 : public IReportHandler { +public: + virtual void onLCDStateUpdated() = 0; + virtual void onMidiMessageLEDStateUpdated(bool ledState) = 0; + +protected: + ~IReportHandlerV1() {} +}; + // Defines the interface for receiving MIDI messages generated by MIDI stream parser. // Corresponds to the current version of mt32emu_midi_receiver_i interface. class IMidiReceiver { @@ -190,14 +223,24 @@ public: Bit32u getStereoOutputSamplerate(const AnalogOutputMode analog_output_mode) { return mt32emu_get_stereo_output_samplerate(static_cast(analog_output_mode)); } AnalogOutputMode getBestAnalogOutputMode(const double target_samplerate) { return static_cast(mt32emu_get_best_analog_output_mode(target_samplerate)); } + size_t getMachineIDs(const char **machine_ids, size_t machine_ids_size) { return mt32emu_get_machine_ids(machine_ids, machine_ids_size); } + size_t getROMIDs(const char **rom_ids, size_t rom_ids_size, const char *machine_id) { return mt32emu_get_rom_ids(rom_ids, rom_ids_size, machine_id); } + mt32emu_return_code identifyROMData(mt32emu_rom_info *rom_info, const Bit8u *data, size_t data_size, const char *machine_id) { return mt32emu_identify_rom_data(rom_info, data, data_size, machine_id); } + mt32emu_return_code identifyROMFile(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id) { return mt32emu_identify_rom_file(rom_info, filename, machine_id); } + // Context-dependent methods mt32emu_context getContext() { return c; } void createContext(mt32emu_report_handler_i report_handler = CppInterfaceImpl::NULL_REPORT_HANDLER, void *instance_data = NULL) { freeContext(); c = mt32emu_create_context(report_handler, instance_data); } - void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(), &report_handler); } + void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_0), &report_handler); } + void createContext(IReportHandlerV1 &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_1), &report_handler); } void freeContext() { if (c != NULL) { mt32emu_free_context(c); c = NULL; } } mt32emu_return_code addROMData(const Bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest = NULL) { return mt32emu_add_rom_data(c, data, data_size, sha1_digest); } mt32emu_return_code addROMFile(const char *filename) { return mt32emu_add_rom_file(c, filename); } + mt32emu_return_code mergeAndAddROMData(const Bit8u *part1_data, size_t part1_data_size, const Bit8u *part2_data, size_t part2_data_size) { return mt32emu_merge_and_add_rom_data(c, part1_data, part1_data_size, NULL, part2_data, part2_data_size, NULL); } + mt32emu_return_code mergeAndAddROMData(const Bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const Bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest) { return mt32emu_merge_and_add_rom_data(c, part1_data, part1_data_size, part1_sha1_digest, part2_data, part2_data_size, part2_sha1_digest); } + mt32emu_return_code mergeAndAddROMFiles(const char *part1_filename, const char *part2_filename) { return mt32emu_merge_and_add_rom_files(c, part1_filename, part2_filename); } + mt32emu_return_code addMachineROMFile(const char *machine_id, const char *filename) { return mt32emu_add_machine_rom_file(c, machine_id, filename); } void getROMInfo(mt32emu_rom_info *rom_info) { mt32emu_get_rom_info(c, rom_info); } void setPartialCount(const Bit32u partial_count) { mt32emu_set_partial_count(c, partial_count); } void setAnalogOutputMode(const AnalogOutputMode analog_output_mode) { mt32emu_set_analog_output_mode(c, static_cast(analog_output_mode)); } @@ -213,6 +256,7 @@ public: Bit32u convertSynthToOutputTimestamp(Bit32u synth_timestamp) { return mt32emu_convert_synth_to_output_timestamp(c, synth_timestamp); } void flushMIDIQueue() { mt32emu_flush_midi_queue(c); } Bit32u setMIDIEventQueueSize(const Bit32u queue_size) { return mt32emu_set_midi_event_queue_size(c, queue_size); } + void configureMIDIEventQueueSysexStorage(const Bit32u storage_buffer_size) { mt32emu_configure_midi_event_queue_sysex_storage(c, storage_buffer_size); } void setMIDIReceiver(mt32emu_midi_receiver_i midi_receiver, void *instance_data) { mt32emu_set_midi_receiver(c, midi_receiver, instance_data); } void setMIDIReceiver(IMidiReceiver &midi_receiver) { setMIDIReceiver(CppInterfaceImpl::getMidiReceiverThunk(), &midi_receiver); } @@ -238,6 +282,7 @@ public: void setReverbCompatibilityMode(const bool mt32_compatible_mode) { mt32emu_set_reverb_compatibility_mode(c, mt32_compatible_mode ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } bool isMT32ReverbCompatibilityMode() { return mt32emu_is_mt32_reverb_compatibility_mode(c) != MT32EMU_BOOL_FALSE; } bool isDefaultReverbMT32Compatible() { return mt32emu_is_default_reverb_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + void preallocateReverbMemory(const bool enabled) { mt32emu_preallocate_reverb_memory(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } void setDACInputMode(const DACInputMode mode) { mt32emu_set_dac_input_mode(c, static_cast(mode)); } DACInputMode getDACInputMode() { return static_cast(mt32emu_get_dac_input_mode(c)); } @@ -250,12 +295,21 @@ public: void setReverbOutputGain(float gain) { mt32emu_set_reverb_output_gain(c, gain); } float getReverbOutputGain() { return mt32emu_get_reverb_output_gain(c); } + void setPartVolumeOverride(Bit8u part_number, Bit8u volume_override) { mt32emu_set_part_volume_override(c, part_number, volume_override); } + Bit8u getPartVolumeOverride(Bit8u part_number) { return mt32emu_get_part_volume_override(c, part_number); } + void setReversedStereoEnabled(const bool enabled) { mt32emu_set_reversed_stereo_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } bool isReversedStereoEnabled() { return mt32emu_is_reversed_stereo_enabled(c) != MT32EMU_BOOL_FALSE; } void setNiceAmpRampEnabled(const bool enabled) { mt32emu_set_nice_amp_ramp_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } bool isNiceAmpRampEnabled() { return mt32emu_is_nice_amp_ramp_enabled(c) != MT32EMU_BOOL_FALSE; } + void setNicePanningEnabled(const bool enabled) { mt32emu_set_nice_panning_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isNicePanningEnabled() { return mt32emu_is_nice_panning_enabled(c) != MT32EMU_BOOL_FALSE; } + + void setNicePartialMixingEnabled(const bool enabled) { mt32emu_set_nice_partial_mixing_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isNicePartialMixingEnabled() { return mt32emu_is_nice_partial_mixing_enabled(c) != MT32EMU_BOOL_FALSE; } + void renderBit16s(Bit16s *stream, Bit32u len) { mt32emu_render_bit16s(c, stream, len); } void renderFloat(float *stream, Bit32u len) { mt32emu_render_float(c, stream, len); } void renderBit16sStreams(const mt32emu_dac_output_bit16s_streams *streams, Bit32u len) { mt32emu_render_bit16s_streams(c, streams, len); } @@ -268,8 +322,17 @@ public: void getPartialStates(Bit8u *partial_states) { mt32emu_get_partial_states(c, partial_states); } Bit32u getPlayingNotes(Bit8u part_number, Bit8u *keys, Bit8u *velocities) { return mt32emu_get_playing_notes(c, part_number, keys, velocities); } const char *getPatchName(Bit8u part_number) { return mt32emu_get_patch_name(c, part_number); } + bool getSoundGroupName(char *soundGroupName, Bit8u timbreGroup, Bit8u timbreNumber) { return mt32emu_get_sound_group_name(c, soundGroupName, timbreGroup, timbreNumber) != MT32EMU_BOOL_FALSE; } + bool getSoundName(char *soundName, Bit8u timbreGroup, Bit8u timbreNumber) { return mt32emu_get_sound_name(c, soundName, timbreGroup, timbreNumber) != MT32EMU_BOOL_FALSE; } void readMemory(Bit32u addr, Bit32u len, Bit8u *data) { mt32emu_read_memory(c, addr, len, data); } + bool getDisplayState(char *target_buffer, const bool narrow_lcd) { return mt32emu_get_display_state(c, target_buffer, narrow_lcd ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE) != MT32EMU_BOOL_FALSE; } + void setMainDisplayMode() { mt32emu_set_main_display_mode(c); } + + void setDisplayCompatibility(const bool oldMT32CompatibilityEnabled) { mt32emu_set_display_compatibility(c, oldMT32CompatibilityEnabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isDisplayOldMT32Compatible() { return mt32emu_is_display_old_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + bool isDefaultDisplayOldMT32Compatible() { return mt32emu_is_default_display_old_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + private: #if MT32EMU_API_TYPE == 2 const mt32emu_service_i i; @@ -279,108 +342,138 @@ private: #if MT32EMU_API_TYPE == 2 const mt32emu_service_i_v1 *iV1() { return (getVersionID() < MT32EMU_SERVICE_VERSION_1) ? NULL : i.v1; } const mt32emu_service_i_v2 *iV2() { return (getVersionID() < MT32EMU_SERVICE_VERSION_2) ? NULL : i.v2; } + const mt32emu_service_i_v3 *iV3() { return (getVersionID() < MT32EMU_SERVICE_VERSION_3) ? NULL : i.v3; } + const mt32emu_service_i_v4 *iV4() { return (getVersionID() < MT32EMU_SERVICE_VERSION_4) ? NULL : i.v4; } + const mt32emu_service_i_v5 *iV5() { return (getVersionID() < MT32EMU_SERVICE_VERSION_5) ? NULL : i.v5; } + const mt32emu_service_i_v6 *iV6() { return (getVersionID() < MT32EMU_SERVICE_VERSION_6) ? NULL : i.v6; } #endif + + Service(const Service &); // prevent copy-construction + Service& operator=(const Service &); // prevent assignment }; namespace CppInterfaceImpl { -static mt32emu_report_handler_version getReportHandlerVersionID(mt32emu_report_handler_i) { - return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; -} +static mt32emu_report_handler_version MT32EMU_C_CALL getReportHandlerVersionID(mt32emu_report_handler_i); -static void printDebug(void *instance_data, const char *fmt, va_list list) { +static void MT32EMU_C_CALL printDebug(void *instance_data, const char *fmt, va_list list) { static_cast(instance_data)->printDebug(fmt, list); } -static void onErrorControlROM(void *instance_data) { +static void MT32EMU_C_CALL onErrorControlROM(void *instance_data) { static_cast(instance_data)->onErrorControlROM(); } -static void onErrorPCMROM(void *instance_data) { +static void MT32EMU_C_CALL onErrorPCMROM(void *instance_data) { static_cast(instance_data)->onErrorPCMROM(); } -static void showLCDMessage(void *instance_data, const char *message) { +static void MT32EMU_C_CALL showLCDMessage(void *instance_data, const char *message) { static_cast(instance_data)->showLCDMessage(message); } -static void onMIDIMessagePlayed(void *instance_data) { +static void MT32EMU_C_CALL onMIDIMessagePlayed(void *instance_data) { static_cast(instance_data)->onMIDIMessagePlayed(); } -static mt32emu_boolean onMIDIQueueOverflow(void *instance_data) { +static mt32emu_boolean MT32EMU_C_CALL onMIDIQueueOverflow(void *instance_data) { return static_cast(instance_data)->onMIDIQueueOverflow() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -static void onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) { +static void MT32EMU_C_CALL onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) { static_cast(instance_data)->onMIDISystemRealtime(system_realtime); } -static void onDeviceReset(void *instance_data) { +static void MT32EMU_C_CALL onDeviceReset(void *instance_data) { static_cast(instance_data)->onDeviceReset(); } -static void onDeviceReconfig(void *instance_data) { +static void MT32EMU_C_CALL onDeviceReconfig(void *instance_data) { static_cast(instance_data)->onDeviceReconfig(); } -static void onNewReverbMode(void *instance_data, mt32emu_bit8u mode) { +static void MT32EMU_C_CALL onNewReverbMode(void *instance_data, mt32emu_bit8u mode) { static_cast(instance_data)->onNewReverbMode(mode); } -static void onNewReverbTime(void *instance_data, mt32emu_bit8u time) { +static void MT32EMU_C_CALL onNewReverbTime(void *instance_data, mt32emu_bit8u time) { static_cast(instance_data)->onNewReverbTime(time); } -static void onNewReverbLevel(void *instance_data, mt32emu_bit8u level) { +static void MT32EMU_C_CALL onNewReverbLevel(void *instance_data, mt32emu_bit8u level) { static_cast(instance_data)->onNewReverbLevel(level); } -static void onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) { +static void MT32EMU_C_CALL onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) { static_cast(instance_data)->onPolyStateChanged(part_num); } -static void onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) { +static void MT32EMU_C_CALL onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) { static_cast(instance_data)->onProgramChanged(part_num, sound_group_name, patch_name); } -static mt32emu_report_handler_i getReportHandlerThunk() { - static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = { - getReportHandlerVersionID, - printDebug, - onErrorControlROM, - onErrorPCMROM, - showLCDMessage, - onMIDIMessagePlayed, - onMIDIQueueOverflow, - onMIDISystemRealtime, - onDeviceReset, - onDeviceReconfig, - onNewReverbMode, - onNewReverbTime, - onNewReverbLevel, - onPolyStateChanged, - onProgramChanged - }; - - static const mt32emu_report_handler_i REPORT_HANDLER_THUNK = { &REPORT_HANDLER_V0_THUNK }; - - return REPORT_HANDLER_THUNK; +static void MT32EMU_C_CALL onLCDStateUpdated(void *instance_data) { + static_cast(instance_data)->onLCDStateUpdated(); } -static mt32emu_midi_receiver_version getMidiReceiverVersionID(mt32emu_midi_receiver_i) { +static void MT32EMU_C_CALL onMidiMessageLEDStateUpdated(void *instance_data, mt32emu_boolean led_state) { + static_cast(instance_data)->onMidiMessageLEDStateUpdated(led_state != MT32EMU_BOOL_FALSE); +} + +#define MT32EMU_REPORT_HANDLER_V0_THUNK \ + getReportHandlerVersionID, \ + printDebug, \ + onErrorControlROM, \ + onErrorPCMROM, \ + showLCDMessage, \ + onMIDIMessagePlayed, \ + onMIDIQueueOverflow, \ + onMIDISystemRealtime, \ + onDeviceReset, \ + onDeviceReconfig, \ + onNewReverbMode, \ + onNewReverbTime, \ + onNewReverbLevel, \ + onPolyStateChanged, \ + onProgramChanged + +static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = { + MT32EMU_REPORT_HANDLER_V0_THUNK +}; + +static const mt32emu_report_handler_i_v1 REPORT_HANDLER_V1_THUNK = { + MT32EMU_REPORT_HANDLER_V0_THUNK, + onLCDStateUpdated, + onMidiMessageLEDStateUpdated +}; + +#undef MT32EMU_REPORT_HANDLER_THUNK_V0 + +static mt32emu_report_handler_version MT32EMU_C_CALL getReportHandlerVersionID(mt32emu_report_handler_i thunk) { + if (thunk.v0 == &REPORT_HANDLER_V0_THUNK) return MT32EMU_REPORT_HANDLER_VERSION_0; + return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; +} + +static mt32emu_report_handler_i getReportHandlerThunk(mt32emu_report_handler_version versionID) { + mt32emu_report_handler_i thunk; + if (versionID == MT32EMU_REPORT_HANDLER_VERSION_0) thunk.v0 = &REPORT_HANDLER_V0_THUNK; + else thunk.v1 = &REPORT_HANDLER_V1_THUNK; + return thunk; +} + +static mt32emu_midi_receiver_version MT32EMU_C_CALL getMidiReceiverVersionID(mt32emu_midi_receiver_i) { return MT32EMU_MIDI_RECEIVER_VERSION_CURRENT; } -static void handleShortMessage(void *instance_data, const mt32emu_bit32u message) { +static void MT32EMU_C_CALL handleShortMessage(void *instance_data, const mt32emu_bit32u message) { static_cast(instance_data)->handleShortMessage(message); } -static void handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) { +static void MT32EMU_C_CALL handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) { static_cast(instance_data)->handleSysex(stream, length); } -static void handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) { +static void MT32EMU_C_CALL handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) { static_cast(instance_data)->handleSystemRealtimeMessage(realtime); } @@ -409,10 +502,17 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_get_library_version_string #undef mt32emu_get_stereo_output_samplerate #undef mt32emu_get_best_analog_output_mode +#undef mt32emu_get_machine_ids +#undef mt32emu_get_rom_ids +#undef mt32emu_identify_rom_data +#undef mt32emu_identify_rom_file #undef mt32emu_create_context #undef mt32emu_free_context #undef mt32emu_add_rom_data #undef mt32emu_add_rom_file +#undef mt32emu_merge_and_add_rom_data +#undef mt32emu_merge_and_add_rom_files +#undef mt32emu_add_machine_rom_file #undef mt32emu_get_rom_info #undef mt32emu_set_partial_count #undef mt32emu_set_analog_output_mode @@ -428,6 +528,7 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_convert_synth_to_output_timestamp #undef mt32emu_flush_midi_queue #undef mt32emu_set_midi_event_queue_size +#undef mt32emu_configure_midi_event_queue_sysex_storage #undef mt32emu_set_midi_receiver #undef mt32emu_get_internal_rendered_sample_count #undef mt32emu_parse_stream @@ -449,6 +550,7 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_set_reverb_compatibility_mode #undef mt32emu_is_mt32_reverb_compatibility_mode #undef mt32emu_is_default_reverb_mt32_compatible +#undef mt32emu_preallocate_reverb_memory #undef mt32emu_set_dac_input_mode #undef mt32emu_get_dac_input_mode #undef mt32emu_set_midi_delay_mode @@ -461,6 +563,10 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_is_reversed_stereo_enabled #undef mt32emu_set_nice_amp_ramp_enabled #undef mt32emu_is_nice_amp_ramp_enabled +#undef mt32emu_set_nice_panning_enabled +#undef mt32emu_is_nice_panning_enabled +#undef mt32emu_set_nice_partial_mixing_enabled +#undef mt32emu_is_nice_partial_mixing_enabled #undef mt32emu_render_bit16s #undef mt32emu_render_float #undef mt32emu_render_bit16s_streams @@ -472,7 +578,14 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_get_partial_states #undef mt32emu_get_playing_notes #undef mt32emu_get_patch_name +#undef mt32emu_get_sound_group_name +#undef mt32emu_get_sound_name #undef mt32emu_read_memory +#undef mt32emu_get_display_state +#undef mt32emu_set_main_display_mode +#undef mt32emu_set_display_compatibility +#undef mt32emu_is_display_old_mt32_compatible +#undef mt32emu_is_default_display_old_mt32_compatible #endif // #if MT32EMU_API_TYPE == 2 diff --git a/src/include/mt32emu/config.h b/src/include/mt32emu/config.h index 5f5b6c9fb..906c23d56 100644 --- a/src/include/mt32emu/config.h +++ b/src/include/mt32emu/config.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,9 +18,9 @@ #ifndef MT32EMU_CONFIG_H #define MT32EMU_CONFIG_H -#define MT32EMU_VERSION "2.2.0" +#define MT32EMU_VERSION "2.7.0" #define MT32EMU_VERSION_MAJOR 2 -#define MT32EMU_VERSION_MINOR 2 +#define MT32EMU_VERSION_MINOR 7 #define MT32EMU_VERSION_PATCH 0 /* Library Exports Configuration @@ -37,4 +37,9 @@ #define MT32EMU_API_TYPE 0 +#define MT32EMU_WITH_LIBSOXR_RESAMPLER 0 +#define MT32EMU_WITH_LIBSAMPLERATE_RESAMPLER 0 +#define MT32EMU_WITH_INTERNAL_RESAMPLER 1 + + #endif diff --git a/src/include/mt32emu/globals.h b/src/include/mt32emu/globals.h index 2d984c82b..86ac1ca5b 100644 --- a/src/include/mt32emu/globals.h +++ b/src/include/mt32emu/globals.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -20,27 +20,35 @@ #include "config.h" -/* Support for compiling shared library. */ +/* Support for compiling shared library. + * MT32EMU_SHARED and mt32emu_EXPORTS are defined when building a shared library. + * MT32EMU_SHARED should also be defined for Windows platforms that provides for a small performance benefit, + * and it _must_ be defined along with MT32EMU_RUNTIME_VERSION_CHECK when using MSVC. + */ #ifdef MT32EMU_SHARED -#if defined _WIN32 || defined __CYGWIN__ -#ifdef _MSC_VER -#ifdef mt32emu_EXPORTS -#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) -#else /* #ifdef mt32emu_EXPORTS */ -#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) -#endif /* #ifdef mt32emu_EXPORTS */ -#else /* #ifdef _MSC_VER */ -#ifdef mt32emu_EXPORTS -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) -#else /* #ifdef mt32emu_EXPORTS */ -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) -#endif /* #ifdef mt32emu_EXPORTS */ -#endif /* #ifdef _MSC_VER */ -#else /* #if defined _WIN32 || defined __CYGWIN__ */ -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) -#endif /* #if defined _WIN32 || defined __CYGWIN__ */ +# if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ +# ifdef _MSC_VER +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) +# endif /* #ifdef mt32emu_EXPORTS */ +# else /* #ifdef _MSC_VER */ +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) +# endif /* #ifdef mt32emu_EXPORTS */ +# endif /* #ifdef _MSC_VER */ +# else /* #if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ */ +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE +# endif /* #ifdef mt32emu_EXPORTS */ +# endif /* #if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ */ #else /* #ifdef MT32EMU_SHARED */ -#define MT32EMU_EXPORT_ATTRIBUTE +# define MT32EMU_EXPORT_ATTRIBUTE #endif /* #ifdef MT32EMU_SHARED */ #if MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2 @@ -49,6 +57,33 @@ #define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE #endif +/* Facilitates easier tracking of the library version when an external symbol was introduced. + * Particularly useful for shared library builds on POSIX systems that support symbol versioning, + * so that the version map file can be generated automatically. + */ +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT + +/* Helpers for compile-time version checks */ + +/* Encodes the given version components to a single integer value to simplify further checks. */ +#define MT32EMU_VERSION_INT(major, minor, patch) ((major << 16) | (minor << 8) | patch) + +/* The version of this library build, as an integer. */ +#define MT32EMU_CURRENT_VERSION_INT MT32EMU_VERSION_INT(MT32EMU_VERSION_MAJOR, MT32EMU_VERSION_MINOR, MT32EMU_VERSION_PATCH) + +/* Compares the current library version with the given version components. Intended for feature checks. */ +#define MT32EMU_VERSION_ATLEAST(major, minor, patch) (MT32EMU_CURRENT_VERSION_INT >= MT32EMU_VERSION_INT(major, minor, patch)) + +/* Implements a simple version check that ensures full API compatibility of this library build + * with the application requirements. The latter can be derived from the versions of used public symbols. + * + * Note: This macro is intended for a quick compile-time check. To ensure compatibility of an application + * linked with a shared library, an automatic version check can be engaged with help of the build option + * libmt32emu_WITH_VERSION_TAGGING. For a fine-grained feature checking in run-time, see functions + * mt32emu_get_library_version_int and Synth::getLibraryVersionInt. + */ +#define MT32EMU_IS_COMPATIBLE(major, minor) (MT32EMU_VERSION_MAJOR == major && MT32EMU_VERSION_MINOR >= minor) + /* Useful constants */ /* Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. diff --git a/src/include/mt32emu/mt32emu.h b/src/include/mt32emu/mt32emu.h index 6b93121be..571b25571 100644 --- a/src/include/mt32emu/mt32emu.h +++ b/src/include/mt32emu/mt32emu.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -37,28 +37,23 @@ */ #ifdef MT32EMU_API_TYPE -#if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=0 -#elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=1 -#elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) -#error Incompatible setting MT32EMU_API_TYPE=2 -#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=3 -#endif +# if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=0 +# elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=1 +# elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) +# error Incompatible setting MT32EMU_API_TYPE=2 +# elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=3 +# endif #else /* #ifdef MT32EMU_API_TYPE */ -#if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 -#define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE -#else -#define MT32EMU_API_TYPE 0 -#endif +# if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 +# define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE +# else +# define MT32EMU_API_TYPE 0 +# endif #endif /* #ifdef MT32EMU_API_TYPE */ -/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */ -/* -#define MT32EMU_SHARED -*/ - #include "globals.h" #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 @@ -79,6 +74,14 @@ #include "MidiStreamParser.h" #include "SampleRateConverter.h" +#if MT32EMU_RUNTIME_VERSION_CHECK == 1 +#include "VersionTagging.h" +#endif + #endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ +#if MT32EMU_RUNTIME_VERSION_CHECK == 2 +#include "VersionTagging.h" +#endif + #endif /* #ifndef MT32EMU_MT32EMU_H */ diff --git a/src/sound/midi_mt32.c b/src/sound/midi_mt32.c index bb86210f3..f9d45c42e 100644 --- a/src/sound/midi_mt32.c +++ b/src/sound/midi_mt32.c @@ -18,11 +18,13 @@ extern void givealbuffer_midi(void *buf, uint32_t size); extern void al_set_midi(int freq, int buf_size); + +static mt32emu_report_handler_version get_mt32_report_handler_version(mt32emu_report_handler_i i); static void display_mt32_message(void *instance_data, const char *message); static const mt32emu_report_handler_i_v0 handler_mt32_v0 = { /** Returns the actual interface version ID */ - NULL, // mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); + get_mt32_report_handler_version, // mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); /** Callback for debug messages, in vprintf() format */ NULL, // void (*printDebug)(void *instance_data, const char *fmt, va_list list); @@ -59,7 +61,7 @@ static const mt32emu_report_handler_i_v0 handler_mt32_v0 = { /** Alternate report handler for Roland CM-32L */ static const mt32emu_report_handler_i_v0 handler_cm32l_v0 = { /** Returns the actual interface version ID */ - NULL, // mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); + get_mt32_report_handler_version, // mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); /** Callback for debug messages, in vprintf() format */ NULL, // void (*printDebug)(void *instance_data, const char *fmt, va_list list); @@ -138,6 +140,12 @@ static float *buffer = NULL; static int16_t *buffer_int16 = NULL; static int midi_pos = 0; +static mt32emu_report_handler_version +get_mt32_report_handler_version(mt32emu_report_handler_i i) +{ + return MT32EMU_REPORT_HANDLER_VERSION_0; +} + static void display_mt32_message(void *instance_data, const char *message) { diff --git a/src/sound/munt/Analog.cpp b/src/sound/munt/Analog.cpp index b14d824dd..41fb19b44 100644 --- a/src/sound/munt/Analog.cpp +++ b/src/sound/munt/Analog.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -273,6 +273,8 @@ Analog *Analog::createAnalog(const AnalogOutputMode mode, const bool oldMT32Anal return new AnalogImpl(mode, oldMT32AnalogLPF); case RendererType_FLOAT: return new AnalogImpl(mode, oldMT32AnalogLPF); + default: + break; } return NULL; } diff --git a/src/sound/munt/Analog.h b/src/sound/munt/Analog.h index 244e4118f..62c092d9d 100644 --- a/src/sound/munt/Analog.h +++ b/src/sound/munt/Analog.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/BReverbModel.cpp b/src/sound/munt/BReverbModel.cpp index 1eb6f7e56..05a2e4240 100644 --- a/src/sound/munt/BReverbModel.cpp +++ b/src/sound/munt/BReverbModel.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -633,6 +633,8 @@ BReverbModel *BReverbModel::createBReverbModel(const ReverbMode mode, const bool return new BReverbModelImpl(mode, mt32CompatibleModel); case RendererType_FLOAT: return new BReverbModelImpl(mode, mt32CompatibleModel); + default: + break; } return NULL; } diff --git a/src/sound/munt/BReverbModel.h b/src/sound/munt/BReverbModel.h index ee2f838b2..ff34e9543 100644 --- a/src/sound/munt/BReverbModel.h +++ b/src/sound/munt/BReverbModel.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/CMakeLists.txt b/src/sound/munt/CMakeLists.txt index 79ac7b2d9..3ebbe40fa 100644 --- a/src/sound/munt/CMakeLists.txt +++ b/src/sound/munt/CMakeLists.txt @@ -13,7 +13,7 @@ # Copyright 2020,2021 David Hrdlička. # -add_library(mt32emu STATIC Analog.cpp BReverbModel.cpp File.cpp FileStream.cpp +add_library(mt32emu STATIC Analog.cpp BReverbModel.cpp Display.cpp File.cpp FileStream.cpp LA32Ramp.cpp LA32FloatWaveGenerator.cpp LA32WaveGenerator.cpp MidiStreamParser.cpp Part.cpp Partial.cpp PartialManager.cpp Poly.cpp ROMInfo.cpp SampleRateConverter.cpp diff --git a/src/sound/munt/Display.cpp b/src/sound/munt/Display.cpp new file mode 100644 index 000000000..e04ea2cd1 --- /dev/null +++ b/src/sound/munt/Display.cpp @@ -0,0 +1,354 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "internals.h" + +#include "Display.h" +#include "Part.h" +#include "Structures.h" +#include "Synth.h" + +namespace MT32Emu { + +/* Details on the emulation model. + * + * There are four display modes emulated: + * - main (Master Volume), set upon startup after showing the welcoming banner; + * - program change notification; + * - custom display message received via a SysEx; + * - error banner (e.g. the MIDI message checksum error). + * Stuff like cursor blinking, patch selection mode, test mode, reaction to the front panel buttons, etc. is out of scope, as more + * convenient UI/UX solutions are likely desired in applications, if at all. + * + * Note, despite the LAPC and CM devices come without the LCD and the front panel buttons, the control ROM does support these, + * if connected to the main board. That's intended for running the test mode in a service centre, as documented. + * + * Within the aforementioned scope, the observable hardware behaviour differs noticeably, depending on the control ROM version. + * At least three milestones can be identified: + * - with MT-32 control ROM V1.06, custom messages are no longer shown unless the display is in the main (Master Volume) mode; + * - with MT-32 control ROM V2.04, new function introduced - Display Reset yet added many other changes (taking the full SysEx + * address into account when processing custom messages and special handling of the ASCII control characters are among them); + * all the second-gen devices, including LAPC-I and CM-32L, behave very similarly; + * - in the third-gen devices, the LCD support was partially cut down in the control ROM (basically, only the status + * of the test mode, the ROM version and the checksum warnings are shown) - it's not fun, so this is NOT emulated. + * + * Features of the old-gen units. + * - Any message with the first address byte 0x20 is processed and has some effect on the LCD. Messages with any other first + * address byte (e.g. starting with 0x21 or 0x1F7F7F with an overlap) are not considered display-relevant. + * - The second and the third address byte are largely irrelevant. Only presence of the second address byte makes an observable + * difference, not the data within. + * - Any string received in the custom message is normalised - all ASCII control characters are replaced with spaces, messages + * shorter than 20 bytes are filled up with spaces to the full supported length. However, should a timbre name contain an ASCII + * control character, it is displayed nevertheless, with zero meaning the end-of-string. + * - Special message 0x20 (of just 1 address byte) shows the contents of the custom message buffer with either the last received + * message or the empty buffer initially filled with spaces. See the note below about the priorities of the display modes. + * - Messages containing two or three bytes with just the address are considered empty and fill the custom message buffer with + * all spaces. The contents of the empty buffer is then shown, depending on the priority of the current display mode. + * - Timing: custom messages are shown until an external event occurs like pressing a front panel button, receiving a new custom + * message, program change, etc., and for indefinitely long otherwise. A program change notification is shown for about 1300 + * milliseconds; when the timer expires, the display returns to the main mode (irrespective to the current display mode). + * When an error occurs, the warning is shown for a limited time only, similarly to the program change notifications. + * - The earlier old-gen devices treat all display modes with equal priority, except the main mode, which has a lower one. This + * makes it possible e.g. to replace the error banner with a custom message or a program change notification, and so on. + * A slightly improved behaviour is observed since the control ROM V1.06, when custom messages were de-prioritised. But still, + * a program change beats an error banner even in the later models. + * + * Features of the second-gen units. + * - All three bytes in SysEx address are now relevant. + * - It is possible to replace individual characters in the custom message buffer which are addressed individually within + * the range 0x200000-0x200013. + * - Writes to higher addresses up to 0x20007F simply make the custom message buffer shown, with either the last received message + * or the empty buffer initially filled with spaces. + * - Writes to address 0x200100 trigger the Display Reset function which resets the display to the main (Master Volume) mode. + * Similarly, showing an error banner is ended. If a program change notification is shown, this function does nothing, however. + * - Writes to other addresses are not considered display-relevant, albeit writing a long string to lower addresses + * (e.g. 0x1F7F7F) that overlaps the display range does result in updating and showing the custom display message. + * - Writing a long string that covers the custom message buffer and address 0x200100 does both things, i.e. updates the buffer + * and triggers the Display Reset function. + * - While the display is not in a user interaction mode, custom messages and error banners have the highest display priority. + * As long as these are shown, program change notifications are suppressed. The display only leaves this mode when the Display + * Reset function is triggered or a front panel button is pressed. Notably, when the user enters the menu, all custom messages + * are ignored, including the Display Reset command, but error banners are shown nevertheless. + * - Sending cut down messages with partially specified address rather leads to undefined behaviour, except for a two-byte message + * 0x20 0x00 which consistently shows the content of the custom message buffer (if priority permits). Otherwise, the behaviour + * depends on the previously submitted address, e.g. the two-byte version of Display Reset may fail depending on the third byte + * of the previous message. One-byte message 0x20 seemingly does Display Reset yet writes a zero character to a position derived + * from the third byte of the preceding message. + * + * Some notes on the behaviour that is common to all hardware models. + * - The display is DM2011 with LSI SED1200D-0A. This unit supports 4 user-programmable characters stored in CGRAM, all 4 get + * loaded at startup. Character #0 is empty (with the cursor underline), #1 is the full block (used to mark active parts), + * #2 is the pipe character (identical to #124 from the CGROM) and #3 is a variation on "down arrow". During normal operation, + * those duplicated characters #2 and #124 are both used in different places and character #3 can only be made visible by adding + * it either to a custom timbre name or a custom message. Character #0 is probably never shown as this code has special meaning + * in the processing routines. For simplicity, we only use characters #124 and #1 in this model. + * - When the main mode is active, the current state of the first 5 parts and the rhythm part is represented by replacing the part + * symbol with the full rectangle character (#1 from the CGRAM). For voice parts, the rectangle is shown as long as at least one + * partial is playing in a non-releasing phase on that part. For the rhythm part, the rectangle blinks briefly when a new NoteOn + * message is received on that part (sometimes even when that actually produces no sound). + */ + +static const char MASTER_VOLUME_WITH_DELIMITER[] = "| 0"; +static const char MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX[] = "|vol: 0"; +static const Bit8u RHYTHM_PART_CODE = 'R'; +static const Bit8u FIELD_DELIMITER = '|'; +static const Bit8u ACTIVE_PART_INDICATOR = 1; + +static const Bit32u DISPLAYED_VOICE_PARTS_COUNT = 5; +static const Bit32u SOUND_GROUP_NAME_WITH_DELIMITER_SIZE = 8; +static const Bit32u MASTER_VOLUME_WITH_DELIMITER_SIZE = sizeof(MASTER_VOLUME_WITH_DELIMITER) - 1; +static const Bit32u MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX_SIZE = sizeof(MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX) - 1; + +// This is the period to show those short blinks of MIDI MESSAGE LED and the rhythm part state. +// Two related countdowns are initialised to 8 and touched each 10 milliseconds by the software timer 0 interrupt handler. +static const Bit32u BLINK_TIME_MILLIS = 80; +static const Bit32u BLINK_TIME_FRAMES = BLINK_TIME_MILLIS * SAMPLE_RATE / 1000; + +// This is based on the (free-running) TIMER1 overflow interrupt. The timer is 16-bit and clocked at 500KHz. +// The message is displayed until 10 overflow interrupts occur. At the standard sample rate, it counts +// precisely as 41943.04 frame times. +static const Bit32u SCHEDULED_DISPLAY_MODE_RESET_FRAMES = 41943; + +/** + * Copies up to lengthLimit characters from possibly null-terminated source to destination. The character of destination located + * at the position of the null terminator (if any) in source and the rest of destination are left untouched. + */ +static void copyNullTerminatedString(Bit8u *destination, const Bit8u *source, Bit32u lengthLimit) { + for (Bit32u i = 0; i < lengthLimit; i++) { + Bit8u c = source[i]; + if (c == 0) break; + destination[i] = c; + } +} + +Display::Display(Synth &useSynth) : + synth(useSynth), + lastLEDState(), + lcdDirty(), + lcdUpdateSignalled(), + lastRhythmPartState(), + mode(Mode_STARTUP_MESSAGE), + midiMessagePlayedSinceLastReset(), + rhythmNotePlayedSinceLastReset() +{ + scheduleDisplayReset(); + const Bit8u *startupMessage = &synth.controlROMData[synth.controlROMMap->startupMessage]; + memcpy(displayBuffer, startupMessage, LCD_TEXT_SIZE); + memset(customMessageBuffer, ' ', LCD_TEXT_SIZE); + memset(voicePartStates, 0, sizeof voicePartStates); +} + +void Display::checkDisplayStateUpdated(bool &midiMessageLEDState, bool &midiMessageLEDUpdated, bool &lcdUpdated) { + midiMessageLEDState = midiMessagePlayedSinceLastReset; + maybeResetTimer(midiMessagePlayedSinceLastReset, midiMessageLEDResetTimestamp); + // Note, the LED represents activity of the voice parts only. + for (Bit32u partIndex = 0; !midiMessageLEDState && partIndex < 8; partIndex++) { + midiMessageLEDState = voicePartStates[partIndex]; + } + midiMessageLEDUpdated = lastLEDState != midiMessageLEDState; + lastLEDState = midiMessageLEDState; + + if (displayResetScheduled && shouldResetTimer(displayResetTimestamp)) setMainDisplayMode(); + + if (lastRhythmPartState != rhythmNotePlayedSinceLastReset && mode == Mode_MAIN) lcdDirty = true; + lastRhythmPartState = rhythmNotePlayedSinceLastReset; + maybeResetTimer(rhythmNotePlayedSinceLastReset, rhythmStateResetTimestamp); + + lcdUpdated = lcdDirty && !lcdUpdateSignalled; + if (lcdUpdated) lcdUpdateSignalled = true; +} + +bool Display::getDisplayState(char *targetBuffer, bool narrowLCD) { + if (lcdUpdateSignalled) { + lcdDirty = false; + lcdUpdateSignalled = false; + + switch (mode) { + case Mode_CUSTOM_MESSAGE: + if (synth.isDisplayOldMT32Compatible()) { + memcpy(displayBuffer, customMessageBuffer, LCD_TEXT_SIZE); + } else { + copyNullTerminatedString(displayBuffer, customMessageBuffer, LCD_TEXT_SIZE); + } + break; + case Mode_ERROR_MESSAGE: { + const Bit8u *sysexErrorMessage = &synth.controlROMData[synth.controlROMMap->sysexErrorMessage]; + memcpy(displayBuffer, sysexErrorMessage, LCD_TEXT_SIZE); + break; + } + case Mode_PROGRAM_CHANGE: { + Bit8u *writePosition = displayBuffer; + *writePosition++ = '1' + lastProgramChangePartIndex; + *writePosition++ = FIELD_DELIMITER; + if (narrowLCD) { + writePosition[TIMBRE_NAME_SIZE] = 0; + } else { + memcpy(writePosition, lastProgramChangeSoundGroupName, SOUND_GROUP_NAME_WITH_DELIMITER_SIZE); + writePosition += SOUND_GROUP_NAME_WITH_DELIMITER_SIZE; + } + copyNullTerminatedString(writePosition, lastProgramChangeTimbreName, TIMBRE_NAME_SIZE); + break; + } + case Mode_MAIN: { + Bit8u *writePosition = displayBuffer; + for (Bit32u partIndex = 0; partIndex < DISPLAYED_VOICE_PARTS_COUNT; partIndex++) { + *writePosition++ = voicePartStates[partIndex] ? ACTIVE_PART_INDICATOR : '1' + partIndex; + *writePosition++ = ' '; + } + *writePosition++ = lastRhythmPartState ? ACTIVE_PART_INDICATOR : RHYTHM_PART_CODE; + *writePosition++ = ' '; + if (narrowLCD) { + memcpy(writePosition, MASTER_VOLUME_WITH_DELIMITER, MASTER_VOLUME_WITH_DELIMITER_SIZE); + writePosition += MASTER_VOLUME_WITH_DELIMITER_SIZE; + *writePosition = 0; + } else { + memcpy(writePosition, MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX, MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX_SIZE); + writePosition += MASTER_VOLUME_WITH_DELIMITER_AND_PREFIX_SIZE; + } + Bit32u masterVol = synth.mt32ram.system.masterVol; + while (masterVol > 0) { + std::div_t result = std::div(masterVol, 10); + *--writePosition = '0' + result.rem; + masterVol = result.quot; + } + break; + } + default: + break; + } + } + + memcpy(targetBuffer, displayBuffer, LCD_TEXT_SIZE); + targetBuffer[LCD_TEXT_SIZE] = 0; + return lastLEDState; +} + +void Display::setMainDisplayMode() { + displayResetScheduled = false; + mode = Mode_MAIN; + lcdDirty = true; +} + +void Display::midiMessagePlayed() { + midiMessagePlayedSinceLastReset = true; + midiMessageLEDResetTimestamp = synth.renderedSampleCount + BLINK_TIME_FRAMES; +} + +void Display::rhythmNotePlayed() { + rhythmNotePlayedSinceLastReset = true; + rhythmStateResetTimestamp = synth.renderedSampleCount + BLINK_TIME_FRAMES; + midiMessagePlayed(); + if (synth.isDisplayOldMT32Compatible() && mode == Mode_CUSTOM_MESSAGE) setMainDisplayMode(); +} + +void Display::voicePartStateChanged(Bit8u partIndex, bool activated) { + if (mode == Mode_MAIN) lcdDirty = true; + voicePartStates[partIndex] = activated; + if (synth.isDisplayOldMT32Compatible() && mode == Mode_CUSTOM_MESSAGE) setMainDisplayMode(); +} + +void Display::masterVolumeChanged() { + if (mode == Mode_MAIN) lcdDirty = true; +} + +void Display::programChanged(Bit8u partIndex) { + if (!synth.isDisplayOldMT32Compatible() && (mode == Mode_CUSTOM_MESSAGE || mode == Mode_ERROR_MESSAGE)) return; + mode = Mode_PROGRAM_CHANGE; + lcdDirty = true; + scheduleDisplayReset(); + lastProgramChangePartIndex = partIndex; + const Part *part = synth.getPart(partIndex); + lastProgramChangeSoundGroupName = synth.getSoundGroupName(part); + memcpy(lastProgramChangeTimbreName, part->getCurrentInstr(), TIMBRE_NAME_SIZE); +} + +void Display::checksumErrorOccurred() { + if (mode != Mode_ERROR_MESSAGE) { + mode = Mode_ERROR_MESSAGE; + lcdDirty = true; + } + if (synth.isDisplayOldMT32Compatible()) { + scheduleDisplayReset(); + } else { + displayResetScheduled = false; + } +} + +bool Display::customDisplayMessageReceived(const Bit8u *message, Bit32u startIndex, Bit32u length) { + if (synth.isDisplayOldMT32Compatible()) { + for (Bit32u i = 0; i < LCD_TEXT_SIZE; i++) { + Bit8u c = i < length ? message[i] : ' '; + if (c < 32 || 127 < c) c = ' '; + customMessageBuffer[i] = c; + } + if (!synth.controlROMFeatures->quirkDisplayCustomMessagePriority + && (mode == Mode_PROGRAM_CHANGE || mode == Mode_ERROR_MESSAGE)) return false; + // Note, real devices keep the display reset timer running. + } else { + if (startIndex > 0x80) return false; + if (startIndex == 0x80) { + if (mode != Mode_PROGRAM_CHANGE) setMainDisplayMode(); + return false; + } + displayResetScheduled = false; + if (startIndex < LCD_TEXT_SIZE) { + if (length > LCD_TEXT_SIZE - startIndex) length = LCD_TEXT_SIZE - startIndex; + memcpy(customMessageBuffer + startIndex, message, length); + } + } + mode = Mode_CUSTOM_MESSAGE; + lcdDirty = true; + return true; +} + +void Display::displayControlMessageReceived(const Bit8u *messageBytes, Bit32u length) { + Bit8u emptyMessage[] = { 0 }; + if (synth.isDisplayOldMT32Compatible()) { + if (length == 1) { + customDisplayMessageReceived(customMessageBuffer, 0, LCD_TEXT_SIZE); + } else { + customDisplayMessageReceived(emptyMessage, 0, 0); + } + } else { + // Always assume the third byte to be zero for simplicity. + if (length == 2) { + customDisplayMessageReceived(emptyMessage, messageBytes[1] << 7, 0); + } else if (length == 1) { + customMessageBuffer[0] = 0; + customDisplayMessageReceived(emptyMessage, 0x80, 0); + } + } +} + +void Display::scheduleDisplayReset() { + displayResetTimestamp = synth.renderedSampleCount + SCHEDULED_DISPLAY_MODE_RESET_FRAMES; + displayResetScheduled = true; +} + +bool Display::shouldResetTimer(Bit32u scheduledResetTimestamp) { + // Deals with wrapping of renderedSampleCount. + return Bit32s(scheduledResetTimestamp - synth.renderedSampleCount) < 0; +} + +void Display::maybeResetTimer(bool &timerState, Bit32u scheduledResetTimestamp) { + if (timerState && shouldResetTimer(scheduledResetTimestamp)) timerState = false; +} + +} // namespace MT32Emu diff --git a/src/sound/munt/Display.h b/src/sound/munt/Display.h new file mode 100644 index 000000000..1802b0bd2 --- /dev/null +++ b/src/sound/munt/Display.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_DISPLAY_H +#define MT32EMU_DISPLAY_H + +#include "globals.h" +#include "Types.h" + +namespace MT32Emu { + +class Synth; + +/** Facilitates emulation of internal state of the MIDI MESSAGE LED and the MT-32 LCD. */ +class Display { +public: + static const unsigned int LCD_TEXT_SIZE = 20; + + enum Mode { + Mode_MAIN, // a.k.a. Master Volume + Mode_STARTUP_MESSAGE, + Mode_PROGRAM_CHANGE, + Mode_CUSTOM_MESSAGE, + Mode_ERROR_MESSAGE + }; + + Display(Synth &synth); + void checkDisplayStateUpdated(bool &midiMessageLEDState, bool &midiMessageLEDUpdated, bool &lcdUpdated); + /** Returns whether the MIDI MESSAGE LED is ON and fills the targetBuffer parameter. */ + bool getDisplayState(char *targetBuffer, bool narrowLCD); + void setMainDisplayMode(); + + void midiMessagePlayed(); + void rhythmNotePlayed(); + void voicePartStateChanged(Bit8u partIndex, bool activated); + void masterVolumeChanged(); + void programChanged(Bit8u partIndex); + void checksumErrorOccurred(); + bool customDisplayMessageReceived(const Bit8u *message, Bit32u startIndex, Bit32u length); + void displayControlMessageReceived(const Bit8u *messageBytes, Bit32u length); + +private: + typedef Bit8u DisplayBuffer[LCD_TEXT_SIZE]; + + static const unsigned int TIMBRE_NAME_SIZE = 10; + + Synth &synth; + + bool lastLEDState; + bool lcdDirty; + bool lcdUpdateSignalled; + bool lastRhythmPartState; + bool voicePartStates[8]; + + Bit8u lastProgramChangePartIndex; + const char *lastProgramChangeSoundGroupName; + Bit8u lastProgramChangeTimbreName[TIMBRE_NAME_SIZE]; + + Mode mode; + Bit32u displayResetTimestamp; + bool displayResetScheduled; + Bit32u midiMessageLEDResetTimestamp; + bool midiMessagePlayedSinceLastReset; + Bit32u rhythmStateResetTimestamp; + bool rhythmNotePlayedSinceLastReset; + + DisplayBuffer displayBuffer; + DisplayBuffer customMessageBuffer; + + void scheduleDisplayReset(); + bool shouldResetTimer(Bit32u scheduledResetTimestamp); + void maybeResetTimer(bool &timerState, Bit32u scheduledResetTimestamp); +}; + +} // namespace MT32Emu + +#endif // #ifndef MT32EMU_DISPLAY_H diff --git a/src/sound/munt/Enumerations.h b/src/sound/munt/Enumerations.h index 05a2b6f6d..3cbfdd4c8 100644 --- a/src/sound/munt/Enumerations.h +++ b/src/sound/munt/Enumerations.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/File.cpp b/src/sound/munt/File.cpp index dbe226648..fb2febeb1 100644 --- a/src/sound/munt/File.cpp +++ b/src/sound/munt/File.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/File.h b/src/sound/munt/File.h index a4b099fbb..2aa34b4c7 100644 --- a/src/sound/munt/File.h +++ b/src/sound/munt/File.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/FileStream.cpp b/src/sound/munt/FileStream.cpp index 3fa1a3107..5e32c10d6 100644 --- a/src/sound/munt/FileStream.cpp +++ b/src/sound/munt/FileStream.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#ifdef MT32EMU_SHARED -#include +#if defined MT32EMU_SHARED && defined MT32EMU_INSTALL_DEFAULT_LOCALE +#include #endif #include "internals.h" @@ -25,13 +25,18 @@ namespace MT32Emu { +// This initialises C locale with the user-preferred system locale once facilitating access +// to ROM files with localised pathnames. This is only necessary in rare cases e.g. when building +// shared library statically linked with C runtime with old MS VC versions, so that the C locale +// set by the client application does not have effect, and thus such ROM files cannot be opened. static inline void configureSystemLocale() { -#ifdef MT32EMU_SHARED +#if defined MT32EMU_SHARED && defined MT32EMU_INSTALL_DEFAULT_LOCALE static bool configured = false; if (configured) return; configured = true; - std::locale::global(std::locale("")); + + setlocale(LC_ALL, ""); #endif } diff --git a/src/sound/munt/FileStream.h b/src/sound/munt/FileStream.h index 2279890b4..3b3976869 100644 --- a/src/sound/munt/FileStream.h +++ b/src/sound/munt/FileStream.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/LA32FloatWaveGenerator.cpp b/src/sound/munt/LA32FloatWaveGenerator.cpp index 34ea1fbf4..7aea6c240 100644 --- a/src/sound/munt/LA32FloatWaveGenerator.cpp +++ b/src/sound/munt/LA32FloatWaveGenerator.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -236,7 +236,7 @@ float LA32FloatWaveGenerator::generateNextSample(const Bit32u ampVal, const Bit1 relWavePos -= cosineLen + hLen; } - // To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment + // To ensure the output wave has no breaks, two different windows are applied to the beginning and the ending of the resonance sine segment if (relWavePos < 0.5f * cosineLen) { float syncSine = sin(FLOAT_PI * relWavePos / cosineLen); if (relWavePos < 0.0f) { diff --git a/src/sound/munt/LA32FloatWaveGenerator.h b/src/sound/munt/LA32FloatWaveGenerator.h index a21d68e2b..b34c1fa86 100644 --- a/src/sound/munt/LA32FloatWaveGenerator.h +++ b/src/sound/munt/LA32FloatWaveGenerator.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/LA32Ramp.cpp b/src/sound/munt/LA32Ramp.cpp index 122ee05ac..cc61d8357 100644 --- a/src/sound/munt/LA32Ramp.cpp +++ b/src/sound/munt/LA32Ramp.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/LA32Ramp.h b/src/sound/munt/LA32Ramp.h index 802b34aa4..178e16b60 100644 --- a/src/sound/munt/LA32Ramp.h +++ b/src/sound/munt/LA32Ramp.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/LA32WaveGenerator.cpp b/src/sound/munt/LA32WaveGenerator.cpp index f4f7eeccb..cf1a34c9c 100644 --- a/src/sound/munt/LA32WaveGenerator.cpp +++ b/src/sound/munt/LA32WaveGenerator.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -173,7 +173,7 @@ void LA32WaveGenerator::generateNextResonanceWaveLogSample() { // Unsure about resonanceSinePosition here. It's possible that dedicated counter & decrement are used. Although, cutoff is finely ramped, so maybe not. logSampleValue += resonanceAmpSubtraction + (((resonanceSinePosition >> 4) * decayFactor) >> 8); - // To ensure the output wave has no breaks, two different windows are appied to the beginning and the ending of the resonance sine segment + // To ensure the output wave has no breaks, two different windows are applied to the beginning and the ending of the resonance sine segment if (phase == POSITIVE_RISING_SINE_SEGMENT || phase == NEGATIVE_FALLING_SINE_SEGMENT) { // The window is synchronous sine here logSampleValue += Tables::getInstance().logsin9[(squareWavePosition >> 9) & 511] << 2; @@ -183,7 +183,7 @@ void LA32WaveGenerator::generateNextResonanceWaveLogSample() { } if (cutoffVal < MIDDLE_CUTOFF_VALUE) { - // For the cutoff values below the cutoff middle point, it seems the amp of the resonance wave is expotentially decayed + // For the cutoff values below the cutoff middle point, it seems the amp of the resonance wave is exponentially decayed logSampleValue += 31743 + ((MIDDLE_CUTOFF_VALUE - cutoffVal) >> 9); } else if (cutoffVal < RESONANCE_DECAY_THRESHOLD_CUTOFF_VALUE) { // For the cutoff values below this point, the amp of the resonance wave is sinusoidally decayed diff --git a/src/sound/munt/LA32WaveGenerator.h b/src/sound/munt/LA32WaveGenerator.h index d2d74f48d..71d909df8 100644 --- a/src/sound/munt/LA32WaveGenerator.h +++ b/src/sound/munt/LA32WaveGenerator.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/MemoryRegion.h b/src/sound/munt/MemoryRegion.h index c8e85c7fb..1f224768c 100644 --- a/src/sound/munt/MemoryRegion.h +++ b/src/sound/munt/MemoryRegion.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -120,7 +120,9 @@ public: }; class DisplayMemoryRegion : public MemoryRegion { public: - DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), SYSEX_BUFFER_SIZE - 1, 1) {} + // Note, we set realMemory to NULL despite the real devices buffer inbound strings. However, it is impossible to retrieve them. + // This entrySize permits emulation of handling a 20-byte display message sent to an old-gen device at address 0x207F7F. + DisplayMemoryRegion(Synth *useSynth) : MemoryRegion(useSynth, NULL, NULL, MR_Display, MT32EMU_MEMADDR(0x200000), 0x4013, 1) {} }; class ResetMemoryRegion : public MemoryRegion { public: diff --git a/src/sound/munt/MidiEventQueue.h b/src/sound/munt/MidiEventQueue.h index 846f47c51..b458b8190 100644 --- a/src/sound/munt/MidiEventQueue.h +++ b/src/sound/munt/MidiEventQueue.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/MidiStreamParser.cpp b/src/sound/munt/MidiStreamParser.cpp index e9fbf7690..7b64f97f9 100644 --- a/src/sound/munt/MidiStreamParser.cpp +++ b/src/sound/munt/MidiStreamParser.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/MidiStreamParser.h b/src/sound/munt/MidiStreamParser.h index f26fe11b7..d3a76c8a0 100644 --- a/src/sound/munt/MidiStreamParser.h +++ b/src/sound/munt/MidiStreamParser.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/Part.cpp b/src/sound/munt/Part.cpp index 465903a72..5888b97b2 100644 --- a/src/sound/munt/Part.cpp +++ b/src/sound/munt/Part.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -59,10 +59,12 @@ Part::Part(Synth *useSynth, unsigned int usePartNum) { } currentInstr[0] = 0; currentInstr[10] = 0; + volumeOverride = 255; modulation = 0; expression = 100; pitchBend = 0; activePartialCount = 0; + activeNonReleasingPolyCount = 0; memset(patchCache, 0, sizeof(patchCache)); } @@ -166,7 +168,7 @@ void Part::refresh() { patchCache[t].reverb = patchTemp->patch.reverbSwitch > 0; } memcpy(currentInstr, timbreTemp->common.name, 10); - synth->newTimbreSet(partNum, patchTemp->patch.timbreGroup, patchTemp->patch.timbreNum, currentInstr); + synth->newTimbreSet(partNum); updatePitchBenderRange(); } @@ -317,7 +319,21 @@ void Part::setVolume(unsigned int midiVolume) { } Bit8u Part::getVolume() const { - return patchTemp->outputLevel; + return volumeOverride <= 100 ? volumeOverride : patchTemp->outputLevel; +} + +void Part::setVolumeOverride(Bit8u volume) { + volumeOverride = volume; + // When volume is 0, we want the part to stop producing any sound at all. + // For that to achieve, we have to actually stop processing NoteOn MIDI messages; merely + // returning 0 volume is not enough - the output may still be generated at a very low level. + // But first, we have to stop all the currently playing polys. This behaviour may also help + // with performance issues, because parts muted this way barely consume CPU resources. + if (volume == 0) allSoundOff(); +} + +Bit8u Part::getVolumeOverride() const { + return volumeOverride; } Bit8u Part::getExpression() const { @@ -380,6 +396,7 @@ void RhythmPart::noteOn(unsigned int midiKey, unsigned int velocity) { synth->printDebug("%s: Attempted to play invalid key %d (velocity %d)", name, midiKey, velocity); return; } + synth->rhythmNotePlayed(); unsigned int key = midiKey; unsigned int drumNum = key - 24; int drumTimbreNum = rhythmTemp[drumNum].timbre; @@ -609,6 +626,27 @@ void Part::partialDeactivated(Poly *poly) { } } +void RhythmPart::polyStateChanged(PolyState, PolyState) {} + +void Part::polyStateChanged(PolyState oldState, PolyState newState) { + switch (newState) { + case POLY_Playing: + if (activeNonReleasingPolyCount++ == 0) synth->voicePartStateChanged(partNum, true); + break; + case POLY_Releasing: + case POLY_Inactive: + if (oldState == POLY_Playing || oldState == POLY_Held) { + if (--activeNonReleasingPolyCount == 0) synth->voicePartStateChanged(partNum, false); + } + break; + default: + break; + } +#ifdef MT32EMU_TRACE_POLY_STATE_CHANGES + synth->printDebug("Part %d: Changed poly state %d->%d, activeNonReleasingPolyCount=%d", partNum, oldState, newState, activeNonReleasingPolyCount); +#endif +} + PolyList::PolyList() : firstPoly(NULL), lastPoly(NULL) {} bool PolyList::isEmpty() const { diff --git a/src/sound/munt/Part.h b/src/sound/munt/Part.h index bc2e11416..d266efb7e 100644 --- a/src/sound/munt/Part.h +++ b/src/sound/munt/Part.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -55,6 +55,7 @@ private: bool holdpedal; unsigned int activePartialCount; + unsigned int activeNonReleasingPolyCount; PatchCache patchCache[4]; PolyList activePolys; @@ -69,6 +70,8 @@ protected: MemParams::PatchTemp *patchTemp; char name[8]; // "Part 1".."Part 8", "Rhythm" char currentInstr[11]; + // Values outside the valid range 0..100 imply no override. + Bit8u volumeOverride; Bit8u modulation; Bit8u expression; Bit32s pitchBend; @@ -95,8 +98,10 @@ public: virtual void noteOff(unsigned int midiKey); void allNotesOff(); void allSoundOff(); - Bit8u getVolume() const; // Internal volume, 0-100, exposed for use by ExternalInterface - void setVolume(unsigned int midiVolume); + Bit8u getVolume() const; // Effective output level, valid range 0..100. + void setVolume(unsigned int midiVolume); // Valid range 0..127, as defined for MIDI controller 7. + Bit8u getVolumeOverride() const; + void setVolumeOverride(Bit8u volumeOverride); Bit8u getModulation() const; void setModulation(unsigned int midiModulation); Bit8u getExpression() const; @@ -122,6 +127,7 @@ public: // This should only be called by Poly void partialDeactivated(Poly *poly); + virtual void polyStateChanged(PolyState oldState, PolyState newState); // These are rather specialised, and should probably only be used by PartialManager bool abortFirstPoly(PolyState polyState); @@ -146,6 +152,7 @@ public: unsigned int getAbsTimbreNum() const; void setPan(unsigned int midiPan); void setProgram(unsigned int patchNum); + void polyStateChanged(PolyState oldState, PolyState newState); }; } // namespace MT32Emu diff --git a/src/sound/munt/Partial.cpp b/src/sound/munt/Partial.cpp index 877d93b45..2a4b21d9f 100644 --- a/src/sound/munt/Partial.cpp +++ b/src/sound/munt/Partial.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/Partial.h b/src/sound/munt/Partial.h index 0c4355742..bfc6f6dca 100644 --- a/src/sound/munt/Partial.h +++ b/src/sound/munt/Partial.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/PartialManager.cpp b/src/sound/munt/PartialManager.cpp index 508d5fa6c..609adaa74 100644 --- a/src/sound/munt/PartialManager.cpp +++ b/src/sound/munt/PartialManager.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/PartialManager.h b/src/sound/munt/PartialManager.h index 6b59857cc..5c019effe 100644 --- a/src/sound/munt/PartialManager.h +++ b/src/sound/munt/PartialManager.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/Poly.cpp b/src/sound/munt/Poly.cpp index f37e471d4..0306e51ff 100644 --- a/src/sound/munt/Poly.cpp +++ b/src/sound/munt/Poly.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -53,7 +53,7 @@ void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, activePartialCount--; } } - state = POLY_Inactive; + setState(POLY_Inactive); } key = newKey; @@ -65,7 +65,7 @@ void Poly::reset(unsigned int newKey, unsigned int newVelocity, bool newSustain, partials[i] = newPartials[i]; if (newPartials[i] != NULL) { activePartialCount++; - state = POLY_Playing; + setState(POLY_Playing); } } } @@ -80,7 +80,7 @@ bool Poly::noteOff(bool pedalHeld) { if (state == POLY_Held) { return false; } - state = POLY_Held; + setState(POLY_Held); } else { startDecay(); } @@ -98,7 +98,7 @@ bool Poly::startDecay() { if (state == POLY_Inactive || state == POLY_Releasing) { return false; } - state = POLY_Releasing; + setState(POLY_Releasing); for (int t = 0; t < 4; t++) { Partial *partial = partials[t]; @@ -123,6 +123,13 @@ bool Poly::startAbort() { return true; } +void Poly::setState(PolyState newState) { + if (state == newState) return; + PolyState oldState = state; + state = newState; + part->polyStateChanged(oldState, newState); +} + void Poly::backupCacheToPartials(PatchCache cache[4]) { for (int partialNum = 0; partialNum < 4; partialNum++) { Partial *partial = partials[partialNum]; @@ -171,7 +178,7 @@ void Poly::partialDeactivated(Partial *partial) { } } if (activePartialCount == 0) { - state = POLY_Inactive; + setState(POLY_Inactive); if (part->getSynth()->abortingPoly == this) { part->getSynth()->abortingPoly = NULL; } diff --git a/src/sound/munt/Poly.h b/src/sound/munt/Poly.h index 5b7cc30e4..dd6def094 100644 --- a/src/sound/munt/Poly.h +++ b/src/sound/munt/Poly.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,6 +41,8 @@ private: Poly *next; + void setState(PolyState state); + public: Poly(); void setPart(Part *usePart); diff --git a/src/sound/munt/ROMInfo.cpp b/src/sound/munt/ROMInfo.cpp index 308d3eb1e..0f58cd292 100644 --- a/src/sound/munt/ROMInfo.cpp +++ b/src/sound/munt/ROMInfo.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -19,45 +19,164 @@ #include "internals.h" +#include "File.h" #include "ROMInfo.h" namespace MT32Emu { -static const ROMInfo *getKnownROMInfoFromList(Bit32u index) { - // Known ROMs - static const ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL}; - static const ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL}; +namespace { +struct ROMInfoList { + const ROMInfo * const *romInfos; + const Bit32u itemCount; +}; + +struct ROMInfoLists { + ROMInfoList mt32_1_04; + ROMInfoList mt32_1_05; + ROMInfoList mt32_1_06; + ROMInfoList mt32_1_07; + ROMInfoList mt32_bluer; + ROMInfoList mt32_2_03; + ROMInfoList mt32_2_04; + ROMInfoList mt32_2_06; + ROMInfoList mt32_2_07; + ROMInfoList cm32l_1_00; + ROMInfoList cm32l_1_02; + ROMInfoList cm32ln_1_00; + ROMInfoList fullROMInfos; + ROMInfoList partialROMInfos; + ROMInfoList allROMInfos; +}; + +} + +#define _CALC_ARRAY_LENGTH(x) Bit32u(sizeof (x) / sizeof *(x) - 1) + +static const ROMInfoLists &getROMInfoLists() { + static ROMInfo CTRL_MT32_V1_04_A = {32768, "9cd4858014c4e8a9dff96053f784bfaac1092a2e", ROMInfo::Control, "ctrl_mt32_1_04_a", "MT-32 Control v1.04", ROMInfo::Mux0, NULL}; + static ROMInfo CTRL_MT32_V1_04_B = {32768, "fe8db469b5bfeb37edb269fd47e3ce6d91014652", ROMInfo::Control, "ctrl_mt32_1_04_b", "MT-32 Control v1.04", ROMInfo::Mux1, &CTRL_MT32_V1_04_A}; + static ROMInfo CTRL_MT32_V1_04 = {65536, "5a5cb5a77d7d55ee69657c2f870416daed52dea7", ROMInfo::Control, "ctrl_mt32_1_04", "MT-32 Control v1.04", ROMInfo::Full, NULL}; + static ROMInfo CTRL_MT32_V1_05_A = {32768, "57a09d80d2f7ca5b9734edbe9645e6e700f83701", ROMInfo::Control, "ctrl_mt32_1_05_a", "MT-32 Control v1.05", ROMInfo::Mux0, NULL}; + static ROMInfo CTRL_MT32_V1_05_B = {32768, "52e3c6666db9ef962591a8ee99be0cde17f3a6b6", ROMInfo::Control, "ctrl_mt32_1_05_b", "MT-32 Control v1.05", ROMInfo::Mux1, &CTRL_MT32_V1_05_A}; + static ROMInfo CTRL_MT32_V1_05 = {65536, "e17a3a6d265bf1fa150312061134293d2b58288c", ROMInfo::Control, "ctrl_mt32_1_05", "MT-32 Control v1.05", ROMInfo::Full, NULL}; + static ROMInfo CTRL_MT32_V1_06_A = {32768, "cc83bf23cee533097fb4c7e2c116e43b50ebacc8", ROMInfo::Control, "ctrl_mt32_1_06_a", "MT-32 Control v1.06", ROMInfo::Mux0, NULL}; + static ROMInfo CTRL_MT32_V1_06_B = {32768, "bf4f15666bc46679579498386704893b630c1171", ROMInfo::Control, "ctrl_mt32_1_06_b", "MT-32 Control v1.06", ROMInfo::Mux1, &CTRL_MT32_V1_06_A}; + static ROMInfo CTRL_MT32_V1_06 = {65536, "a553481f4e2794c10cfe597fef154eef0d8257de", ROMInfo::Control, "ctrl_mt32_1_06", "MT-32 Control v1.06", ROMInfo::Full, NULL}; + static ROMInfo CTRL_MT32_V1_07_A = {32768, "13f06b38f0d9e0fc050b6503ab777bb938603260", ROMInfo::Control, "ctrl_mt32_1_07_a", "MT-32 Control v1.07", ROMInfo::Mux0, NULL}; + static ROMInfo CTRL_MT32_V1_07_B = {32768, "c55e165487d71fa88bd8c5e9c083bc456c1a89aa", ROMInfo::Control, "ctrl_mt32_1_07_b", "MT-32 Control v1.07", ROMInfo::Mux1, &CTRL_MT32_V1_07_A}; + static ROMInfo CTRL_MT32_V1_07 = {65536, "b083518fffb7f66b03c23b7eb4f868e62dc5a987", ROMInfo::Control, "ctrl_mt32_1_07", "MT-32 Control v1.07", ROMInfo::Full, NULL}; + static ROMInfo CTRL_MT32_BLUER_A = {32768, "11a6ae5d8b6ee328b371af7f1e40b82125aa6b4d", ROMInfo::Control, "ctrl_mt32_bluer_a", "MT-32 Control BlueRidge", ROMInfo::Mux0, NULL}; + static ROMInfo CTRL_MT32_BLUER_B = {32768, "e0934320d7cbb5edfaa29e0d01ae835ef620085b", ROMInfo::Control, "ctrl_mt32_bluer_b", "MT-32 Control BlueRidge", ROMInfo::Mux1, &CTRL_MT32_BLUER_A}; + static ROMInfo CTRL_MT32_BLUER = {65536, "7b8c2a5ddb42fd0732e2f22b3340dcf5360edf92", ROMInfo::Control, "ctrl_mt32_bluer", "MT-32 Control BlueRidge", ROMInfo::Full, NULL}; + + static const ROMInfo CTRL_MT32_V2_03 = {131072, "5837064c9df4741a55f7c4d8787ac158dff2d3ce", ROMInfo::Control, "ctrl_mt32_2_03", "MT-32 Control v2.03", ROMInfo::Full, NULL}; static const ROMInfo CTRL_MT32_V2_04 = {131072, "2c16432b6c73dd2a3947cba950a0f4c19d6180eb", ROMInfo::Control, "ctrl_mt32_2_04", "MT-32 Control v2.04", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V2_06 = {131072, "2869cf4c235d671668cfcb62415e2ce8323ad4ed", ROMInfo::Control, "ctrl_mt32_2_06", "MT-32 Control v2.06", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_MT32_V2_07 = {131072, "47b52adefedaec475c925e54340e37673c11707c", ROMInfo::Control, "ctrl_mt32_2_07", "MT-32 Control v2.07", ROMInfo::Full, NULL}; static const ROMInfo CTRL_CM32L_V1_00 = {65536, "73683d585cd6948cc19547942ca0e14a0319456d", ROMInfo::Control, "ctrl_cm32l_1_00", "CM-32L/LAPC-I Control v1.00", ROMInfo::Full, NULL}; static const ROMInfo CTRL_CM32L_V1_02 = {65536, "a439fbb390da38cada95a7cbb1d6ca199cd66ef8", ROMInfo::Control, "ctrl_cm32l_1_02", "CM-32L/LAPC-I Control v1.02", ROMInfo::Full, NULL}; + static const ROMInfo CTRL_CM32LN_V1_00 = {65536, "dc1c5b1b90a4646d00f7daf3679733c7badc7077", ROMInfo::Control, "ctrl_cm32ln_1_00", "CM-32LN/CM-500/LAPC-N Control v1.00", ROMInfo::Full, NULL}; - static const ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL}; - static const ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL}; + static ROMInfo PCM_MT32_L = {262144, "3a1e19b0cd4036623fd1d1d11f5f25995585962b", ROMInfo::PCM, "pcm_mt32_l", "MT-32 PCM ROM", ROMInfo::FirstHalf, NULL}; + static ROMInfo PCM_MT32_H = {262144, "2cadb99d21a6a4a6f5b61b6218d16e9b43f61d01", ROMInfo::PCM, "pcm_mt32_h", "MT-32 PCM ROM", ROMInfo::SecondHalf, &PCM_MT32_L}; + static ROMInfo PCM_MT32 = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_mt32", "MT-32 PCM ROM", ROMInfo::Full, NULL}; + // Alias of PCM_MT32 ROM, only useful for pairing with PCM_CM32L_H. + static ROMInfo PCM_CM32L_L = {524288, "f6b1eebc4b2d200ec6d3d21d51325d5b48c60252", ROMInfo::PCM, "pcm_cm32l_l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::FirstHalf, NULL}; + static ROMInfo PCM_CM32L_H = {524288, "3ad889fde5db5b6437cbc2eb6e305312fec3df93", ROMInfo::PCM, "pcm_cm32l_h", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::SecondHalf, &PCM_CM32L_L}; + static ROMInfo PCM_CM32L = {1048576, "289cc298ad532b702461bfc738009d9ebe8025ea", ROMInfo::PCM, "pcm_cm32l", "CM-32L/CM-64/LAPC-I PCM ROM", ROMInfo::Full, NULL}; - static const ROMInfo * const ROM_INFOS[] = { + static const ROMInfo * const FULL_ROM_INFOS[] = { &CTRL_MT32_V1_04, &CTRL_MT32_V1_05, &CTRL_MT32_V1_06, &CTRL_MT32_V1_07, &CTRL_MT32_BLUER, + &CTRL_MT32_V2_03, &CTRL_MT32_V2_04, + &CTRL_MT32_V2_06, + &CTRL_MT32_V2_07, &CTRL_CM32L_V1_00, &CTRL_CM32L_V1_02, + &CTRL_CM32LN_V1_00, &PCM_MT32, &PCM_CM32L, - NULL}; + NULL + }; + static const ROMInfo * const PARTIAL_ROM_INFOS[] = { + &CTRL_MT32_V1_04_A, &CTRL_MT32_V1_04_B, + &CTRL_MT32_V1_05_A, &CTRL_MT32_V1_05_B, + &CTRL_MT32_V1_06_A, &CTRL_MT32_V1_06_B, + &CTRL_MT32_V1_07_A, &CTRL_MT32_V1_07_B, + &CTRL_MT32_BLUER_A, &CTRL_MT32_BLUER_B, + &PCM_MT32_L, &PCM_MT32_H, + &PCM_CM32L_L, &PCM_CM32L_H, + NULL + }; + static const ROMInfo *ALL_ROM_INFOS[_CALC_ARRAY_LENGTH(FULL_ROM_INFOS) + _CALC_ARRAY_LENGTH(PARTIAL_ROM_INFOS) + 1]; - return ROM_INFOS[index]; + if (CTRL_MT32_V1_04_A.pairROMInfo == NULL) { + CTRL_MT32_V1_04_A.pairROMInfo = &CTRL_MT32_V1_04_B; + CTRL_MT32_V1_05_A.pairROMInfo = &CTRL_MT32_V1_05_B; + CTRL_MT32_V1_06_A.pairROMInfo = &CTRL_MT32_V1_06_B; + CTRL_MT32_V1_07_A.pairROMInfo = &CTRL_MT32_V1_07_B; + CTRL_MT32_BLUER_A.pairROMInfo = &CTRL_MT32_BLUER_B; + PCM_MT32_L.pairROMInfo = &PCM_MT32_H; + PCM_CM32L_L.pairROMInfo = &PCM_CM32L_H; + + memcpy(&ALL_ROM_INFOS[0], FULL_ROM_INFOS, sizeof FULL_ROM_INFOS); + memcpy(&ALL_ROM_INFOS[_CALC_ARRAY_LENGTH(FULL_ROM_INFOS)], PARTIAL_ROM_INFOS, sizeof PARTIAL_ROM_INFOS); // Includes NULL terminator. + } + + static const ROMInfo * const MT32_V1_04_ROMS[] = {&CTRL_MT32_V1_04, &PCM_MT32, &CTRL_MT32_V1_04_A, &CTRL_MT32_V1_04_B, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V1_05_ROMS[] = {&CTRL_MT32_V1_05, &PCM_MT32, &CTRL_MT32_V1_05_A, &CTRL_MT32_V1_05_B, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V1_06_ROMS[] = {&CTRL_MT32_V1_06, &PCM_MT32, &CTRL_MT32_V1_06_A, &CTRL_MT32_V1_06_B, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V1_07_ROMS[] = {&CTRL_MT32_V1_07, &PCM_MT32, &CTRL_MT32_V1_07_A, &CTRL_MT32_V1_07_B, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_BLUER_ROMS[] = {&CTRL_MT32_BLUER, &PCM_MT32, &CTRL_MT32_BLUER_A, &CTRL_MT32_BLUER_B, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V2_03_ROMS[] = {&CTRL_MT32_V2_03, &PCM_MT32, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V2_04_ROMS[] = {&CTRL_MT32_V2_04, &PCM_MT32, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V2_06_ROMS[] = {&CTRL_MT32_V2_06, &PCM_MT32, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const MT32_V2_07_ROMS[] = {&CTRL_MT32_V2_07, &PCM_MT32, &PCM_MT32_L, &PCM_MT32_H, NULL}; + static const ROMInfo * const CM32L_V1_00_ROMS[] = {&CTRL_CM32L_V1_00, &PCM_CM32L, &PCM_CM32L_L, &PCM_CM32L_H, NULL}; + static const ROMInfo * const CM32L_V1_02_ROMS[] = {&CTRL_CM32L_V1_02, &PCM_CM32L, &PCM_CM32L_L, &PCM_CM32L_H, NULL}; + static const ROMInfo * const CM32LN_V1_00_ROMS[] = {&CTRL_CM32LN_V1_00, &PCM_CM32L, NULL}; + + static const ROMInfoLists romInfoLists = { + {MT32_V1_04_ROMS, _CALC_ARRAY_LENGTH(MT32_V1_04_ROMS)}, + {MT32_V1_05_ROMS, _CALC_ARRAY_LENGTH(MT32_V1_05_ROMS)}, + {MT32_V1_06_ROMS, _CALC_ARRAY_LENGTH(MT32_V1_06_ROMS)}, + {MT32_V1_07_ROMS, _CALC_ARRAY_LENGTH(MT32_V1_07_ROMS)}, + {MT32_BLUER_ROMS, _CALC_ARRAY_LENGTH(MT32_BLUER_ROMS)}, + {MT32_V2_03_ROMS, _CALC_ARRAY_LENGTH(MT32_V2_03_ROMS)}, + {MT32_V2_04_ROMS, _CALC_ARRAY_LENGTH(MT32_V2_04_ROMS)}, + {MT32_V2_06_ROMS, _CALC_ARRAY_LENGTH(MT32_V2_06_ROMS)}, + {MT32_V2_07_ROMS, _CALC_ARRAY_LENGTH(MT32_V2_07_ROMS)}, + {CM32L_V1_00_ROMS, _CALC_ARRAY_LENGTH(CM32L_V1_00_ROMS)}, + {CM32L_V1_02_ROMS, _CALC_ARRAY_LENGTH(CM32L_V1_02_ROMS)}, + {CM32LN_V1_00_ROMS, _CALC_ARRAY_LENGTH(CM32LN_V1_00_ROMS)}, + {FULL_ROM_INFOS, _CALC_ARRAY_LENGTH(FULL_ROM_INFOS)}, + {PARTIAL_ROM_INFOS, _CALC_ARRAY_LENGTH(PARTIAL_ROM_INFOS)}, + {ALL_ROM_INFOS, _CALC_ARRAY_LENGTH(ALL_ROM_INFOS)} + }; + return romInfoLists; } -const ROMInfo* ROMInfo::getROMInfo(File *file) { +static const ROMInfo * const *getKnownROMInfoList() { + return getROMInfoLists().allROMInfos.romInfos; +} + +static const ROMInfo *getKnownROMInfoFromList(Bit32u index) { + return getKnownROMInfoList()[index]; +} + +const ROMInfo *ROMInfo::getROMInfo(File *file) { + return getROMInfo(file, getKnownROMInfoList()); +} + +const ROMInfo *ROMInfo::getROMInfo(File *file, const ROMInfo * const *romInfos) { size_t fileSize = file->getSize(); - for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { - const ROMInfo *romInfo = getKnownROMInfoFromList(i); + for (Bit32u i = 0; romInfos[i] != NULL; i++) { + const ROMInfo *romInfo = romInfos[i]; if (fileSize == romInfo->fileSize && !strcmp(file->getSHA1(), romInfo->sha1Digest)) { return romInfo; } @@ -69,17 +188,11 @@ void ROMInfo::freeROMInfo(const ROMInfo *romInfo) { (void) romInfo; } -static Bit32u getROMCount() { - Bit32u count; - for(count = 0; getKnownROMInfoFromList(count) != NULL; count++) { - } - return count; -} - -const ROMInfo** ROMInfo::getROMInfoList(Bit32u types, Bit32u pairTypes) { - const ROMInfo **romInfoList = new const ROMInfo*[getROMCount() + 1]; +const ROMInfo **ROMInfo::getROMInfoList(Bit32u types, Bit32u pairTypes) { + Bit32u romCount = getROMInfoLists().allROMInfos.itemCount; // Excludes the NULL terminator. + const ROMInfo **romInfoList = new const ROMInfo*[romCount + 1]; const ROMInfo **currentROMInList = romInfoList; - for (Bit32u i = 0; getKnownROMInfoFromList(i) != NULL; i++) { + for (Bit32u i = 0; i < romCount; i++) { const ROMInfo *romInfo = getKnownROMInfoFromList(i); if ((types & (1 << romInfo->type)) && (pairTypes & (1 << romInfo->pairType))) { *currentROMInList++ = romInfo; @@ -93,27 +206,157 @@ void ROMInfo::freeROMInfoList(const ROMInfo **romInfoList) { delete[] romInfoList; } -ROMImage::ROMImage(File *useFile) : file(useFile), romInfo(ROMInfo::getROMInfo(file)) +const ROMInfo * const *ROMInfo::getAllROMInfos(Bit32u *itemCount) { + if (itemCount != NULL) *itemCount = getROMInfoLists().allROMInfos.itemCount; + return getROMInfoLists().allROMInfos.romInfos; +} + +const ROMInfo * const *ROMInfo::getFullROMInfos(Bit32u *itemCount) { + if (itemCount != NULL) *itemCount = getROMInfoLists().fullROMInfos.itemCount; + return getROMInfoLists().fullROMInfos.romInfos; +} + +const ROMInfo * const *ROMInfo::getPartialROMInfos(Bit32u *itemCount) { + if (itemCount != NULL) *itemCount = getROMInfoLists().partialROMInfos.itemCount; + return getROMInfoLists().partialROMInfos.romInfos; +} + +const ROMImage *ROMImage::makeFullROMImage(Bit8u *data, size_t dataSize) { + return new ROMImage(new ArrayFile(data, dataSize), true, getKnownROMInfoList()); +} + +const ROMImage *ROMImage::appendImages(const ROMImage *romImageLow, const ROMImage *romImageHigh) { + const Bit8u *romDataLow = romImageLow->getFile()->getData(); + const Bit8u *romDataHigh = romImageHigh->getFile()->getData(); + size_t partSize = romImageLow->getFile()->getSize(); + Bit8u *data = new Bit8u[2 * partSize]; + memcpy(data, romDataLow, partSize); + memcpy(data + partSize, romDataHigh, partSize); + const ROMImage *romImageFull = makeFullROMImage(data, 2 * partSize); + if (romImageFull->getROMInfo() == NULL) { + freeROMImage(romImageFull); + return NULL; + } + return romImageFull; +} + +const ROMImage *ROMImage::interleaveImages(const ROMImage *romImageEven, const ROMImage *romImageOdd) { + const Bit8u *romDataEven = romImageEven->getFile()->getData(); + const Bit8u *romDataOdd = romImageOdd->getFile()->getData(); + size_t partSize = romImageEven->getFile()->getSize(); + Bit8u *data = new Bit8u[2 * partSize]; + Bit8u *writePtr = data; + for (size_t romDataIx = 0; romDataIx < partSize; romDataIx++) { + *(writePtr++) = romDataEven[romDataIx]; + *(writePtr++) = romDataOdd[romDataIx]; + } + const ROMImage *romImageFull = makeFullROMImage(data, 2 * partSize); + if (romImageFull->getROMInfo() == NULL) { + freeROMImage(romImageFull); + return NULL; + } + return romImageFull; +} + +ROMImage::ROMImage(File *useFile, bool useOwnFile, const ROMInfo * const *romInfos) : + file(useFile), ownFile(useOwnFile), romInfo(ROMInfo::getROMInfo(file, romInfos)) {} ROMImage::~ROMImage() { ROMInfo::freeROMInfo(romInfo); + if (ownFile) { + const Bit8u *data = file->getData(); + delete file; + delete[] data; + } } -const ROMImage* ROMImage::makeROMImage(File *file) { - return new ROMImage(file); +const ROMImage *ROMImage::makeROMImage(File *file) { + return new ROMImage(file, false, getKnownROMInfoList()); +} + +const ROMImage *ROMImage::makeROMImage(File *file, const ROMInfo * const *romInfos) { + return new ROMImage(file, false, romInfos); +} + +const ROMImage *ROMImage::makeROMImage(File *file1, File *file2) { + const ROMInfo * const *partialROMInfos = getROMInfoLists().partialROMInfos.romInfos; + const ROMImage *image1 = makeROMImage(file1, partialROMInfos); + const ROMImage *image2 = makeROMImage(file2, partialROMInfos); + const ROMImage *fullImage = image1->getROMInfo() == NULL || image2->getROMInfo() == NULL ? NULL : mergeROMImages(image1, image2); + freeROMImage(image1); + freeROMImage(image2); + return fullImage; } void ROMImage::freeROMImage(const ROMImage *romImage) { delete romImage; } -File* ROMImage::getFile() const { +const ROMImage *ROMImage::mergeROMImages(const ROMImage *romImage1, const ROMImage *romImage2) { + if (romImage1->romInfo->pairROMInfo != romImage2->romInfo) { + return NULL; + } + switch (romImage1->romInfo->pairType) { + case ROMInfo::FirstHalf: + return appendImages(romImage1, romImage2); + case ROMInfo::SecondHalf: + return appendImages(romImage2, romImage1); + case ROMInfo::Mux0: + return interleaveImages(romImage1, romImage2); + case ROMInfo::Mux1: + return interleaveImages(romImage2, romImage1); + default: + break; + } + return NULL; +} + +File *ROMImage::getFile() const { return file; } -const ROMInfo* ROMImage::getROMInfo() const { +bool ROMImage::isFileUserProvided() const { + return !ownFile; +} + +const ROMInfo *ROMImage::getROMInfo() const { return romInfo; } +const MachineConfiguration * const *MachineConfiguration::getAllMachineConfigurations(Bit32u *itemCount) { + static const ROMInfoLists &romInfoLists = getROMInfoLists(); + static const MachineConfiguration MT32_1_04 = MachineConfiguration("mt32_1_04", romInfoLists.mt32_1_04.romInfos, romInfoLists.mt32_1_04.itemCount); + static const MachineConfiguration MT32_1_05 = MachineConfiguration("mt32_1_05", romInfoLists.mt32_1_05.romInfos, romInfoLists.mt32_1_05.itemCount); + static const MachineConfiguration MT32_1_06 = MachineConfiguration("mt32_1_06", romInfoLists.mt32_1_06.romInfos, romInfoLists.mt32_1_06.itemCount); + static const MachineConfiguration MT32_1_07 = MachineConfiguration("mt32_1_07", romInfoLists.mt32_1_07.romInfos, romInfoLists.mt32_1_07.itemCount); + static const MachineConfiguration MT32_BLUER = MachineConfiguration("mt32_bluer", romInfoLists.mt32_bluer.romInfos, romInfoLists.mt32_bluer.itemCount); + static const MachineConfiguration MT32_2_03 = MachineConfiguration("mt32_2_03", romInfoLists.mt32_2_03.romInfos, romInfoLists.mt32_2_03.itemCount); + static const MachineConfiguration MT32_2_04 = MachineConfiguration("mt32_2_04", romInfoLists.mt32_2_04.romInfos, romInfoLists.mt32_2_04.itemCount); + static const MachineConfiguration MT32_2_06 = MachineConfiguration("mt32_2_06", romInfoLists.mt32_2_06.romInfos, romInfoLists.mt32_2_06.itemCount); + static const MachineConfiguration MT32_2_07 = MachineConfiguration("mt32_2_07", romInfoLists.mt32_2_07.romInfos, romInfoLists.mt32_2_07.itemCount); + static const MachineConfiguration CM32L_1_00 = MachineConfiguration("cm32l_1_00", romInfoLists.cm32l_1_00.romInfos, romInfoLists.cm32l_1_00.itemCount); + static const MachineConfiguration CM32L_1_02 = MachineConfiguration("cm32l_1_02", romInfoLists.cm32l_1_02.romInfos, romInfoLists.cm32l_1_02.itemCount); + static const MachineConfiguration CM32LN_1_00 = MachineConfiguration("cm32ln_1_00", romInfoLists.cm32ln_1_00.romInfos, romInfoLists.cm32ln_1_00.itemCount); + static const MachineConfiguration * const MACHINE_CONFIGURATIONS[] = { + &MT32_1_04, &MT32_1_05, &MT32_1_06, &MT32_1_07, &MT32_BLUER, &MT32_2_03, &MT32_2_04, &MT32_2_06, &MT32_2_07, &CM32L_1_00, &CM32L_1_02, &CM32LN_1_00, NULL + }; + + if (itemCount != NULL) *itemCount = _CALC_ARRAY_LENGTH(MACHINE_CONFIGURATIONS); + return MACHINE_CONFIGURATIONS; +} + +MachineConfiguration::MachineConfiguration(const char *useMachineID, const ROMInfo * const *useROMInfos, Bit32u useROMInfosCount) : + machineID(useMachineID), romInfos(useROMInfos), romInfosCount(useROMInfosCount) +{} + +const char *MachineConfiguration::getMachineID() const { + return machineID; +} + +const ROMInfo * const *MachineConfiguration::getCompatibleROMInfos(Bit32u *itemCount) const { + if (itemCount != NULL) *itemCount = romInfosCount; + return romInfos; +} + } // namespace MT32Emu diff --git a/src/sound/munt/ROMInfo.h b/src/sound/munt/ROMInfo.h index b695ba2a1..1580362f4 100644 --- a/src/sound/munt/ROMInfo.h +++ b/src/sound/munt/ROMInfo.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -34,11 +34,28 @@ public: enum Type {PCM, Control, Reverb} type; const char *shortName; const char *description; - enum PairType {Full, FirstHalf, SecondHalf, Mux0, Mux1} pairType; - ROMInfo *pairROMInfo; + enum PairType { + // Complete ROM image ready to use with Synth. + Full, + // ROM image contains data that occupies lower addresses. Needs pairing before use. + FirstHalf, + // ROM image contains data that occupies higher addresses. Needs pairing before use. + SecondHalf, + // ROM image contains data that occupies even addresses. Needs pairing before use. + Mux0, + // ROM image contains data that occupies odd addresses. Needs pairing before use. + Mux1 + } pairType; + // NULL for Full images or a pointer to the corresponding other image for pairing. + const ROMInfo *pairROMInfo; - // Returns a ROMInfo struct by inspecting the size and the SHA1 hash - MT32EMU_EXPORT static const ROMInfo* getROMInfo(File *file); + // Returns a ROMInfo struct by inspecting the size and the SHA1 hash of the file + // among all the known ROMInfos. + MT32EMU_EXPORT static const ROMInfo *getROMInfo(File *file); + + // Returns a ROMInfo struct by inspecting the size and the SHA1 hash of the file + // among the ROMInfos listed in the NULL-terminated list romInfos. + MT32EMU_EXPORT_V(2.5) static const ROMInfo *getROMInfo(File *file, const ROMInfo * const *romInfos); // Currently no-op MT32EMU_EXPORT static void freeROMInfo(const ROMInfo *romInfo); @@ -46,33 +63,111 @@ public: // Allows retrieving a NULL-terminated list of ROMInfos for a range of types and pairTypes // (specified by bitmasks) // Useful for GUI/console app to output information on what ROMs it supports - MT32EMU_EXPORT static const ROMInfo** getROMInfoList(Bit32u types, Bit32u pairTypes); + // The caller must free the returned list with freeROMInfoList when finished. + MT32EMU_EXPORT static const ROMInfo **getROMInfoList(Bit32u types, Bit32u pairTypes); - // Frees the list of ROMInfos given + // Frees the list of ROMInfos given that has been created by getROMInfoList. MT32EMU_EXPORT static void freeROMInfoList(const ROMInfo **romInfos); + + // Returns an immutable NULL-terminated list of all (full and partial) supported ROMInfos. + // For convenience, this method also can fill the number of non-NULL items present in the list + // if a non-NULL value is provided in optional argument itemCount. + MT32EMU_EXPORT_V(2.5) static const ROMInfo * const *getAllROMInfos(Bit32u *itemCount = NULL); + // Returns an immutable NULL-terminated list of all supported full ROMInfos. + // For convenience, this method also can fill the number of non-NULL items present in the list + // if a non-NULL value is provided in optional argument itemCount. + MT32EMU_EXPORT_V(2.5) static const ROMInfo * const *getFullROMInfos(Bit32u *itemCount = NULL); + // Returns an immutable NULL-terminated list of all supported partial ROMInfos. + // For convenience, this method also can fill the number of non-NULL items present in the list + // if a non-NULL value is provided in optional argument itemCount. + MT32EMU_EXPORT_V(2.5) static const ROMInfo * const *getPartialROMInfos(Bit32u *itemCount = NULL); }; -// Synth::open() is to require a full control ROMImage and a full PCM ROMImage to work +// Synth::open() requires a full control ROMImage and a compatible full PCM ROMImage to work class ROMImage { -private: - File * const file; - const ROMInfo * const romInfo; - - ROMImage(File *file); - ~ROMImage(); - public: // Creates a ROMImage object given a ROMInfo and a File. Keeps a reference // to the File and ROMInfo given, which must be freed separately by the user - // after the ROMImage is freed - MT32EMU_EXPORT static const ROMImage* makeROMImage(File *file); + // after the ROMImage is freed. + // CAVEAT: This method always prefers full ROM images over partial ones. + // Because the lower half of CM-32L/CM-64/LAPC-I PCM ROM is essentially the full + // MT-32 PCM ROM, it is therefore aliased. In this case a partial image can only be + // created by the overridden method makeROMImage(File *, const ROMInfo * const *). + MT32EMU_EXPORT static const ROMImage *makeROMImage(File *file); + + // Same as the method above but only permits creation of a ROMImage if the file content + // matches one of the ROMs described in a NULL-terminated list romInfos. This list can be + // created using e.g. method ROMInfo::getROMInfoList. + MT32EMU_EXPORT_V(2.5) static const ROMImage *makeROMImage(File *file, const ROMInfo * const *romInfos); + + // Creates a ROMImage object given a couple of files that contain compatible partial ROM images. + // The files aren't referenced by the resulting ROMImage and may be freed anytime afterwards. + // The file in the resulting image will be automatically freed along with the ROMImage. + // If the given files contain incompatible partial images, NULL is returned. + MT32EMU_EXPORT_V(2.5) static const ROMImage *makeROMImage(File *file1, File *file2); // Must only be done after all Synths using the ROMImage are deleted MT32EMU_EXPORT static void freeROMImage(const ROMImage *romImage); + // Checks whether the given ROMImages are pairable and merges them into a full image, if possible. + // If the check fails, NULL is returned. + MT32EMU_EXPORT_V(2.5) static const ROMImage *mergeROMImages(const ROMImage *romImage1, const ROMImage *romImage2); + MT32EMU_EXPORT File *getFile() const; + + // Returns true in case this ROMImage is built with a user provided File that has to be deallocated separately. + // For a ROMImage created via merging two partial ROMImages, this method returns false. + MT32EMU_EXPORT_V(2.5) bool isFileUserProvided() const; MT32EMU_EXPORT const ROMInfo *getROMInfo() const; + +private: + static const ROMImage *makeFullROMImage(Bit8u *data, size_t dataSize); + static const ROMImage *appendImages(const ROMImage *romImageLow, const ROMImage *romImageHigh); + static const ROMImage *interleaveImages(const ROMImage *romImageEven, const ROMImage *romImageOdd); + + File * const file; + const bool ownFile; + const ROMInfo * const romInfo; + + ROMImage(File *file, bool ownFile, const ROMInfo * const *romInfos); + ~ROMImage(); + + // Make ROMIMage an identity class. + ROMImage(const ROMImage &); + ROMImage &operator=(const ROMImage &); +}; + +class MachineConfiguration { +public: + // Returns an immutable NULL-terminated list of all supported machine configurations. + // For convenience, this method also can fill the number of non-NULL items present in the list + // if a non-NULL value is provided in optional argument itemCount. + MT32EMU_EXPORT_V(2.5) static const MachineConfiguration * const *getAllMachineConfigurations(Bit32u *itemCount = NULL); + + // Returns a string identifier of this MachineConfiguration. + MT32EMU_EXPORT_V(2.5) const char *getMachineID() const; + + // Returns an immutable NULL-terminated list of ROMInfos that are compatible with this + // MachineConfiguration. That means the respective ROMImages can be successfully used together + // by the emulation engine. Calling ROMInfo::getROMInfo or ROMImage::makeROMImage with this list + // supplied enables identification of all files containing desired ROM images while filtering out + // any incompatible ones. + // For convenience, this method also can fill the number of non-NULL items present in the list + // if a non-NULL value is provided in optional argument itemCount. + MT32EMU_EXPORT_V(2.5) const ROMInfo * const *getCompatibleROMInfos(Bit32u *itemCount = NULL) const; + +private: + const char * const machineID; + const ROMInfo * const * const romInfos; + const Bit32u romInfosCount; + + MachineConfiguration(const char *machineID, const ROMInfo * const *romInfos, Bit32u romInfosCount); + + // Make MachineConfiguration an identity class. + MachineConfiguration(const MachineConfiguration &); + ~MachineConfiguration() {} + MachineConfiguration &operator=(const MachineConfiguration &); }; } // namespace MT32Emu diff --git a/src/sound/munt/SampleRateConverter.cpp b/src/sound/munt/SampleRateConverter.cpp index 9ae35e962..cce6f59fd 100644 --- a/src/sound/munt/SampleRateConverter.cpp +++ b/src/sound/munt/SampleRateConverter.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/SampleRateConverter.h b/src/sound/munt/SampleRateConverter.h index 96f3925e3..6831ff2a0 100644 --- a/src/sound/munt/SampleRateConverter.h +++ b/src/sound/munt/SampleRateConverter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/SampleRateConverter_dummy.cpp b/src/sound/munt/SampleRateConverter_dummy.cpp deleted file mode 100644 index 09f491338..000000000 --- a/src/sound/munt/SampleRateConverter_dummy.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright (C) 2015-2017 Sergey V. Mikayev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 2.1 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -#include -#include -#include <86box/plat.h> -#include "SampleRateConverter.h" - -#include "Synth.h" - -using namespace MT32Emu; - -static inline void *createDelegate(UNUSED(Synth &synth), UNUSED(double targetSampleRate), UNUSED(SamplerateConversionQuality quality)) { - return 0; -} - -AnalogOutputMode SampleRateConverter::getBestAnalogOutputMode(UNUSED(double targetSampleRate)) { - return AnalogOutputMode_COARSE; -} - -SampleRateConverter::SampleRateConverter(Synth &useSynth, double targetSampleRate, SamplerateConversionQuality useQuality) : - synthInternalToTargetSampleRateRatio(SAMPLE_RATE / targetSampleRate), - useSynthDelegate(useSynth.getStereoOutputSampleRate() == targetSampleRate), - srcDelegate(useSynthDelegate ? &useSynth : createDelegate(useSynth, targetSampleRate, useQuality)) -{} - -SampleRateConverter::~SampleRateConverter() { -} - -void SampleRateConverter::getOutputSamples(float *buffer, unsigned int length) { - if (useSynthDelegate) { - static_cast(srcDelegate)->render(buffer, length); - return; - } -} - -void SampleRateConverter::getOutputSamples(Bit16s *outBuffer, unsigned int length) { - if (useSynthDelegate) { - static_cast(srcDelegate)->render(outBuffer, length); - return; - } -} - -double SampleRateConverter::convertOutputToSynthTimestamp(double outputTimestamp) const { - return outputTimestamp * synthInternalToTargetSampleRateRatio; -} - -double SampleRateConverter::convertSynthToOutputTimestamp(double synthTimestamp) const { - return synthTimestamp / synthInternalToTargetSampleRateRatio; -} diff --git a/src/sound/munt/Structures.h b/src/sound/munt/Structures.h index 8202c44b9..171033325 100644 --- a/src/sound/munt/Structures.h +++ b/src/sound/munt/Structures.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -191,6 +191,9 @@ struct ControlROMFeatureSet { unsigned int quirkPanMult : 1; unsigned int quirkKeyShift : 1; unsigned int quirkTVFBaseCutoffLimit : 1; + unsigned int quirkFastPitchChanges : 1; + unsigned int quirkDisplayCustomMessagePriority : 1; + unsigned int oldMT32DisplayFeatures : 1; // Features below don't actually depend on control ROM version, which is used to identify hardware model unsigned int defaultReverbMT32Compatible : 1; @@ -221,6 +224,8 @@ struct ControlROMMap { Bit16u timbreMaxTable; // 72 bytes Bit16u soundGroupsTable; // 14 bytes each entry Bit16u soundGroupsCount; + Bit16u startupMessage; // 20 characters + NULL terminator + Bit16u sysexErrorMessage; // 20 characters + NULL terminator }; struct ControlROMPCMStruct { diff --git a/src/sound/munt/Synth.cpp b/src/sound/munt/Synth.cpp index d61ad44a6..0b81edb93 100644 --- a/src/sound/munt/Synth.cpp +++ b/src/sound/munt/Synth.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -22,6 +22,7 @@ #include "Synth.h" #include "Analog.h" #include "BReverbModel.h" +#include "Display.h" #include "File.h" #include "MemoryRegion.h" #include "MidiEventQueue.h" @@ -41,19 +42,35 @@ namespace MT32Emu { // MIDI interface data transfer rate in samples. Used to simulate the transfer delay. static const double MIDI_DATA_TRANSFER_RATE = double(SAMPLE_RATE) / 31250.0 * 8.0; -// FIXME: there should be more specific feature sets for various MT-32 control ROM versions -static const ControlROMFeatureSet OLD_MT32_COMPATIBLE = { - true, // quirkBasePitchOverflow - true, // quirkPitchEnvelopeOverflow - true, // quirkRingModulationNoMix - true, // quirkTVAZeroEnvLevels - true, // quirkPanMult - true, // quirkKeyShift - true, // quirkTVFBaseCutoffLimit - true, // defaultReverbMT32Compatible - true // oldMT32AnalogLPF +static const ControlROMFeatureSet OLD_MT32_ELDER = { + true, // quirkBasePitchOverflow + true, // quirkPitchEnvelopeOverflow + true, // quirkRingModulationNoMix + true, // quirkTVAZeroEnvLevels + true, // quirkPanMult + true, // quirkKeyShift + true, // quirkTVFBaseCutoffLimit + false, // quirkFastPitchChanges + true, // quirkDisplayCustomMessagePriority + true, // oldMT32DisplayFeatures + true, // defaultReverbMT32Compatible + true // oldMT32AnalogLPF }; -static const ControlROMFeatureSet CM32L_COMPATIBLE = { +static const ControlROMFeatureSet OLD_MT32_LATER = { + true, // quirkBasePitchOverflow + true, // quirkPitchEnvelopeOverflow + true, // quirkRingModulationNoMix + true, // quirkTVAZeroEnvLevels + true, // quirkPanMult + true, // quirkKeyShift + true, // quirkTVFBaseCutoffLimit + false, // quirkFastPitchChanges + false, // quirkDisplayCustomMessagePriority + true, // oldMT32DisplayFeatures + true, // defaultReverbMT32Compatible + true // oldMT32AnalogLPF +}; +static const ControlROMFeatureSet NEW_MT32_COMPATIBLE = { false, // quirkBasePitchOverflow false, // quirkPitchEnvelopeOverflow false, // quirkRingModulationNoMix @@ -61,20 +78,41 @@ static const ControlROMFeatureSet CM32L_COMPATIBLE = { false, // quirkPanMult false, // quirkKeyShift false, // quirkTVFBaseCutoffLimit + false, // quirkFastPitchChanges + false, // quirkDisplayCustomMessagePriority + false, // oldMT32DisplayFeatures false, // defaultReverbMT32Compatible - false // oldMT32AnalogLPF + false // oldMT32AnalogLPF +}; +static const ControlROMFeatureSet CM32LN_COMPATIBLE = { + false, // quirkBasePitchOverflow + false, // quirkPitchEnvelopeOverflow + false, // quirkRingModulationNoMix + false, // quirkTVAZeroEnvLevels + false, // quirkPanMult + false, // quirkKeyShift + false, // quirkTVFBaseCutoffLimit + true, // quirkFastPitchChanges + false, // quirkDisplayCustomMessagePriority + false, // oldMT32DisplayFeatures + false, // defaultReverbMT32Compatible + false // oldMT32AnalogLPF }; -static const ControlROMMap ControlROMMaps[8] = { - // ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC - { "ctrl_mt32_1_04", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19 }, - { "ctrl_mt32_1_05", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19 }, - { "ctrl_mt32_1_06", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19 }, - { "ctrl_mt32_1_07", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19 }, // MT-32 revision 1 - {"ctrl_mt32_bluer", OLD_MT32_COMPATIBLE, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19 }, // MT-32 Blue Ridge mod - {"ctrl_mt32_2_04", CM32L_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 30, 0x8580, 85, 0x4F5D, 0x4F78, 0x4F66, 0x4899, 0x489D, 0x48B6, 0x48CD, 0x5A58, 19 }, - {"ctrl_cm32l_1_00", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19 }, - {"ctrl_cm32l_1_02", CM32L_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19 } // CM-32L +static const ControlROMMap ControlROMMaps[] = { + // ID Features PCMmap PCMc tmbrA tmbrAO, tmbrAC tmbrB tmbrBO tmbrBC tmbrR trC rhythm rhyC rsrv panpot prog rhyMax patMax sysMax timMax sndGrp sGC stMsg sErMsg + {"ctrl_mt32_1_04", OLD_MT32_ELDER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73A6, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x7064, 19, 0x217A, 0x4BB6}, + {"ctrl_mt32_1_05", OLD_MT32_ELDER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57C7, 0x57E2, 0x57D0, 0x5252, 0x525E, 0x526E, 0x520A, 0x70CA, 19, 0x217A, 0x4BB6}, + {"ctrl_mt32_1_06", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x7414, 85, 0x57D9, 0x57F4, 0x57E2, 0x5264, 0x5270, 0x5280, 0x521C, 0x70CA, 19, 0x217A, 0x4BBA}, + {"ctrl_mt32_1_07", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x73fe, 85, 0x57B1, 0x57CC, 0x57BA, 0x523C, 0x5248, 0x5258, 0x51F4, 0x70B0, 19, 0x217A, 0x4B92}, + {"ctrl_mt32_bluer", OLD_MT32_LATER, 0x3000, 128, 0x8000, 0x0000, false, 0xC000, 0x4000, false, 0x3200, 30, 0x741C, 85, 0x57E5, 0x5800, 0x57EE, 0x5270, 0x527C, 0x528C, 0x5228, 0x70CE, 19, 0x217A, 0x4BC6}, + {"ctrl_mt32_2_03", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F49, 0x4F64, 0x4F52, 0x4885, 0x4889, 0x48A2, 0x48B9, 0x5A44, 19, 0x1EF0, 0x4066}, + {"ctrl_mt32_2_04", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F5D, 0x4F78, 0x4F66, 0x4899, 0x489D, 0x48B6, 0x48CD, 0x5A58, 19, 0x1EF0, 0x406D}, + {"ctrl_mt32_2_06", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F69, 0x4F84, 0x4F72, 0x48A5, 0x48A9, 0x48C2, 0x48D9, 0x5A64, 19, 0x1EF0, 0x4021}, + {"ctrl_mt32_2_07", NEW_MT32_COMPATIBLE, 0x8100, 128, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F81, 0x4F9C, 0x4F8A, 0x48B9, 0x48BD, 0x48D6, 0x48ED, 0x5A78, 19, 0x1EE7, 0x4035}, + {"ctrl_cm32l_1_00", NEW_MT32_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F65, 0x4F80, 0x4F6E, 0x48A1, 0x48A5, 0x48BE, 0x48D5, 0x5A6C, 19, 0x1EF0, 0x401D}, + {"ctrl_cm32l_1_02", NEW_MT32_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4F93, 0x4FAE, 0x4F9C, 0x48CB, 0x48CF, 0x48E8, 0x48FF, 0x5A96, 19, 0x1EE7, 0x4047}, + {"ctrl_cm32ln_1_00", CM32LN_COMPATIBLE, 0x8100, 256, 0x8000, 0x8000, true, 0x8080, 0x8000, true, 0x8500, 64, 0x8580, 85, 0x4EC7, 0x4EE2, 0x4ED0, 0x47FF, 0x4803, 0x481C, 0x4833, 0x55A2, 19, 0x1F59, 0x3F7C} // (Note that old MT-32 ROMs actually have 86 entries for rhythmTemp) }; @@ -138,6 +176,8 @@ protected: synth.renderedSampleCount += count; } + void updateDisplayState(); + public: Renderer(Synth &useSynth) : synth(useSynth) {} @@ -209,10 +249,16 @@ public: Bit32u midiEventQueueSize; Bit32u midiEventQueueSysexStorageBufferSize; + + Display *display; + bool oldMT32DisplayFeatures; + + ReportHandler2 defaultReportHandler; + ReportHandler2 *reportHandler2; }; Bit32u Synth::getLibraryVersionInt() { - return (MT32EMU_VERSION_MAJOR << 16) | (MT32EMU_VERSION_MINOR << 8) | (MT32EMU_VERSION_PATCH); + return MT32EMU_CURRENT_VERSION_INT; } const char *Synth::getLibraryVersionString() { @@ -244,13 +290,8 @@ Synth::Synth(ReportHandler *useReportHandler) : controlROMMap = NULL; controlROMFeatures = NULL; - if (useReportHandler == NULL) { - reportHandler = new ReportHandler; - isDefaultReportHandler = true; - } else { - reportHandler = useReportHandler; - isDefaultReportHandler = false; - } + reportHandler = useReportHandler != NULL ? useReportHandler : &extensions.defaultReportHandler; + extensions.reportHandler2 = &extensions.defaultReportHandler; extensions.preallocatedReverbMemory = false; for (int i = REVERB_MODE_ROOM; i <= REVERB_MODE_TAP_DELAY; i++) { @@ -289,18 +330,27 @@ Synth::Synth(ReportHandler *useReportHandler) : lastReceivedMIDIEventTimestamp = 0; memset(parts, 0, sizeof(parts)); renderedSampleCount = 0; + extensions.display = NULL; + extensions.oldMT32DisplayFeatures = false; } Synth::~Synth() { close(); // Make sure we're closed and everything is freed - if (isDefaultReportHandler) { - delete reportHandler; - } delete &mt32ram; delete &mt32default; delete &extensions; } +void Synth::setReportHandler2(ReportHandler2 *reportHandler2) { + if (reportHandler2 != NULL) { + reportHandler = reportHandler2; + extensions.reportHandler2 = reportHandler2; + } else { + reportHandler = &extensions.defaultReportHandler; + extensions.reportHandler2 = &extensions.defaultReportHandler; + } +} + void ReportHandler::showLCDMessage(const char *data) { printf("WRITE-LCD: %s\n", data); } @@ -310,26 +360,38 @@ void ReportHandler::printDebug(const char *fmt, va_list list) { printf("\n"); } -void Synth::newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]) { - const char *soundGroupName; +void Synth::rhythmNotePlayed() const { + extensions.display->rhythmNotePlayed(); +} + +void Synth::voicePartStateChanged(Bit8u partNum, bool partActivated) const { + extensions.display->voicePartStateChanged(partNum, partActivated); +} + +void Synth::newTimbreSet(Bit8u partNum) const { + const Part *part = getPart(partNum); + reportHandler->onProgramChanged(partNum, getSoundGroupName(part), part->getCurrentInstr()); +} + +const char *Synth::getSoundGroupName(const Part *part) const { + const PatchParam &patch = part->getPatchTemp()->patch; + return getSoundGroupName(patch.timbreGroup, patch.timbreNum); +} + +const char *Synth::getSoundGroupName(Bit8u timbreGroup, Bit8u timbreNumber) const { switch (timbreGroup) { case 1: timbreNumber += 64; // Fall-through case 0: - soundGroupName = soundGroupNames[soundGroupIx[timbreNumber]]; - break; + return soundGroupNames[soundGroupIx[timbreNumber]]; case 2: - soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 2]; - break; + return soundGroupNames[controlROMMap->soundGroupsCount - 2]; case 3: - soundGroupName = soundGroupNames[controlROMMap->soundGroupsCount - 1]; - break; + return soundGroupNames[controlROMMap->soundGroupsCount - 1]; default: - soundGroupName = NULL; - break; + return NULL; } - reportHandler->onProgramChanged(partNum, soundGroupName, patchName); } #define MT32EMU_PRINT_DEBUG \ @@ -450,6 +512,16 @@ float Synth::getReverbOutputGain() const { return reverbOutputGain; } +void Synth::setPartVolumeOverride(Bit8u partNumber, Bit8u volumeOverride) { + if (opened && partNumber < 9) { + parts[partNumber]->setVolumeOverride(volumeOverride); + } +} + +Bit8u Synth::getPartVolumeOverride(Bit8u partNumber) const { + return (!opened || partNumber > 8) ? 255 : parts[partNumber]->getVolumeOverride(); +} + void Synth::setReversedStereoEnabled(bool enabled) { reversedStereoEnabled = enabled; } @@ -542,7 +614,7 @@ bool Synth::loadPCMROM(const ROMImage &pcmROMImage) { int order[16] = {0, 9, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 8}; Bit16s log = 0; - for (int u = 0; u < 15; u++) { + for (int u = 0; u < 16; u++) { int bit; if (order[u] < 8) { bit = (s >> (7 - order[u])) & 0x1; @@ -717,6 +789,16 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B return false; } + if (controlROMMap->timbreRCount == 30) { + // We must initialise all 64 rhythm timbres to avoid undefined behaviour. + // SEMI-CONFIRMED: Old-gen MT-32 units likely map timbres 30..59 to 0..29. + // Attempts to play rhythm timbres 60..63 exhibit undefined behaviour. + // We want to emulate the wrap around, so merely copy the entire set of standard + // timbres once more. The last 4 dangerous timbres are zeroed out. + memcpy(&mt32ram.timbres[222], &mt32ram.timbres[192], sizeof(*mt32ram.timbres) * 30); + memset(&mt32ram.timbres[252], 0, sizeof(*mt32ram.timbres) * 4); + } + #if MT32EMU_MONITOR_INIT printDebug("Initialising Timbre Bank M"); #endif @@ -838,6 +920,9 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B return false; } + extensions.display = new Display(*this); + extensions.oldMT32DisplayFeatures = controlROMFeatures->oldMT32DisplayFeatures; + opened = true; activated = false; @@ -850,6 +935,9 @@ bool Synth::open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, B void Synth::dispose() { opened = false; + delete extensions.display; + extensions.display = NULL; + delete midiQueue; midiQueue = NULL; @@ -1064,7 +1152,7 @@ void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) { if (velocity == 0) { // MIDI defines note-on with velocity 0 as being the same as note-off with velocity 40 parts[part]->noteOff(note); - } else { + } else if (parts[part]->getVolumeOverride() > 0) { parts[part]->noteOn(note, velocity); } break; @@ -1130,16 +1218,21 @@ void Synth::playMsgOnPart(Bit8u part, Bit8u code, Bit8u note, Bit8u velocity) { #endif return; } - + extensions.display->midiMessagePlayed(); break; case 0xC: // Program change //printDebug("Program change %01x", note); parts[part]->setProgram(note); + if (part < 8) { + extensions.display->midiMessagePlayed(); + extensions.display->programChanged(part); + } break; case 0xE: // Pitch bender bend = (velocity << 7) | (note); //printDebug("Pitch bender %02x", bend); parts[part]->setBend(bend); + extensions.display->midiMessagePlayed(); break; default: #if MT32EMU_MONITOR_MIDI > 0 @@ -1197,12 +1290,19 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys printDebug("playSysexWithoutHeader: Message is not intended for this device ID (provided: %02x, expected: 0x10 or channel)", int(device)); return; } - // This is checked early in the real devices (before any sysex length checks or further processing) - // FIXME: Response to SYSEX_CMD_DAT reset with partials active (and in general) is untested. - if ((command == SYSEX_CMD_DT1 || command == SYSEX_CMD_DAT) && sysex[0] == 0x7F) { - reset(); + + // All models process the checksum before anything else and ignore messages lacking the checksum, or containing the checksum only. + if (len < 2) { + printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); return; } + Bit8u checksum = calcSysexChecksum(sysex, len - 1); + if (checksum != sysex[len - 1]) { + printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum); + if (opened) extensions.display->checksumErrorOccurred(); + return; + } + len -= 1; // Exclude checksum if (command == SYSEX_CMD_EOD) { #if MT32EMU_MONITOR_SYSEX > 0 @@ -1210,16 +1310,6 @@ void Synth::playSysexWithoutHeader(Bit8u device, Bit8u command, const Bit8u *sys #endif return; } - if (len < 4) { - printDebug("playSysexWithoutHeader: Message is too short (%d bytes)!", len); - return; - } - Bit8u checksum = calcSysexChecksum(sysex, len - 1); - if (checksum != sysex[len - 1]) { - printDebug("playSysexWithoutHeader: Message checksum is incorrect (provided: %02x, expected: %02x)!", sysex[len - 1], checksum); - return; - } - len -= 1; // Exclude checksum switch (command) { case SYSEX_CMD_WSD: #if MT32EMU_MONITOR_SYSEX > 0 @@ -1259,12 +1349,34 @@ void Synth::readSysex(Bit8u /*device*/, const Bit8u * /*sysex*/, Bit32u /*len*/) } void Synth::writeSysex(Bit8u device, const Bit8u *sysex, Bit32u len) { - if (!opened) return; + if (!opened || len < 1) return; + + // This is checked early in the real devices (before any sysex length checks or further processing) + if (sysex[0] == 0x7F) { + if (!isDisplayOldMT32Compatible()) extensions.display->midiMessagePlayed(); + reset(); + return; + } + + extensions.display->midiMessagePlayed(); reportHandler->onMIDIMessagePlayed(); + + if (len < 3) { + // A short message of just 1 or 2 bytes may be written to the display area yet it may cause a user-visible effect, + // similarly to the reset area. + if (sysex[0] == 0x20) { + extensions.display->displayControlMessageReceived(sysex, len); + return; + } + printDebug("writeSysex: Message is too short (%d bytes)!", len); + return; + } + Bit32u addr = (sysex[0] << 16) | (sysex[1] << 8) | (sysex[2]); addr = MT32EMU_MEMADDR(addr); sysex += 3; len -= 3; + //printDebug("Sysex addr: 0x%06x", MT32EMU_SYSEXMEMADDR(addr)); // NOTE: Please keep both lower and upper bounds in each check, for ease of reading @@ -1345,6 +1457,7 @@ void Synth::writeSysexGlobal(Bit32u addr, const Bit8u *sysex, Bit32u len) { if (region == NULL) { printDebug("Sysex write to unrecognised address %06x, len %d", MT32EMU_SYSEXMEMADDR(addr), len); + // FIXME: Real devices may respond differently to a long SysEx that covers adjacent regions. break; } writeMemoryRegion(region, addr, region->getClampedLen(addr, len), sysex); @@ -1663,7 +1776,10 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le } break; case MR_Display: - char buf[SYSEX_BUFFER_SIZE]; + if (len > Display::LCD_TEXT_SIZE) len = Display::LCD_TEXT_SIZE; + if (!extensions.display->customDisplayMessageReceived(data, off, len)) break; + // Holds zero-terminated string of the maximum length. + char buf[Display::LCD_TEXT_SIZE + 1]; memcpy(&buf, &data[0], len); buf[len] = 0; #if MT32EMU_MONITOR_SYSEX > 0 @@ -1674,6 +1790,8 @@ void Synth::writeMemoryRegion(const MemoryRegion *region, Bit32u addr, Bit32u le case MR_Reset: reset(); break; + default: + break; } } @@ -1767,6 +1885,10 @@ void Synth::refreshSystemChanAssign(Bit8u firstPart, Bit8u lastPart) { } void Synth::refreshSystemMasterVol() { + // Note, this should only occur when the user turns the volume knob. When the master volume is set via a SysEx, display + // doesn't actually update on all real devices. However, we anyway update the display, as we don't foresee a dedicated + // API for setting the master volume yet it's rather dubious that one really needs this quirk to be fairly emulated. + if (opened) extensions.display->masterVolumeChanged(); #if MT32EMU_MONITOR_SYSEX > 0 printDebug(" Master volume: %d", mt32ram.system.masterVol); #endif @@ -1816,6 +1938,32 @@ Bit32s Synth::getMasterTunePitchDelta() const { return extensions.masterTunePitchDelta; } +bool Synth::getDisplayState(char *targetBuffer, bool narrowLCD) const { + if (!opened) { + memset(targetBuffer, ' ', Display::LCD_TEXT_SIZE); + targetBuffer[Display::LCD_TEXT_SIZE] = 0; + return false; + } + return extensions.display->getDisplayState(targetBuffer, narrowLCD); +} + +void Synth::setMainDisplayMode() { + if (opened) extensions.display->setMainDisplayMode(); +} + + +void Synth::setDisplayCompatibility(bool oldMT32CompatibilityEnabled) { + extensions.oldMT32DisplayFeatures = oldMT32CompatibilityEnabled; +} + +bool Synth::isDisplayOldMT32Compatible() const { + return extensions.oldMT32DisplayFeatures; +} + +bool Synth::isDefaultDisplayOldMT32Compatible() const { + return opened && controlROMFeatures->oldMT32DisplayFeatures; +} + /** Defines an interface of a class that maintains storage of variable-sized data of SysEx messages. */ class MidiEventQueue::SysexDataStorage { public: @@ -1994,6 +2142,15 @@ Bit32u Synth::getStereoOutputSampleRate() const { return (analog == NULL) ? SAMPLE_RATE : analog->getOutputSampleRate(); } +void Renderer::updateDisplayState() { + bool midiMessageLEDState; + bool midiMessageLEDStateUpdated; + bool lcdUpdated; + synth.extensions.display->checkDisplayStateUpdated(midiMessageLEDState, midiMessageLEDStateUpdated, lcdUpdated); + if (midiMessageLEDStateUpdated) synth.extensions.reportHandler2->onMidiMessageLEDStateUpdated(midiMessageLEDState); + if (lcdUpdated) synth.extensions.reportHandler2->onLCDStateUpdated(); +} + template void RendererImpl::doRender(Sample *stereoStream, Bit32u len) { if (!isActivated()) { @@ -2002,6 +2159,7 @@ void RendererImpl::doRender(Sample *stereoStream, Bit32u len) { printDebug("RendererImpl: Invalid call to Analog::process()!\n"); } Synth::muteSampleBuffer(stereoStream, len << 1); + updateDisplayState(); return; } @@ -2358,6 +2516,7 @@ void RendererImpl::produceStreams(const DACOutputStreams &stream getPartialManager().clearAlreadyOutputed(); incRenderedSampleCount(len); + updateDisplayState(); } void Synth::printPartialUsage(Bit32u sampleOffset) { @@ -2468,6 +2627,26 @@ const char *Synth::getPatchName(Bit8u partNumber) const { return (!opened || partNumber > 8) ? NULL : parts[partNumber]->getCurrentInstr(); } +bool Synth::getSoundGroupName(char *soundGroupName, Bit8u timbreGroup, Bit8u timbreNumber) const { + if (!opened || 63 < timbreNumber) return false; + const char *foundGroupName = getSoundGroupName(timbreGroup, timbreNumber); + if (foundGroupName == NULL) return false; + memcpy(soundGroupName, foundGroupName, 7); + soundGroupName[7] = 0; + return true; +} + +bool Synth::getSoundName(char *soundName, Bit8u timbreGroup, Bit8u timbreNumber) const { + if (!opened || 3 < timbreGroup) return false; + Bit8u timbresInGroup = 3 == timbreGroup ? controlROMMap->timbreRCount : 64; + if (timbresInGroup <= timbreNumber) return false; + TimbreParam::CommonParam &timbreCommon = mt32ram.timbres[timbreGroup * 64 + timbreNumber].timbre.common; + if (timbreCommon.partialMute == 0) return false; + memcpy(soundName, timbreCommon.name, sizeof timbreCommon.name); + soundName[sizeof timbreCommon.name] = 0; + return true; +} + const Part *Synth::getPart(Bit8u partNum) const { if (partNum > 8) { return NULL; diff --git a/src/sound/munt/Synth.h b/src/sound/munt/Synth.h index 65f2656e6..0f88eb9f0 100644 --- a/src/sound/munt/Synth.h +++ b/src/sound/munt/Synth.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -69,6 +69,9 @@ const Bit8u SYSEX_CMD_EOD = 0x45; // End of data const Bit8u SYSEX_CMD_ERR = 0x4E; // Communications error const Bit8u SYSEX_CMD_RJC = 0x4F; // Rejection +// This value isn't quite correct: the new-gen MT-32 control ROMs (ver. 2.XX) are twice as big. +// Nevertheless, this is still relevant for library internal usage because the higher half +// of those ROMs only contains the demo songs in all cases. const Bit32u CONTROL_ROM_SIZE = 64 * 1024; // Set of multiplexed output streams appeared at the DAC entrance. @@ -113,8 +116,21 @@ public: virtual void onProgramChanged(Bit8u /* partNum */, const char * /* soundGroupName */, const char * /* patchName */) {} }; +// Extends ReportHandler, so that the client may supply callbacks for reporting signals about updated display state. +class MT32EMU_EXPORT_V(2.6) ReportHandler2 : public ReportHandler { +public: + virtual ~ReportHandler2() {} + + // Invoked to signal about a change of the emulated LCD state. Use method Synth::getDisplayState to retrieve the actual data. + // This callback will not be invoked on further changes, until the client retrieves the LCD state. + virtual void onLCDStateUpdated() {} + // Invoked when the emulated MIDI MESSAGE LED changes state. The ledState parameter represents whether the LED is ON. + virtual void onMidiMessageLEDStateUpdated(bool /* ledState */) {} +}; + class Synth { friend class DefaultMidiStreamParser; +friend class Display; friend class MemoryRegion; friend class Part; friend class Partial; @@ -177,7 +193,7 @@ private: bool opened; bool activated; - bool isDefaultReportHandler; + bool isDefaultReportHandler; // No longer used, retained for binary compatibility only. ReportHandler *reportHandler; PartialManager *partialManager; @@ -227,7 +243,11 @@ private: void printPartialUsage(Bit32u sampleOffset = 0); - void newTimbreSet(Bit8u partNum, Bit8u timbreGroup, Bit8u timbreNumber, const char patchName[]); + void rhythmNotePlayed() const; + void voicePartStateChanged(Bit8u partNum, bool activated) const; + void newTimbreSet(Bit8u partNum) const; + const char *getSoundGroupName(const Part *part) const; + const char *getSoundGroupName(Bit8u timbreGroup, Bit8u timbreNumber) const; void printDebug(const char *fmt, ...); // partNum should be 0..7 for Part 1..8, or 8 for Rhythm @@ -290,9 +310,13 @@ public: MT32EMU_EXPORT explicit Synth(ReportHandler *useReportHandler = NULL); MT32EMU_EXPORT ~Synth(); + // Sets an implementation of ReportHandler2 interface for reporting various errors, information and debug messages. + // If the argument is NULL, the default implementation is installed as a fallback. + MT32EMU_EXPORT_V(2.6) void setReportHandler2(ReportHandler2 *reportHandler2); + // Used to initialise the MT-32. Must be called before any other function. - // Returns true if initialization was sucessful, otherwise returns false. - // controlROMImage and pcmROMImage represent Control and PCM ROM images for use by synth. + // Returns true if initialization was successful, otherwise returns false. + // controlROMImage and pcmROMImage represent full Control and PCM ROM images for use by synth. // usePartialCount sets the maximum number of partials playing simultaneously for this session (optional). // analogOutputMode sets the mode for emulation of analogue circuitry of the hardware units (optional). MT32EMU_EXPORT bool open(const ROMImage &controlROMImage, const ROMImage &pcmROMImage, Bit32u usePartialCount = DEFAULT_MAX_PARTIALS, AnalogOutputMode analogOutputMode = AnalogOutputMode_COARSE); @@ -391,7 +415,7 @@ public: MT32EMU_EXPORT bool isMT32ReverbCompatibilityMode() const; // Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. MT32EMU_EXPORT bool isDefaultReverbMT32Compatible() const; - // If enabled, reverb buffers for all modes are keept around allocated all the time to avoid memory + // If enabled, reverb buffers for all modes are kept around allocated all the time to avoid memory // allocating/freeing in the rendering thread, which may be required for realtime operation. // Otherwise, reverb buffers that are not in use are deleted to save memory (the default behaviour). MT32EMU_EXPORT void preallocateReverbMemory(bool enabled); @@ -423,6 +447,22 @@ public: // Returns current output gain factor for reverb wet output channels. MT32EMU_EXPORT float getReverbOutputGain() const; + // Sets (or removes) an override for the current volume (output level) on a specific part. + // When the part volume is overridden, the MIDI controller Volume (7) on the MIDI channel this part is assigned to + // has no effect on the output level of this part. Similarly, the output level value set on this part via a SysEx that + // modifies the Patch temp structure is disregarded. + // To enable the override mode, argument volumeOverride should be in range 0..100, setting a value outside this range + // disables the previously set override, if any. + // Note: Setting volumeOverride to 0 mutes the part completely, meaning no sound is generated at all. + // This is unlike the behaviour of real devices - setting 0 volume on a part may leave it still producing + // sound at a very low level. + // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + MT32EMU_EXPORT_V(2.6) void setPartVolumeOverride(Bit8u partNumber, Bit8u volumeOverride); + // Returns the overridden volume previously set on a specific part; a value outside the range 0..100 means no override + // is currently in effect. + // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + MT32EMU_EXPORT_V(2.6) Bit8u getPartVolumeOverride(Bit8u partNumber) const; + // Swaps left and right output channels. MT32EMU_EXPORT void setReversedStereoEnabled(bool enabled); // Returns whether left and right output channels are swapped. @@ -529,10 +569,53 @@ public: // Returns name of the patch set on the specified part. // Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + // The returned value is a null-terminated string which is guaranteed to remain valid until the next call to one of render methods. MT32EMU_EXPORT const char *getPatchName(Bit8u partNumber) const; + // Retrieves the name of the sound group the timbre identified by arguments timbreGroup and timbreNumber is associated with. + // Values 0-3 of timbreGroup correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + // For all but the RHYTHM timbre bank, allowed values of timbreNumber are in range 0-63. The number of timbres + // contained in the RHYTHM bank depends on the used control ROM version. + // The argument soundGroupName must point to an array of at least 8 characters. The result is a null-terminated string. + // Returns whether the specified timbre has been found and the result written in soundGroupName. + MT32EMU_EXPORT_V(2.7) bool getSoundGroupName(char *soundGroupName, Bit8u timbreGroup, Bit8u timbreNumber) const; + // Retrieves the name of the timbre identified by arguments timbreGroup and timbreNumber. + // Values 0-3 of timbreGroup correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + // For all but the RHYTHM timbre bank, allowed values of timbreNumber are in range 0-63. The number of timbres + // contained in the RHYTHM bank depends on the used control ROM version. + // The argument soundName must point to an array of at least 11 characters. The result is a null-terminated string. + // Returns whether the specified timbre has been found and the result written in soundName. + MT32EMU_EXPORT_V(2.7) bool getSoundName(char *soundName, Bit8u timbreGroup, Bit8u timbreNumber) const; + // Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). MT32EMU_EXPORT void readMemory(Bit32u addr, Bit32u len, Bit8u *data); + + // Retrieves the current state of the emulated MT-32 display facilities. + // Typically, the state is updated during the rendering. When that happens, a related callback from ReportHandler2 is invoked. + // However, there might be no need to invoke this method after each update, e.g. when the render buffer is just a few milliseconds + // long. + // The argument targetBuffer must point to an array of at least 21 characters. The result is a null-terminated string. + // The optional argument narrowLCD enables a condensed representation of the displayed information in some cases. This is mainly + // intended to route the result to a hardware LCD that is only 16 characters wide. Automatic scrolling of longer strings + // is not supported. + // Returns whether the MIDI MESSAGE LED is ON and fills the targetBuffer parameter. + MT32EMU_EXPORT_V(2.6) bool getDisplayState(char *targetBuffer, bool narrowLCD = false) const; + + // Resets the emulated LCD to the main mode (Master Volume). This has the same effect as pressing the Master Volume button + // while the display shows some other message. Useful for the new-gen devices as those require a special Display Reset SysEx + // to return to the main mode e.g. from showing a custom display message or a checksum error. + MT32EMU_EXPORT_V(2.6) void setMainDisplayMode(); + + // Permits to select an arbitrary display emulation model that does not necessarily match the actual behaviour implemented + // in the control ROM version being used. + // Invoking this method with the argument set to true forces emulation of the old-gen MT-32 display features. + // Otherwise, emulation of the new-gen devices is enforced (these include CM-32L and LAPC-I as if these were connected to an LCD). + MT32EMU_EXPORT_V(2.6) void setDisplayCompatibility(bool oldMT32CompatibilityEnabled); + // Returns whether the currently configured features of the emulated display are compatible with the old-gen MT-32 devices. + MT32EMU_EXPORT_V(2.6) bool isDisplayOldMT32Compatible() const; + // Returns whether the emulated display features configured by default depending on the actual control ROM version + // are compatible with the old-gen MT-32 devices. + MT32EMU_EXPORT_V(2.6) bool isDefaultDisplayOldMT32Compatible() const; }; // class Synth } // namespace MT32Emu diff --git a/src/sound/munt/TVA.cpp b/src/sound/munt/TVA.cpp index a49ad0193..e3f76181f 100644 --- a/src/sound/munt/TVA.cpp +++ b/src/sound/munt/TVA.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -99,7 +99,7 @@ static int calcVeloAmpSubtraction(Bit8u veloSensitivity, unsigned int velocity) return absVelocityMult - (velocityMult >> 8); // PORTABILITY NOTE: Assumes arithmetic shift } -static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, const MemParams::PatchTemp *patchTemp, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression, bool hasRingModQuirk) { +static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemParams::System *system, const TimbreParam::PartialParam *partialParam, Bit8u partVolume, const MemParams::RhythmTemp *rhythmTemp, int biasAmpSubtraction, int veloAmpSubtraction, Bit8u expression, bool hasRingModQuirk) { int amp = 155; if (!(hasRingModQuirk ? partial->isRingModulatingNoMix() : partial->isRingModulatingSlave())) { @@ -107,7 +107,7 @@ static int calcBasicAmp(const Tables *tables, const Partial *partial, const MemP if (amp < 0) { return 0; } - amp -= tables->levelToAmpSubtraction[patchTemp->outputLevel]; + amp -= tables->levelToAmpSubtraction[partVolume]; if (amp < 0) { return 0; } @@ -154,7 +154,6 @@ static int calcKeyTimeSubtraction(Bit8u envTimeKeyfollow, int key) { void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartialParam, const MemParams::RhythmTemp *newRhythmTemp) { part = newPart; partialParam = newPartialParam; - patchTemp = newPart->getPatchTemp(); rhythmTemp = newRhythmTemp; playing = true; @@ -169,7 +168,7 @@ void TVA::reset(const Part *newPart, const TimbreParam::PartialParam *newPartial biasAmpSubtraction = calcBiasAmpSubtractions(partialParam, key); veloAmpSubtraction = calcVeloAmpSubtraction(partialParam->tva.veloSensitivity, velocity); - int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, part->getVolume(), newRhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); int newPhase; if (partialParam->tva.envTime[0] == 0) { // Initially go to the TVA_PHASE_ATTACK target amp, and spend the next phase going from there to the TVA_PHASE_2 target amp @@ -221,7 +220,7 @@ void TVA::recalcSustain() { } // We're sustaining. Recalculate all the values const Tables *tables = &Tables::getInstance(); - int newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); + int newTarget = calcBasicAmp(tables, partial, system, partialParam, part->getVolume(), rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); newTarget += partialParam->tva.envLevel[3]; // Although we're in TVA_PHASE_SUSTAIN at this point, we cannot be sure that there is no active ramp at the moment. @@ -271,10 +270,10 @@ void TVA::nextPhase() { } bool allLevelsZeroFromNowOn = false; - if (!partial->getSynth()->controlROMFeatures->quirkTVAZeroEnvLevels && partialParam->tva.envLevel[3] == 0) { + if (partialParam->tva.envLevel[3] == 0) { if (newPhase == TVA_PHASE_4) { allLevelsZeroFromNowOn = true; - } else if (partialParam->tva.envLevel[2] == 0) { + } else if (!partial->getSynth()->controlROMFeatures->quirkTVAZeroEnvLevels && partialParam->tva.envLevel[2] == 0) { if (newPhase == TVA_PHASE_3) { allLevelsZeroFromNowOn = true; } else if (partialParam->tva.envLevel[1] == 0) { @@ -294,7 +293,7 @@ void TVA::nextPhase() { int envPointIndex = phase; if (!allLevelsZeroFromNowOn) { - newTarget = calcBasicAmp(tables, partial, system, partialParam, patchTemp, rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); + newTarget = calcBasicAmp(tables, partial, system, partialParam, part->getVolume(), rhythmTemp, biasAmpSubtraction, veloAmpSubtraction, part->getExpression(), partial->getSynth()->controlROMFeatures->quirkRingModulationNoMix); if (newPhase == TVA_PHASE_SUSTAIN || newPhase == TVA_PHASE_RELEASE) { if (partialParam->tva.envLevel[3] == 0) { diff --git a/src/sound/munt/TVA.h b/src/sound/munt/TVA.h index de6e61017..415909be8 100644 --- a/src/sound/munt/TVA.h +++ b/src/sound/munt/TVA.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -67,7 +67,6 @@ private: const Part *part; const TimbreParam::PartialParam *partialParam; - const MemParams::PatchTemp *patchTemp; const MemParams::RhythmTemp *rhythmTemp; bool playing; diff --git a/src/sound/munt/TVF.cpp b/src/sound/munt/TVF.cpp index 3d5f26049..47ce0a936 100644 --- a/src/sound/munt/TVF.cpp +++ b/src/sound/munt/TVF.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -206,6 +206,8 @@ void TVF::nextPhase() { } startRamp((levelMult * partialParam->tvf.envLevel[3]) >> 8, 0, newPhase); return; + default: + break; } int envPointIndex = phase; diff --git a/src/sound/munt/TVF.h b/src/sound/munt/TVF.h index 149b1d09b..1b766e8ed 100644 --- a/src/sound/munt/TVF.h +++ b/src/sound/munt/TVF.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/TVP.cpp b/src/sound/munt/TVP.cpp index 3d5f492fd..9921f3a4d 100644 --- a/src/sound/munt/TVP.cpp +++ b/src/sound/munt/TVP.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -54,13 +54,32 @@ static Bit16u keyToPitchTable[] = { // We want to do processing 4000 times per second. FIXME: This is pretty arbitrary. static const int NOMINAL_PROCESS_TIMER_PERIOD_SAMPLES = SAMPLE_RATE / 4000; -// The timer runs at 500kHz. This is how much to increment it after 8 samples passes. -// We multiply by 8 to get rid of the fraction and deal with just integers. -static const int PROCESS_TIMER_INCREMENT_x8 = 8 * 500000 / SAMPLE_RATE; +// In all hardware units we emulate, the main clock frequency of the MCU is 12MHz. +// However, the MCU used in the 3rd-gen sound modules (like CM-500 and LAPC-N) +// is significantly faster. Importantly, the software timer also works faster, +// yet this fact has been seemingly missed. To be more specific, the software timer +// ticks each 8 "state times", and 1 state time equals to 3 clock periods +// for 8095 and 8098 but 2 clock periods for 80C198. That is, on MT-32 and CM-32L, +// the software timer tick rate is 12,000,000 / 3 / 8 = 500kHz, but on the 3rd-gen +// devices it's 12,000,000 / 2 / 8 = 750kHz instead. + +// For 1st- and 2nd-gen devices, the timer ticks at 500kHz. This is how much to increment +// timeElapsed once 16 samples passes. We multiply by 16 to get rid of the fraction +// and deal with just integers. +static const int PROCESS_TIMER_TICKS_PER_SAMPLE_X16_1N2_GEN = (500000 << 4) / SAMPLE_RATE; +// For 3rd-gen devices, the timer ticks at 750kHz. This is how much to increment +// timeElapsed once 16 samples passes. We multiply by 16 to get rid of the fraction +// and deal with just integers. +static const int PROCESS_TIMER_TICKS_PER_SAMPLE_X16_3_GEN = (750000 << 4) / SAMPLE_RATE; TVP::TVP(const Partial *usePartial) : - partial(usePartial), system(&usePartial->getSynth()->mt32ram.system) { -} + partial(usePartial), + system(&usePartial->getSynth()->mt32ram.system), + processTimerTicksPerSampleX16( + partial->getSynth()->controlROMFeatures->quirkFastPitchChanges + ? PROCESS_TIMER_TICKS_PER_SAMPLE_X16_3_GEN + : PROCESS_TIMER_TICKS_PER_SAMPLE_X16_1N2_GEN) +{} static Bit16s keyToPitch(unsigned int key) { // We're using a table to do: return round_to_nearest_or_even((key - 60) * (4096.0 / 12.0)) @@ -270,7 +289,7 @@ void TVP::setupPitchChange(int targetPitchOffset, Bit8u changeDuration) { pitchOffsetDelta = -pitchOffsetDelta; } // We want to maximise the number of bits of the Bit16s "pitchOffsetChangePerBigTick" we use in order to get the best possible precision later - Bit32u absPitchOffsetDelta = pitchOffsetDelta << 16; + Bit32u absPitchOffsetDelta = (pitchOffsetDelta & 0xFFFF) << 16; Bit8u normalisationShifts = normalise(absPitchOffsetDelta); // FIXME: Double-check: normalisationShifts is usually between 0 and 15 here, unless the delta is 0, in which case it's 31 absPitchOffsetDelta = absPitchOffsetDelta >> 1; // Make room for the sign bit @@ -301,7 +320,7 @@ void TVP::startDecay() { Bit16u TVP::nextPitch() { // We emulate MCU software timer using these counter and processTimerIncrement variables. - // The value of nominalProcessTimerPeriod approximates the period in samples + // The value of NOMINAL_PROCESS_TIMER_PERIOD_SAMPLES approximates the period in samples // between subsequent firings of the timer that normally occur. // However, accurate emulation is quite complicated because the timer is not guaranteed to fire in time. // This makes pitch variations on real unit non-deterministic and dependent on various factors. @@ -309,7 +328,7 @@ Bit16u TVP::nextPitch() { timeElapsed = (timeElapsed + processTimerIncrement) & 0x00FFFFFF; // This roughly emulates pitch deviations observed on real units when playing a single partial that uses TVP/LFO. counter = NOMINAL_PROCESS_TIMER_PERIOD_SAMPLES + (rand() & 3); - processTimerIncrement = (PROCESS_TIMER_INCREMENT_x8 * counter) >> 3; + processTimerIncrement = (processTimerTicksPerSampleX16 * counter) >> 4; process(); } counter--; @@ -337,13 +356,16 @@ void TVP::process() { return; } // FIXME: Write explanation for this stuff + // NOTE: Value of shifts may happily exceed the maximum of 31 specified for the 8095 MCU. + // We assume the device performs a shift with the rightmost 5 bits of the counter regardless of argument size, + // since shift instructions of any size have the same maximum. int rightShifts = shifts; if (rightShifts > 13) { rightShifts -= 13; - negativeBigTicksRemaining = negativeBigTicksRemaining >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift + negativeBigTicksRemaining = negativeBigTicksRemaining >> (rightShifts & 0x1F); // PORTABILITY NOTE: Assumes arithmetic shift rightShifts = 13; } - int newResult = (negativeBigTicksRemaining * pitchOffsetChangePerBigTick) >> rightShifts; // PORTABILITY NOTE: Assumes arithmetic shift + int newResult = (negativeBigTicksRemaining * pitchOffsetChangePerBigTick) >> (rightShifts & 0x1F); // PORTABILITY NOTE: Assumes arithmetic shift newResult += targetPitchOffsetWithoutLFO + lfoPitchOffset; currentPitchOffset = newResult; updatePitch(); diff --git a/src/sound/munt/TVP.h b/src/sound/munt/TVP.h index c3dc314b4..61bd2033e 100644 --- a/src/sound/munt/TVP.h +++ b/src/sound/munt/TVP.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -36,6 +36,7 @@ private: const TimbreParam::PartialParam *partialParam; const MemParams::PatchTemp *patchTemp; + const int processTimerTicksPerSampleX16; int processTimerIncrement; int counter; Bit32u timeElapsed; diff --git a/src/sound/munt/Tables.cpp b/src/sound/munt/Tables.cpp index 7fee467e8..dff042d20 100644 --- a/src/sound/munt/Tables.cpp +++ b/src/sound/munt/Tables.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/Tables.h b/src/sound/munt/Tables.h index 790ee17b9..2f9053215 100644 --- a/src/sound/munt/Tables.h +++ b/src/sound/munt/Tables.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/Types.h b/src/sound/munt/Types.h index 17c33e568..12e454750 100644 --- a/src/sound/munt/Types.h +++ b/src/sound/munt/Types.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/VersionTagging.cpp b/src/sound/munt/VersionTagging.cpp new file mode 100644 index 000000000..0a3388f3b --- /dev/null +++ b/src/sound/munt/VersionTagging.cpp @@ -0,0 +1,32 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#include "globals.h" + +extern "C" { +// Here's a list of all tagged minor library versions through global (potentially versioned) symbols. +// An application that's been linked with an older library version will be able to find a matching tag, +// while for an application linked with a newer library version there will be no match. + +MT32EMU_EXPORT_V(2.5) extern const volatile char mt32emu_2_5 = 0; +MT32EMU_EXPORT_V(2.6) extern const volatile char mt32emu_2_6 = 0; +MT32EMU_EXPORT_V(2.7) extern const volatile char mt32emu_2_7 = 0; + +#if MT32EMU_VERSION_MAJOR > 2 || MT32EMU_VERSION_MINOR > 7 +#error "Missing version tag definition for current library version" +#endif +} diff --git a/src/sound/munt/VersionTagging.h b/src/sound/munt/VersionTagging.h new file mode 100644 index 000000000..df211f3c0 --- /dev/null +++ b/src/sound/munt/VersionTagging.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +#ifndef MT32EMU_VERSION_TAG_H +#define MT32EMU_VERSION_TAG_H + +#include "globals.h" + +/* This is intended to implement a simple check of a shared library version in runtime. Sadly, per-symbol versioning + * is unavailable on many platforms, and even where it is, it's still not too easy to maintain for a C++ library. + * Therefore, the goal here is just to ensure that the client application quickly bails out when attempted to run + * with an older version of shared library, as well as to produce a more readable error message indicating a version mismatch + * rather than a report about some missing symbols with unreadable mangled names. + * This is an optional feature, since it adds some minor burden to both the library and client applications code, + * albeit it is ought to work on platforms that do not implement symbol versioning. + */ + +#define MT32EMU_REALLY_BUILD_VERSION_TAG(major, minor) mt32emu_ ## major ## _ ## minor +/* This macro expansion step permits resolution the actual version numbers. */ +#define MT32EMU_BUILD_VERSION_TAG(major, minor) MT32EMU_REALLY_BUILD_VERSION_TAG(major, minor) +#define MT32EMU_VERSION_TAG MT32EMU_BUILD_VERSION_TAG(MT32EMU_VERSION_MAJOR, MT32EMU_VERSION_MINOR) + +#if defined(__cplusplus) + +extern "C" { +MT32EMU_EXPORT extern const volatile char MT32EMU_VERSION_TAG; +} +// This pulls the external reference in yet prevents it from being optimised out. +static const volatile char mt32emu_version_tag = MT32EMU_VERSION_TAG; + +#else + +static void mt32emu_refer_version_tag(void) { + MT32EMU_EXPORT extern const volatile char MT32EMU_VERSION_TAG; + (void)MT32EMU_VERSION_TAG; +} + +static void (*const volatile mt32emu_refer_version_tag_ref)(void) = mt32emu_refer_version_tag; + +#endif + +#undef MT32EMU_REALLY_BUILD_VERSION_TAG +#undef MT32EMU_BUILD_VERSION_TAG +#undef MT32EMU_VERSION_TAG + +#endif diff --git a/src/sound/munt/c_interface/c_interface.cpp b/src/sound/munt/c_interface/c_interface.cpp index 48eb2824a..4c7706be8 100644 --- a/src/sound/munt/c_interface/c_interface.cpp +++ b/src/sound/munt/c_interface/c_interface.cpp @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -15,6 +15,8 @@ * along with this program. If not, see . */ +#include + #include "../globals.h" #include "../Types.h" #include "../File.h" @@ -37,11 +39,11 @@ struct SamplerateConversionState { SampleRateConverter *src; }; -static mt32emu_service_version getSynthVersionID(mt32emu_service_i) { +static mt32emu_service_version MT32EMU_C_CALL getSynthVersionID(mt32emu_service_i) { return MT32EMU_SERVICE_VERSION_CURRENT; } -static const mt32emu_service_i_v3 SERVICE_VTABLE = { +static const mt32emu_service_i_v6 SERVICE_VTABLE = { getSynthVersionID, mt32emu_get_supported_report_handler_version, mt32emu_get_supported_midi_receiver_version, @@ -118,13 +120,29 @@ static const mt32emu_service_i_v3 SERVICE_VTABLE = { mt32emu_set_nice_partial_mixing_enabled, mt32emu_is_nice_partial_mixing_enabled, mt32emu_preallocate_reverb_memory, - mt32emu_configure_midi_event_queue_sysex_storage + mt32emu_configure_midi_event_queue_sysex_storage, + mt32emu_get_machine_ids, + mt32emu_get_rom_ids, + mt32emu_identify_rom_data, + mt32emu_identify_rom_file, + mt32emu_merge_and_add_rom_data, + mt32emu_merge_and_add_rom_files, + mt32emu_add_machine_rom_file, + mt32emu_get_display_state, + mt32emu_set_main_display_mode, + mt32emu_set_display_compatibility, + mt32emu_is_display_old_mt32_compatible, + mt32emu_is_default_display_old_mt32_compatible, + mt32emu_set_part_volume_override, + mt32emu_get_part_volume_override, + mt32emu_get_sound_group_name, + mt32emu_get_sound_name }; } // namespace MT32Emu struct mt32emu_data { - ReportHandler *reportHandler; + ReportHandler2 *reportHandler; Synth *synth; const ROMImage *controlROMImage; const ROMImage *pcmROMImage; @@ -138,16 +156,19 @@ struct mt32emu_data { namespace MT32Emu { -class DelegatingReportHandlerAdapter : public ReportHandler { +class DelegatingReportHandlerAdapter : public ReportHandler2 { public: DelegatingReportHandlerAdapter(mt32emu_report_handler_i useReportHandler, void *useInstanceData) : delegate(useReportHandler), instanceData(useInstanceData) {} -protected: +private: const mt32emu_report_handler_i delegate; void * const instanceData; -private: + bool isVersionLess(mt32emu_report_handler_version versionID) { + return delegate.v0->getVersionID(delegate) < versionID; + } + void printDebug(const char *fmt, va_list list) { if (delegate.v0->printDebug == NULL) { ReportHandler::printDebug(fmt, list); @@ -258,6 +279,22 @@ private: delegate.v0->onProgramChanged(instanceData, partNum, soundGroupName, patchName); } } + + void onLCDStateUpdated() { + if (isVersionLess(MT32EMU_REPORT_HANDLER_VERSION_1) || delegate.v1->onLCDStateUpdated == NULL) { + ReportHandler2::onLCDStateUpdated(); + } else { + delegate.v1->onLCDStateUpdated(instanceData); + } + } + + void onMidiMessageLEDStateUpdated(bool ledState) { + if (isVersionLess(MT32EMU_REPORT_HANDLER_VERSION_1) || delegate.v1->onMidiMessageLEDStateUpdated == NULL) { + ReportHandler2::onMidiMessageLEDStateUpdated(ledState); + } else { + delegate.v1->onMidiMessageLEDStateUpdated(instanceData, ledState ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); + } + } }; class DelegatingMidiStreamParser : public DefaultMidiStreamParser { @@ -295,30 +332,130 @@ private: } }; -static mt32emu_return_code addROMFile(mt32emu_data *data, File *file) { - const ROMImage *image = ROMImage::makeROMImage(file); - const ROMInfo *info = image->getROMInfo(); - if (info == NULL) { - ROMImage::freeROMImage(image); +static void fillROMInfo(mt32emu_rom_info *rom_info, const ROMInfo *controlROMInfo, const ROMInfo *pcmROMInfo) { + if (controlROMInfo != NULL) { + rom_info->control_rom_id = controlROMInfo->shortName; + rom_info->control_rom_description = controlROMInfo->description; + rom_info->control_rom_sha1_digest = controlROMInfo->sha1Digest; + } else { + rom_info->control_rom_id = NULL; + rom_info->control_rom_description = NULL; + rom_info->control_rom_sha1_digest = NULL; + } + if (pcmROMInfo != NULL) { + rom_info->pcm_rom_id = pcmROMInfo->shortName; + rom_info->pcm_rom_description = pcmROMInfo->description; + rom_info->pcm_rom_sha1_digest = pcmROMInfo->sha1Digest; + } else { + rom_info->pcm_rom_id = NULL; + rom_info->pcm_rom_description = NULL; + rom_info->pcm_rom_sha1_digest = NULL; + } +} + +static const MachineConfiguration *findMachineConfiguration(const char *machine_id) { + Bit32u configurationCount; + const MachineConfiguration * const *configurations = MachineConfiguration::getAllMachineConfigurations(&configurationCount); + for (Bit32u i = 0; i < configurationCount; i++) { + if (!strcmp(configurations[i]->getMachineID(), machine_id)) return configurations[i]; + } + return NULL; +} + +static mt32emu_return_code identifyROM(mt32emu_rom_info *rom_info, File *romFile, const char *machineID) { + const ROMInfo *romInfo; + if (machineID == NULL) { + romInfo = ROMInfo::getROMInfo(romFile); + } else { + const MachineConfiguration *configuration = findMachineConfiguration(machineID); + if (configuration == NULL) { + fillROMInfo(rom_info, NULL, NULL); + return MT32EMU_RC_MACHINE_NOT_IDENTIFIED; + } + romInfo = ROMInfo::getROMInfo(romFile, configuration->getCompatibleROMInfos()); + } + if (romInfo == NULL) { + fillROMInfo(rom_info, NULL, NULL); return MT32EMU_RC_ROM_NOT_IDENTIFIED; } - if (info->type == ROMInfo::Control) { - if (data->controlROMImage != NULL) { - delete data->controlROMImage->getFile(); - ROMImage::freeROMImage(data->controlROMImage); - } - data->controlROMImage = image; - return MT32EMU_RC_ADDED_CONTROL_ROM; - } else if (info->type == ROMInfo::PCM) { - if (data->pcmROMImage != NULL) { - delete data->pcmROMImage->getFile(); - ROMImage::freeROMImage(data->pcmROMImage); - } - data->pcmROMImage = image; - return MT32EMU_RC_ADDED_PCM_ROM; + if (romInfo->type == ROMInfo::Control) fillROMInfo(rom_info, romInfo, NULL); + else if (romInfo->type == ROMInfo::PCM) fillROMInfo(rom_info, NULL, romInfo); + else fillROMInfo(rom_info, NULL, NULL); + return MT32EMU_RC_OK; +} + +static bool isROMInfoCompatible(const MachineConfiguration *machineConfiguration, const ROMInfo *romInfo) { + Bit32u romCount; + const ROMInfo * const *compatibleROMInfos = machineConfiguration->getCompatibleROMInfos(&romCount); + for (Bit32u i = 0; i < romCount; i++) { + if (romInfo == compatibleROMInfos[i]) return true; } - ROMImage::freeROMImage(image); - return MT32EMU_RC_OK; // No support for reverb ROM yet. + return false; +} + +static mt32emu_return_code replaceOrMergeROMImage(const ROMImage *&contextROMImage, const ROMImage *newROMImage, const MachineConfiguration *machineConfiguration, mt32emu_return_code addedFullROM, mt32emu_return_code addedPartialROM) { + if (contextROMImage != NULL) { + if (machineConfiguration != NULL) { + const ROMImage *mergedROMImage = ROMImage::mergeROMImages(contextROMImage, newROMImage); + if (mergedROMImage != NULL) { + if (newROMImage->isFileUserProvided()) delete newROMImage->getFile(); + ROMImage::freeROMImage(newROMImage); + if (contextROMImage->isFileUserProvided()) delete contextROMImage->getFile(); + ROMImage::freeROMImage(contextROMImage); + contextROMImage = mergedROMImage; + return addedFullROM; + } + if (newROMImage->getROMInfo() == contextROMImage->getROMInfo() + || (newROMImage->getROMInfo()->pairType != ROMInfo::Full + && isROMInfoCompatible(machineConfiguration, contextROMImage->getROMInfo()))) { + ROMImage::freeROMImage(newROMImage); + return MT32EMU_RC_OK; + } + } + if (contextROMImage->isFileUserProvided()) delete contextROMImage->getFile(); + ROMImage::freeROMImage(contextROMImage); + } + contextROMImage = newROMImage; + return newROMImage->getROMInfo()->pairType == ROMInfo::Full ? addedFullROM: addedPartialROM; +} + +static mt32emu_return_code addROMFiles(mt32emu_data *data, File *file1, File *file2 = NULL, const MachineConfiguration *machineConfiguration = NULL) { + const ROMImage *romImage; + if (machineConfiguration != NULL) { + romImage = ROMImage::makeROMImage(file1, machineConfiguration->getCompatibleROMInfos()); + } else { + romImage = file2 == NULL ? ROMImage::makeROMImage(file1, ROMInfo::getFullROMInfos()) : ROMImage::makeROMImage(file1, file2); + } + if (romImage == NULL) return MT32EMU_RC_ROMS_NOT_PAIRABLE; + const ROMInfo *info = romImage->getROMInfo(); + if (info == NULL) { + ROMImage::freeROMImage(romImage); + return MT32EMU_RC_ROM_NOT_IDENTIFIED; + } + switch (info->type) { + case ROMInfo::Control: + return replaceOrMergeROMImage(data->controlROMImage, romImage, machineConfiguration, MT32EMU_RC_ADDED_CONTROL_ROM, MT32EMU_RC_ADDED_PARTIAL_CONTROL_ROM); + case ROMInfo::PCM: + return replaceOrMergeROMImage(data->pcmROMImage, romImage, machineConfiguration, MT32EMU_RC_ADDED_PCM_ROM, MT32EMU_RC_ADDED_PARTIAL_PCM_ROM); + default: + ROMImage::freeROMImage(romImage); + return MT32EMU_RC_OK; // No support for reverb ROM yet. + } +} + +static mt32emu_return_code createFileStream(const char *filename, FileStream *&fileStream) { + mt32emu_return_code rc; + fileStream = new FileStream; + if (!fileStream->open(filename)) { + rc = MT32EMU_RC_FILE_NOT_FOUND; + } else if (fileStream->getSize() == 0) { + rc = MT32EMU_RC_FILE_NOT_LOADED; + } else { + return MT32EMU_RC_OK; + } + delete fileStream; + fileStream = NULL; + return rc; } } // namespace MT32Emu @@ -327,40 +464,92 @@ static mt32emu_return_code addROMFile(mt32emu_data *data, File *file) { extern "C" { -mt32emu_service_i mt32emu_get_service_i() { +mt32emu_service_i MT32EMU_C_CALL mt32emu_get_service_i() { mt32emu_service_i i; - i.v3 = &SERVICE_VTABLE; + i.v6 = &SERVICE_VTABLE; return i; } -mt32emu_report_handler_version mt32emu_get_supported_report_handler_version() { +mt32emu_report_handler_version MT32EMU_C_CALL mt32emu_get_supported_report_handler_version() { return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; } -mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version() { +mt32emu_midi_receiver_version MT32EMU_C_CALL mt32emu_get_supported_midi_receiver_version() { return MT32EMU_MIDI_RECEIVER_VERSION_CURRENT; } -mt32emu_bit32u mt32emu_get_library_version_int() { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_library_version_int() { return Synth::getLibraryVersionInt(); } -const char *mt32emu_get_library_version_string() { +const char * MT32EMU_C_CALL mt32emu_get_library_version_string() { return Synth::getLibraryVersionString(); } -mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode) { return Synth::getStereoOutputSampleRate(static_cast(analog_output_mode)); } -mt32emu_analog_output_mode mt32emu_get_best_analog_output_mode(const double target_samplerate) { +mt32emu_analog_output_mode MT32EMU_C_CALL mt32emu_get_best_analog_output_mode(const double target_samplerate) { return mt32emu_analog_output_mode(SampleRateConverter::getBestAnalogOutputMode(target_samplerate)); } -mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data) { +size_t MT32EMU_C_CALL mt32emu_get_machine_ids(const char **machine_ids, size_t machine_ids_size) { + Bit32u configurationCount; + const MachineConfiguration * const *configurations = MachineConfiguration::getAllMachineConfigurations(&configurationCount); + if (machine_ids != NULL) { + for (Bit32u i = 0; i < machine_ids_size; i++) { + machine_ids[i] = i < configurationCount ? configurations[i]->getMachineID() : NULL; + } + } + return configurationCount; +} + +size_t MT32EMU_C_CALL mt32emu_get_rom_ids(const char **rom_ids, size_t rom_ids_size, const char *machine_id) { + const ROMInfo * const *romInfos; + Bit32u romCount; + if (machine_id != NULL) { + const MachineConfiguration *configuration = findMachineConfiguration(machine_id); + if (configuration != NULL) { + romInfos = configuration->getCompatibleROMInfos(&romCount); + } else { + romInfos = NULL; + romCount = 0U; + } + } else { + romInfos = ROMInfo::getAllROMInfos(&romCount); + } + if (rom_ids != NULL) { + for (size_t i = 0; i < rom_ids_size; i++) { + rom_ids[i] = i < romCount ? romInfos[i]->shortName : NULL; + } + } + return romCount; +} + +mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_data(mt32emu_rom_info *rom_info, const mt32emu_bit8u *data, size_t data_size, const char *machine_id) { + ArrayFile romFile = ArrayFile(data, data_size); + return identifyROM(rom_info, &romFile, machine_id); +} + +mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_file(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id) { + FileStream *fs; + mt32emu_return_code rc = createFileStream(filename, fs); + if (fs == NULL) return rc; + rc = identifyROM(rom_info, fs, machine_id); + delete fs; + return rc; +} + +mt32emu_context MT32EMU_C_CALL mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data) { mt32emu_data *data = new mt32emu_data; - data->reportHandler = (report_handler.v0 != NULL) ? new DelegatingReportHandlerAdapter(report_handler, instance_data) : new ReportHandler; - data->synth = new Synth(data->reportHandler); + data->synth = new Synth; + if (report_handler.v0 != NULL) { + data->reportHandler = new DelegatingReportHandlerAdapter(report_handler, instance_data); + data->synth->setReportHandler2(data->reportHandler); + } else { + data->reportHandler = NULL; + } data->midiParser = new DefaultMidiStreamParser(*data->synth); data->controlROMImage = NULL; data->pcmROMImage = NULL; @@ -375,7 +564,7 @@ mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, return data; } -void mt32emu_free_context(mt32emu_context data) { +void MT32EMU_C_CALL mt32emu_free_context(mt32emu_context data) { if (data == NULL) return; delete data->srcState->src; @@ -384,12 +573,12 @@ void mt32emu_free_context(mt32emu_context data) { data->srcState = NULL; if (data->controlROMImage != NULL) { - delete data->controlROMImage->getFile(); + if (data->controlROMImage->isFileUserProvided()) delete data->controlROMImage->getFile(); ROMImage::freeROMImage(data->controlROMImage); data->controlROMImage = NULL; } if (data->pcmROMImage != NULL) { - delete data->pcmROMImage->getFile(); + if (data->pcmROMImage->isFileUserProvided()) delete data->pcmROMImage->getFile(); ROMImage::freeROMImage(data->pcmROMImage); data->pcmROMImage = NULL; } @@ -402,76 +591,86 @@ void mt32emu_free_context(mt32emu_context data) { delete data; } -mt32emu_return_code mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest) { - if (sha1_digest == NULL) return addROMFile(context, new ArrayFile(data, data_size)); - return addROMFile(context, new ArrayFile(data, data_size, *sha1_digest)); +mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest) { + if (sha1_digest == NULL) return addROMFiles(context, new ArrayFile(data, data_size)); + return addROMFiles(context, new ArrayFile(data, data_size, *sha1_digest)); } -mt32emu_return_code mt32emu_add_rom_file(mt32emu_context context, const char *filename) { - mt32emu_return_code rc = MT32EMU_RC_OK; - FileStream *fs = new FileStream; - if (fs->open(filename)) { - if (fs->getData() != NULL) { - rc = addROMFile(context, fs); - if (rc > 0) return rc; - } else { - rc = MT32EMU_RC_FILE_NOT_LOADED; - } - } else { - rc = MT32EMU_RC_FILE_NOT_FOUND; - } - delete fs; +mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_file(mt32emu_context context, const char *filename) { + FileStream *fs; + mt32emu_return_code rc = createFileStream(filename, fs); + if (fs != NULL) rc = addROMFiles(context, fs); + if (rc <= MT32EMU_RC_OK) delete fs; return rc; } -void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info) { - const ROMInfo *romInfo = context->controlROMImage == NULL ? NULL : context->controlROMImage->getROMInfo(); - if (romInfo != NULL) { - rom_info->control_rom_id = romInfo->shortName; - rom_info->control_rom_description = romInfo->description; - rom_info->control_rom_sha1_digest = romInfo->sha1Digest; - } else { - rom_info->control_rom_id = NULL; - rom_info->control_rom_description = NULL; - rom_info->control_rom_sha1_digest = NULL; - } - romInfo = context->pcmROMImage == NULL ? NULL : context->pcmROMImage->getROMInfo(); - if (romInfo != NULL) { - rom_info->pcm_rom_id = romInfo->shortName; - rom_info->pcm_rom_description = romInfo->description; - rom_info->pcm_rom_sha1_digest = romInfo->sha1Digest; - } else { - rom_info->pcm_rom_id = NULL; - rom_info->pcm_rom_description = NULL; - rom_info->pcm_rom_sha1_digest = NULL; - } +mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_data(mt32emu_context context, const mt32emu_bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const mt32emu_bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest) { + ArrayFile *file1 = part1_sha1_digest == NULL ? new ArrayFile(part1_data, part1_data_size) : new ArrayFile(part1_data, part1_data_size, *part1_sha1_digest); + ArrayFile *file2 = part2_sha1_digest == NULL ? new ArrayFile(part2_data, part2_data_size) : new ArrayFile(part2_data, part2_data_size, *part2_sha1_digest); + mt32emu_return_code rc = addROMFiles(context, file1, file2); + delete file1; + delete file2; + return rc; } -void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_files(mt32emu_context context, const char *part1_filename, const char *part2_filename) { + FileStream *fs1; + mt32emu_return_code rc = createFileStream(part1_filename, fs1); + if (fs1 != NULL) { + FileStream *fs2; + rc = createFileStream(part2_filename, fs2); + if (fs2 != NULL) { + rc = addROMFiles(context, fs1, fs2); + delete fs2; + } + delete fs1; + } + return rc; +} + +mt32emu_return_code MT32EMU_C_CALL mt32emu_add_machine_rom_file(mt32emu_context context, const char *machine_id, const char *filename) { + const MachineConfiguration *machineConfiguration = findMachineConfiguration(machine_id); + if (machineConfiguration == NULL) return MT32EMU_RC_MACHINE_NOT_IDENTIFIED; + + FileStream *fs; + mt32emu_return_code rc = createFileStream(filename, fs); + if (fs == NULL) return rc; + rc = addROMFiles(context, fs, NULL, machineConfiguration); + if (rc <= MT32EMU_RC_OK) delete fs; + return rc; +} + +void MT32EMU_C_CALL mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info) { + const ROMInfo *controlROMInfo = context->controlROMImage == NULL ? NULL : context->controlROMImage->getROMInfo(); + const ROMInfo *pcmROMInfo = context->pcmROMImage == NULL ? NULL : context->pcmROMImage->getROMInfo(); + fillROMInfo(rom_info, controlROMInfo, pcmROMInfo); +} + +void MT32EMU_C_CALL mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count) { context->partialCount = partial_count; } -void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode) { +void MT32EMU_C_CALL mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode) { context->analogOutputMode = static_cast(analog_output_mode); } -void mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate) { +void MT32EMU_C_CALL mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate) { context->srcState->outputSampleRate = SampleRateConverter::getSupportedOutputSampleRate(samplerate); } -void mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality) { +void MT32EMU_C_CALL mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality) { context->srcState->srcQuality = SamplerateConversionQuality(quality); } -void mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type) { +void MT32EMU_C_CALL mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type) { context->synth->selectRendererType(static_cast(renderer_type)); } -mt32emu_renderer_type mt32emu_get_selected_renderer_type(mt32emu_context context) { +mt32emu_renderer_type MT32EMU_C_CALL mt32emu_get_selected_renderer_type(mt32emu_context context) { return static_cast(context->synth->getSelectedRendererType()); } -mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_open_synth(mt32emu_const_context context) { if ((context->controlROMImage == NULL) || (context->pcmROMImage == NULL)) { return MT32EMU_RC_MISSING_ROMS; } @@ -484,211 +683,219 @@ mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context) { return MT32EMU_RC_OK; } -void mt32emu_close_synth(mt32emu_const_context context) { +void MT32EMU_C_CALL mt32emu_close_synth(mt32emu_const_context context) { context->synth->close(); delete context->srcState->src; context->srcState->src = NULL; } -mt32emu_boolean mt32emu_is_open(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_open(mt32emu_const_context context) { return context->synth->isOpen() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context) { if (context->srcState->src == NULL) { return context->synth->getStereoOutputSampleRate(); } return mt32emu_bit32u(0.5 + context->srcState->src->convertSynthToOutputTimestamp(SAMPLE_RATE)); } -mt32emu_bit32u mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp) { if (context->srcState->src == NULL) { return output_timestamp; } return mt32emu_bit32u(0.5 + context->srcState->src->convertOutputToSynthTimestamp(output_timestamp)); } -mt32emu_bit32u mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp) { if (context->srcState->src == NULL) { return synth_timestamp; } return mt32emu_bit32u(0.5 + context->srcState->src->convertSynthToOutputTimestamp(synth_timestamp)); } -void mt32emu_flush_midi_queue(mt32emu_const_context context) { +void MT32EMU_C_CALL mt32emu_flush_midi_queue(mt32emu_const_context context) { context->synth->flushMIDIQueue(); } -mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size) { return context->synth->setMIDIEventQueueSize(queue_size); } -void mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size) { - return context->synth->configureMIDIEventQueueSysexStorage(storage_buffer_size); +void MT32EMU_C_CALL mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size) { + context->synth->configureMIDIEventQueueSysexStorage(storage_buffer_size); } -void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data) { +void MT32EMU_C_CALL mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data) { delete context->midiParser; context->midiParser = (midi_receiver.v0 != NULL) ? new DelegatingMidiStreamParser(context, midi_receiver, instance_data) : new DefaultMidiStreamParser(*context->synth); } -mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context) { return context->synth->getInternalRenderedSampleCount(); } -void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length) { +void MT32EMU_C_CALL mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length) { context->midiParser->resetTimestamp(); context->midiParser->parseStream(stream, length); } -void mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp) { +void MT32EMU_C_CALL mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp) { context->midiParser->setTimestamp(timestamp); context->midiParser->parseStream(stream, length); } -void mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message) { +void MT32EMU_C_CALL mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message) { context->midiParser->resetTimestamp(); context->midiParser->processShortMessage(message); } -void mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp) { +void MT32EMU_C_CALL mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp) { context->midiParser->setTimestamp(timestamp); context->midiParser->processShortMessage(message); } -mt32emu_return_code mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg) { if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; return (context->synth->playMsg(msg)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; } -mt32emu_return_code mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; return (context->synth->playSysex(sysex, len)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; } -mt32emu_return_code mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp) { if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; return (context->synth->playMsg(msg, timestamp)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; } -mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp) { +mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp) { if (!context->synth->isOpen()) return MT32EMU_RC_NOT_OPENED; return (context->synth->playSysex(sysex, len, timestamp)) ? MT32EMU_RC_OK : MT32EMU_RC_QUEUE_FULL; } -void mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg) { +void MT32EMU_C_CALL mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg) { context->synth->playMsgNow(msg); } -void mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity) { +void MT32EMU_C_CALL mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity) { context->synth->playMsgOnPart(part, code, note, velocity); } -void mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { context->synth->playSysexNow(sysex, len); } -void mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len) { context->synth->writeSysex(channel, sysex, len); } -void mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled) { +void MT32EMU_C_CALL mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled) { context->synth->setReverbEnabled(reverb_enabled != MT32EMU_BOOL_FALSE); } -mt32emu_boolean mt32emu_is_reverb_enabled(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_enabled(mt32emu_const_context context) { return context->synth->isReverbEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -void mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden) { +void MT32EMU_C_CALL mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden) { context->synth->setReverbOverridden(reverb_overridden != MT32EMU_BOOL_FALSE); } -mt32emu_boolean mt32emu_is_reverb_overridden(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_overridden(mt32emu_const_context context) { return context->synth->isReverbOverridden() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -void mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode) { +void MT32EMU_C_CALL mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode) { context->synth->setReverbCompatibilityMode(mt32_compatible_mode != MT32EMU_BOOL_FALSE); } -mt32emu_boolean mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context) { return context->synth->isMT32ReverbCompatibilityMode() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -mt32emu_boolean mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context) { return context->synth->isDefaultReverbMT32Compatible() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -void mt32emu_preallocate_reverb_memory(mt32emu_const_context context, const mt32emu_boolean enabled) { - return context->synth->preallocateReverbMemory(enabled != MT32EMU_BOOL_FALSE); +void MT32EMU_C_CALL mt32emu_preallocate_reverb_memory(mt32emu_const_context context, const mt32emu_boolean enabled) { + context->synth->preallocateReverbMemory(enabled != MT32EMU_BOOL_FALSE); } -void mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode) { +void MT32EMU_C_CALL mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode) { context->synth->setDACInputMode(static_cast(mode)); } -mt32emu_dac_input_mode mt32emu_get_dac_input_mode(mt32emu_const_context context) { +mt32emu_dac_input_mode MT32EMU_C_CALL mt32emu_get_dac_input_mode(mt32emu_const_context context) { return static_cast(context->synth->getDACInputMode()); } -void mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode) { +void MT32EMU_C_CALL mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode) { context->synth->setMIDIDelayMode(static_cast(mode)); } -mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const_context context) { +mt32emu_midi_delay_mode MT32EMU_C_CALL mt32emu_get_midi_delay_mode(mt32emu_const_context context) { return static_cast(context->synth->getMIDIDelayMode()); } -void mt32emu_set_output_gain(mt32emu_const_context context, float gain) { +void MT32EMU_C_CALL mt32emu_set_output_gain(mt32emu_const_context context, float gain) { context->synth->setOutputGain(gain); } -float mt32emu_get_output_gain(mt32emu_const_context context) { +float MT32EMU_C_CALL mt32emu_get_output_gain(mt32emu_const_context context) { return context->synth->getOutputGain(); } -void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain) { +void MT32EMU_C_CALL mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain) { context->synth->setReverbOutputGain(gain); } -float mt32emu_get_reverb_output_gain(mt32emu_const_context context) { +float MT32EMU_C_CALL mt32emu_get_reverb_output_gain(mt32emu_const_context context) { return context->synth->getReverbOutputGain(); } -void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { +void MT32EMU_C_CALL mt32emu_set_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u volume_override) { + context->synth->setPartVolumeOverride(part_number, volume_override); +} + +mt32emu_bit8u MT32EMU_C_CALL mt32emu_get_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number) { + return context->synth->getPartVolumeOverride(part_number); +} + +void MT32EMU_C_CALL mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { context->synth->setReversedStereoEnabled(enabled != MT32EMU_BOOL_FALSE); } -mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context) { return context->synth->isReversedStereoEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -void mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { +void MT32EMU_C_CALL mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { context->synth->setNiceAmpRampEnabled(enabled != MT32EMU_BOOL_FALSE); } -mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context) { return context->synth->isNiceAmpRampEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -MT32EMU_EXPORT void mt32emu_set_nice_panning_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { +void MT32EMU_C_CALL mt32emu_set_nice_panning_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { context->synth->setNicePanningEnabled(enabled != MT32EMU_BOOL_FALSE); } -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_panning_enabled(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_panning_enabled(mt32emu_const_context context) { return context->synth->isNicePanningEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -MT32EMU_EXPORT void mt32emu_set_nice_partial_mixing_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { +void MT32EMU_C_CALL mt32emu_set_nice_partial_mixing_enabled(mt32emu_const_context context, const mt32emu_boolean enabled) { context->synth->setNicePartialMixingEnabled(enabled != MT32EMU_BOOL_FALSE); } -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_partial_mixing_enabled(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_partial_mixing_enabled(mt32emu_const_context context) { return context->synth->isNicePartialMixingEnabled() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len) { if (context->srcState->src != NULL) { context->srcState->src->getOutputSamples(stream, len); } else { @@ -696,7 +903,7 @@ void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream } } -void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len) { if (context->srcState->src != NULL) { context->srcState->src->getOutputSamples(stream, len); } else { @@ -704,44 +911,72 @@ void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_ } } -void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len) { context->synth->renderStreams(*reinterpret_cast *>(streams), len); } -void mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len) { +void MT32EMU_C_CALL mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len) { context->synth->renderStreams(*reinterpret_cast *>(streams), len); } -mt32emu_boolean mt32emu_has_active_partials(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_has_active_partials(mt32emu_const_context context) { return context->synth->hasActivePartials() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -mt32emu_boolean mt32emu_is_active(mt32emu_const_context context) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_active(mt32emu_const_context context) { return context->synth->isActive() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -mt32emu_bit32u mt32emu_get_partial_count(mt32emu_const_context context) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_partial_count(mt32emu_const_context context) { return context->synth->getPartialCount(); } -mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context context) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_part_states(mt32emu_const_context context) { return context->synth->getPartStates(); } -void mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states) { +void MT32EMU_C_CALL mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states) { context->synth->getPartialStates(partial_states); } -mt32emu_bit32u mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities) { +mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities) { return context->synth->getPlayingNotes(part_number, keys, velocities); } -const char *mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number) { +const char * MT32EMU_C_CALL mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number) { return context->synth->getPatchName(part_number); } -void mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data) { +mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_group_name(mt32emu_const_context context, char *sound_group_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number) { + return context->synth->getSoundGroupName(sound_group_name, timbre_group, timbre_number) ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_name(mt32emu_const_context context, char *sound_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number) { + return context->synth->getSoundName(sound_name, timbre_group, timbre_number) ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void MT32EMU_C_CALL mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data) { context->synth->readMemory(addr, len, data); } +mt32emu_boolean MT32EMU_C_CALL mt32emu_get_display_state(mt32emu_const_context context, char *target_buffer, const mt32emu_boolean narrow_lcd) { + return context->synth->getDisplayState(target_buffer, narrow_lcd != MT32EMU_BOOL_FALSE) ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +void MT32EMU_C_CALL mt32emu_set_main_display_mode(mt32emu_const_context context) { + context->synth->setMainDisplayMode(); +} + +void MT32EMU_C_CALL mt32emu_set_display_compatibility(mt32emu_const_context context, mt32emu_boolean old_mt32_compatibility_enabled) { + context->synth->setDisplayCompatibility(old_mt32_compatibility_enabled != MT32EMU_BOOL_FALSE); +} + +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_display_old_mt32_compatible(mt32emu_const_context context) { + return context->synth->isDisplayOldMT32Compatible() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + +mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_display_old_mt32_compatible(mt32emu_const_context context) { + return context->synth->isDefaultDisplayOldMT32Compatible() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; +} + } // extern "C" diff --git a/src/sound/munt/c_interface/c_interface.h b/src/sound/munt/c_interface/c_interface.h index 0924dcce5..5653c9051 100644 --- a/src/sound/munt/c_interface/c_interface.h +++ b/src/sound/munt/c_interface/c_interface.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -24,7 +24,9 @@ #include "c_types.h" #undef MT32EMU_EXPORT +#undef MT32EMU_EXPORT_V #define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT #ifdef __cplusplus extern "C" { @@ -35,24 +37,28 @@ extern "C" { /* === Interface handling === */ /** Returns mt32emu_service_i interface. */ -MT32EMU_EXPORT mt32emu_service_i mt32emu_get_service_i(void); +MT32EMU_EXPORT mt32emu_service_i MT32EMU_C_CALL mt32emu_get_service_i(void); #if MT32EMU_EXPORTS_TYPE == 2 #undef MT32EMU_EXPORT +#undef MT32EMU_EXPORT_V #define MT32EMU_EXPORT +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT #endif /** * Returns the version ID of mt32emu_report_handler_i interface the library has been compiled with. * This allows a client to fall-back gracefully instead of silently not receiving expected event reports. */ -MT32EMU_EXPORT mt32emu_report_handler_version mt32emu_get_supported_report_handler_version(void); +MT32EMU_EXPORT mt32emu_report_handler_version MT32EMU_C_CALL mt32emu_get_supported_report_handler_version(void); /** * Returns the version ID of mt32emu_midi_receiver_version_i interface the library has been compiled with. * This allows a client to fall-back gracefully instead of silently not receiving expected MIDI messages. */ -MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver_version(void); +MT32EMU_EXPORT mt32emu_midi_receiver_version MT32EMU_C_CALL mt32emu_get_supported_midi_receiver_version(void); + +/* === Utility === */ /** * Returns library version as an integer in format: 0x00MMmmpp, where: @@ -60,67 +66,149 @@ MT32EMU_EXPORT mt32emu_midi_receiver_version mt32emu_get_supported_midi_receiver * mm - minor version number * pp - patch number */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_library_version_int(void); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_library_version_int(void); /** * Returns library version as a C-string in format: "MAJOR.MINOR.PATCH". */ -MT32EMU_EXPORT const char *mt32emu_get_library_version_string(void); +MT32EMU_EXPORT const char * MT32EMU_C_CALL mt32emu_get_library_version_string(void); /** * Returns output sample rate used in emulation of stereo analog circuitry of hardware units for particular analog_output_mode. * See comment for mt32emu_analog_output_mode. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_stereo_output_samplerate(const mt32emu_analog_output_mode analog_output_mode); /** * Returns the value of analog_output_mode for which the output signal may retain its full frequency spectrum * at the sample rate specified by the target_samplerate argument. * See comment for mt32emu_analog_output_mode. */ -MT32EMU_EXPORT mt32emu_analog_output_mode mt32emu_get_best_analog_output_mode(const double target_samplerate); +MT32EMU_EXPORT mt32emu_analog_output_mode MT32EMU_C_CALL mt32emu_get_best_analog_output_mode(const double target_samplerate); + +/* === ROM handling === */ + +/** + * Retrieves a list of identifiers (as C-strings) of supported machines. Argument machine_ids points to the array of size + * machine_ids_size to be filled. + * Returns the number of identifiers available for retrieval. The size of the target array to be allocated can be found + * by passing NULL in argument machine_ids; argument machine_ids_size is ignored in this case. + */ +MT32EMU_EXPORT_V(2.5) size_t MT32EMU_C_CALL mt32emu_get_machine_ids(const char **machine_ids, size_t machine_ids_size); +/** + * Retrieves a list of identifiers (as C-strings) of supported ROM images. Argument rom_ids points to the array of size + * rom_ids_size to be filled. Optional argument machine_id can be used to indicate a specific machine to retrieve ROM identifiers + * for; if NULL, identifiers of all the ROM images supported by the emulation engine are retrieved. + * Returns the number of ROM identifiers available for retrieval. The size of the target array to be allocated can be found + * by passing NULL in argument rom_ids; argument rom_ids_size is ignored in this case. If argument machine_id contains + * an unrecognised value, 0 is returned. + */ +MT32EMU_EXPORT_V(2.5) size_t MT32EMU_C_CALL mt32emu_get_rom_ids(const char **rom_ids, size_t rom_ids_size, const char *machine_id); + +/** + * Identifies a ROM image the provided data array contains by its SHA1 digest. Optional argument machine_id can be used to indicate + * a specific machine to identify the ROM image for; if NULL, the ROM image is identified for any supported machine. + * A mt32emu_rom_info structure supplied in argument rom_info is filled in accordance with the provided ROM image; unused fields + * are filled with NULLs. If the content of the ROM image is not identified successfully (e.g. when the ROM image is incompatible + * with the specified machine), all fields of rom_info are filled with NULLs. + * Returns MT32EMU_RC_OK upon success or a negative error code otherwise. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_data(mt32emu_rom_info *rom_info, const mt32emu_bit8u *data, size_t data_size, const char *machine_id); +/** + * Loads the content of the file specified by argument filename and identifies a ROM image the file contains by its SHA1 digest. + * Optional argument machine_id can be used to indicate a specific machine to identify the ROM image for; if NULL, the ROM image + * is identified for any supported machine. + * A mt32emu_rom_info structure supplied in argument rom_info is filled in accordance with the provided ROM image; unused fields + * are filled with NULLs. If the content of the file is not identified successfully (e.g. when the ROM image is incompatible + * with the specified machine), all fields of rom_info are filled with NULLs. + * Returns MT32EMU_RC_OK upon success or a negative error code otherwise. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_identify_rom_file(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id); /* == Context-dependent functions == */ /** Initialises a new emulation context and installs custom report handler if non-NULL. */ -MT32EMU_EXPORT mt32emu_context mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data); +MT32EMU_EXPORT mt32emu_context MT32EMU_C_CALL mt32emu_create_context(mt32emu_report_handler_i report_handler, void *instance_data); /** Closes and destroys emulation context. */ -MT32EMU_EXPORT void mt32emu_free_context(mt32emu_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_free_context(mt32emu_context context); /** - * Adds new ROM identified by its SHA1 digest to the emulation context replacing previously added ROM of the same type if any. - * Argument sha1_digest can be NULL, in this case the digest will be computed using the actual ROM data. + * Adds a new full ROM data image identified by its SHA1 digest to the emulation context replacing previously added ROM of the same + * type if any. Argument sha1_digest can be NULL, in this case the digest will be computed using the actual ROM data. * If sha1_digest is set to non-NULL, it is assumed being correct and will not be recomputed. - * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * The provided data array is NOT copied and used directly for efficiency. The caller should not deallocate it while the emulation + * context is referring to the ROM data. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). * Returns positive value upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_data(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); /** - * Loads a ROM file, identify it by SHA1 digest, and adds it to the emulation context replacing previously added ROM of the same type if any. - * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of mt32emu_open_synth(). + * Loads a ROM file that contains a full ROM data image, identifies it by the SHA1 digest, and adds it to the emulation context + * replacing previously added ROM of the same type if any. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). * Returns positive value upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_add_rom_file(mt32emu_context context, const char *filename); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_add_rom_file(mt32emu_context context, const char *filename); + +/** + * Merges a pair of compatible ROM data image parts into a full image and adds it to the emulation context replacing previously + * added ROM of the same type if any. Each partial image is identified by its SHA1 digest. Arguments partN_sha1_digest can be NULL, + * in this case the digest will be computed using the actual ROM data. If a non-NULL SHA1 value is provided, it is assumed being + * correct and will not be recomputed. The provided data arrays may be deallocated as soon as the function completes. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_data(mt32emu_context context, const mt32emu_bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const mt32emu_bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest); + +/** + * Loads a pair of files that contains compatible parts of a full ROM image, identifies them by the SHA1 digest, merges these + * parts into a full ROM image and adds it to the emulation context replacing previously added ROM of the same type if any. + * This function doesn't immediately change the state of already opened synth. Newly added ROM will take effect upon next call of + * mt32emu_open_synth(). + * Returns positive value upon success. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_merge_and_add_rom_files(mt32emu_context context, const char *part1_filename, const char *part2_filename); + +/** + * Loads a file that contains a ROM image of a specific machine, identifies it by the SHA1 digest, and adds it to the emulation + * context. The ROM image can only be identified successfully if it is compatible with the specified machine. + * Full and partial ROM images are supported and handled according to the following rules: + * - a file with any compatible ROM image is added if none (of the same type) exists in the emulation context; + * - a file with any compatible ROM image replaces any image of the same type that is incompatible with the specified machine; + * - a file with a full ROM image replaces the previously added partial ROM of the same type; + * - a file with a partial ROM image is merged with the previously added ROM image if pairable; + * - otherwise, the file is ignored. + * The described behaviour allows the caller application to traverse a directory with ROM files attempting to add each one in turn. + * As soon as both the full control and the full PCM ROM images are added and / or merged, the iteration can be stopped. + * This function doesn't immediately change the state of already opened synth. Newly added ROMs will take effect upon next call of + * mt32emu_open_synth(). + * Returns a positive value in case changes have been made, MT32EMU_RC_OK if the file has been ignored or a negative error code + * upon failure. + */ +MT32EMU_EXPORT_V(2.5) mt32emu_return_code MT32EMU_C_CALL mt32emu_add_machine_rom_file(mt32emu_context context, const char *machine_id, const char *filename); /** * Fills in mt32emu_rom_info structure with identifiers and descriptions of control and PCM ROM files identified and added to the synth context. * If one of the ROM files is not loaded and identified yet, NULL is returned in the corresponding fields of the mt32emu_rom_info structure. */ -MT32EMU_EXPORT void mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_get_rom_info(mt32emu_const_context context, mt32emu_rom_info *rom_info); /** * Allows to override the default maximum number of partials playing simultaneously within the emulation session. * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_partial_count(mt32emu_context context, const mt32emu_bit32u partial_count); /** * Allows to override the default mode for emulation of analogue circuitry of the hardware units within the emulation session. * This function doesn't immediately change the state of already opened synth. Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_analog_output_mode(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); /** * Allows to convert the synthesiser output to any desired sample rate. The samplerate conversion @@ -131,7 +219,7 @@ MT32EMU_EXPORT void mt32emu_set_analog_output_mode(mt32emu_context context, cons * This function doesn't immediately change the state of already opened synth. * Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_stereo_output_samplerate(mt32emu_context context, const double samplerate); /** * Several samplerate conversion quality options are provided which allow to trade-off the conversion speed vs. @@ -140,66 +228,66 @@ MT32EMU_EXPORT void mt32emu_set_stereo_output_samplerate(mt32emu_context context * This function doesn't immediately change the state of already opened synth. * Newly set value will take effect upon next call of mt32emu_open_synth(). */ -MT32EMU_EXPORT void mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_samplerate_conversion_quality(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); /** * Selects new type of the wave generator and renderer to be used during subsequent calls to mt32emu_open_synth(). * By default, MT32EMU_RT_BIT16S is selected. * See mt32emu_renderer_type for details. */ -MT32EMU_EXPORT void mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_select_renderer_type(mt32emu_context context, const mt32emu_renderer_type renderer_type); /** * Returns previously selected type of the wave generator and renderer. * See mt32emu_renderer_type for details. */ -MT32EMU_EXPORT mt32emu_renderer_type mt32emu_get_selected_renderer_type(mt32emu_context context); +MT32EMU_EXPORT mt32emu_renderer_type MT32EMU_C_CALL mt32emu_get_selected_renderer_type(mt32emu_context context); /** * Prepares the emulation context to receive MIDI messages and produce output audio data using aforehand added set of ROMs, * and optionally set the maximum partial count and the analog output mode. * Returns MT32EMU_RC_OK upon success. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_open_synth(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_open_synth(mt32emu_const_context context); /** Closes the emulation context freeing allocated resources. Added ROMs remain unaffected and ready for reuse. */ -MT32EMU_EXPORT void mt32emu_close_synth(mt32emu_const_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_close_synth(mt32emu_const_context context); /** Returns true if the synth is in completely initialized state, otherwise returns false. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_open(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_open(mt32emu_const_context context); /** * Returns actual sample rate of the fully processed output stereo signal. * If samplerate conversion is used (i.e. when mt32emu_set_stereo_output_samplerate() has been invoked with a non-zero value), * the returned value is the desired output samplerate rounded down to the closest integer. - * Otherwise, the output samplerate is choosen depending on the emulation mode of stereo analog circuitry of hardware units. + * Otherwise, the output samplerate is chosen depending on the emulation mode of stereo analog circuitry of hardware units. * See comment for mt32emu_analog_output_mode for more info. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_actual_stereo_output_samplerate(mt32emu_const_context context); /** * Returns the number of samples produced at the internal synth sample rate (32000 Hz) * that correspond to the given number of samples at the output sample rate. * Intended to facilitate audio time synchronisation. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_output_to_synth_timestamp(mt32emu_const_context context, mt32emu_bit32u output_timestamp); /** * Returns the number of samples produced at the output sample rate * that correspond to the given number of samples at the internal synth sample rate (32000 Hz). * Intended to facilitate audio time synchronisation. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_convert_synth_to_output_timestamp(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); /** All the enqueued events are processed by the synth immediately. */ -MT32EMU_EXPORT void mt32emu_flush_midi_queue(mt32emu_const_context context); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_flush_midi_queue(mt32emu_const_context context); /** * Sets size of the internal MIDI event queue. The queue size is set to the minimum power of 2 that is greater or equal to the size specified. * The queue is flushed before reallocation. * Returns the actual queue size being used. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_set_midi_event_queue_size(mt32emu_const_context context, const mt32emu_bit32u queue_size); /** * Configures the SysEx storage of the internal MIDI event queue. @@ -212,7 +300,7 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_set_midi_event_queue_size(mt32emu_const_co * by a SysEx event, that has been processed and thus is no longer necessary, is disposed instantly. * Note, the queue is flushed and recreated in the process so that its size remains intact. */ -void mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); /** * Installs custom MIDI receiver object intended for receiving MIDI messages generated by MIDI stream parser. @@ -220,13 +308,13 @@ void mt32emu_configure_midi_event_queue_sysex_storage(mt32emu_const_context cont * By default, parsed short MIDI messages and System Exclusive messages are sent to the synth input MIDI queue. * This function allows to override default behaviour. If midi_receiver argument is set to NULL, the default behaviour is restored. */ -MT32EMU_EXPORT void mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_midi_receiver(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); /** * Returns current value of the global counter of samples rendered since the synth was created (at the native sample rate 32000 Hz). * This method helps to compute accurate timestamp of a MIDI message to use with the methods below. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_internal_rendered_sample_count(mt32emu_const_context context); /* Enqueues a MIDI event for subsequent playback. * The MIDI event will be processed not before the specified timestamp. @@ -243,7 +331,7 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_internal_rendered_sample_count(mt32emu * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). */ -MT32EMU_EXPORT void mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_parse_stream(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); /** * Parses a block of raw MIDI bytes and enqueues parsed MIDI messages to play at specified time. @@ -251,31 +339,31 @@ MT32EMU_EXPORT void mt32emu_parse_stream(mt32emu_const_context context, const mt * When a System Realtime MIDI message is parsed, onMIDISystemRealtime callback is invoked. * NOTE: the total length of a SysEx message being fragmented shall not exceed MT32EMU_MAX_STREAM_BUFFER_SIZE (32768 bytes). */ -MT32EMU_EXPORT void mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_parse_stream_at(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); /** * Enqueues a single mt32emu_bit32u-encoded short MIDI message with full processing ASAP. * The short MIDI message may contain no status byte, the running status is used in this case. * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. */ -MT32EMU_EXPORT void mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_short_message(mt32emu_const_context context, mt32emu_bit32u message); /** * Enqueues a single mt32emu_bit32u-encoded short MIDI message to play at specified time with full processing. * The short MIDI message may contain no status byte, the running status is used in this case. * When the argument is a System Realtime MIDI message, onMIDISystemRealtime callback is invoked. */ -MT32EMU_EXPORT void mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_short_message_at(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); /** Enqueues a single short MIDI message to be processed ASAP. The message must contain a status byte. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg(mt32emu_const_context context, mt32emu_bit32u msg); /** Enqueues a single well formed System Exclusive MIDI message to be processed ASAP. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** Enqueues a single short MIDI message to play at specified time. The message must contain a status byte. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_msg_at(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); /** Enqueues a single well formed System Exclusive MIDI message to play at specified time. */ -MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); +MT32EMU_EXPORT mt32emu_return_code MT32EMU_C_CALL mt32emu_play_sysex_at(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); /* WARNING: * The methods below don't ensure minimum 1-sample delay between sequential MIDI events, @@ -287,73 +375,73 @@ MT32EMU_EXPORT mt32emu_return_code mt32emu_play_sysex_at(mt32emu_const_context c * Sends a short MIDI message to the synth for immediate playback. The message must contain a status byte. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_msg_now(mt32emu_const_context context, mt32emu_bit32u msg); /** * Sends unpacked short MIDI message to the synth for immediate playback. The message must contain a status byte. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_msg_on_part(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); /** * Sends a single well formed System Exclusive MIDI message for immediate processing. The length is in bytes. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_play_sysex_now(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** * Sends inner body of a System Exclusive MIDI message for direct processing. The length is in bytes. * See the WARNING above. */ -MT32EMU_EXPORT void mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_write_sysex(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); /** Allows to disable wet reverb output altogether. */ -MT32EMU_EXPORT void mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_enabled(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); /** Returns whether wet reverb output is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_enabled(mt32emu_const_context context); /** * Sets override reverb mode. In this mode, emulation ignores sysexes (or the related part of them) which control the reverb parameters. * This mode is in effect until it is turned off. When the synth is re-opened, the override mode is unchanged but the state * of the reverb model is reset to default. */ -MT32EMU_EXPORT void mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_overridden(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); /** Returns whether reverb settings are overridden. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reverb_overridden(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reverb_overridden(mt32emu_const_context context); /** * Forces reverb model compatibility mode. By default, the compatibility mode corresponds to the used control ROM version. * Invoking this method with the argument set to true forces emulation of old MT-32 reverb circuit. * When the argument is false, emulation of the reverb circuit used in new generation of MT-32 compatible modules is enforced * (these include CM-32L and LAPC-I). */ -MT32EMU_EXPORT void mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_compatibility_mode(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); /** Returns whether reverb is in old MT-32 compatibility mode. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_mt32_reverb_compatibility_mode(mt32emu_const_context context); /** Returns whether default reverb compatibility mode is the old MT-32 compatibility mode. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_reverb_mt32_compatible(mt32emu_const_context context); /** - * If enabled, reverb buffers for all modes are keept around allocated all the time to avoid memory + * If enabled, reverb buffers for all modes are kept around allocated all the time to avoid memory * allocating/freeing in the rendering thread, which may be required for realtime operation. * Otherwise, reverb buffers that are not in use are deleted to save memory (the default behaviour). */ -MT32EMU_EXPORT void mt32emu_preallocate_reverb_memory(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_preallocate_reverb_memory(mt32emu_const_context context, const mt32emu_boolean enabled); /** Sets new DAC input mode. See mt32emu_dac_input_mode for details. */ -MT32EMU_EXPORT void mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_dac_input_mode(mt32emu_const_context context, const mt32emu_dac_input_mode mode); /** Returns current DAC input mode. See mt32emu_dac_input_mode for details. */ -MT32EMU_EXPORT mt32emu_dac_input_mode mt32emu_get_dac_input_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_dac_input_mode MT32EMU_C_CALL mt32emu_get_dac_input_mode(mt32emu_const_context context); /** Sets new MIDI delay mode. See mt32emu_midi_delay_mode for details. */ -MT32EMU_EXPORT void mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_midi_delay_mode(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); /** Returns current MIDI delay mode. See mt32emu_midi_delay_mode for details. */ -MT32EMU_EXPORT mt32emu_midi_delay_mode mt32emu_get_midi_delay_mode(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_midi_delay_mode MT32EMU_C_CALL mt32emu_get_midi_delay_mode(mt32emu_const_context context); /** * Sets output gain factor for synth output channels. Applied to all output samples and unrelated with the synth's Master volume, * it rather corresponds to the gain of the output analog circuitry of the hardware units. However, together with mt32emu_set_reverb_output_gain() * it offers to the user a capability to control the gain of reverb and non-reverb output channels independently. */ -MT32EMU_EXPORT void mt32emu_set_output_gain(mt32emu_const_context context, float gain); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_output_gain(mt32emu_const_context context, float gain); /** Returns current output gain factor for synth output channels. */ -MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context); +MT32EMU_EXPORT float MT32EMU_C_CALL mt32emu_get_output_gain(mt32emu_const_context context); /** * Sets output gain factor for the reverb wet output channels. It rather corresponds to the gain of the output @@ -365,14 +453,34 @@ MT32EMU_EXPORT float mt32emu_get_output_gain(mt32emu_const_context context); * there is a difference in the reverb analogue circuit, and the resulting output gain is 0.68 * of that for LA32 analogue output. This factor is applied to the reverb output gain. */ -MT32EMU_EXPORT void mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reverb_output_gain(mt32emu_const_context context, float gain); /** Returns current output gain factor for reverb wet output channels. */ -MT32EMU_EXPORT float mt32emu_get_reverb_output_gain(mt32emu_const_context context); +MT32EMU_EXPORT float MT32EMU_C_CALL mt32emu_get_reverb_output_gain(mt32emu_const_context context); + +/** + * Sets (or removes) an override for the current volume (output level) on a specific part. + * When the part volume is overridden, the MIDI controller Volume (7) on the MIDI channel this part is assigned to + * has no effect on the output level of this part. Similarly, the output level value set on this part via a SysEx that + * modifies the Patch temp structure is disregarded. + * To enable the override mode, argument volumeOverride should be in range 0..100, setting a value outside this range + * disables the previously set override, if any. + * Note: Setting volumeOverride to 0 mutes the part completely, meaning no sound is generated at all. + * This is unlike the behaviour of real devices - setting 0 volume on a part may leave it still producing + * sound at a very low level. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u volume_override); +/** + * Returns the overridden volume previously set on a specific part; a value outside the range 0..100 means no override + * is currently in effect. + * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_bit8u MT32EMU_C_CALL mt32emu_get_part_volume_override(mt32emu_const_context context, mt32emu_bit8u part_number); /** Swaps left and right output channels. */ -MT32EMU_EXPORT void mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_reversed_stereo_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether left and right output channels are swapped. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_reversed_stereo_enabled(mt32emu_const_context context); /** * Allows to toggle the NiceAmpRamp mode. @@ -382,9 +490,9 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_reversed_stereo_enabled(mt32emu_const_ * We also prefer the quality improvement over the emulation accuracy, * so this mode is enabled by default. */ -MT32EMU_EXPORT void mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_amp_ramp_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether NiceAmpRamp mode is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_context context); /** * Allows to toggle the NicePanning mode. @@ -395,9 +503,9 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_amp_ramp_enabled(mt32emu_const_co * making it smoother thus sacrificing the emulation accuracy. * This mode is disabled by default. */ -MT32EMU_EXPORT void mt32emu_set_nice_panning_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_panning_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether NicePanning mode is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_panning_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_panning_enabled(mt32emu_const_context context); /** * Allows to toggle the NicePartialMixing mode. @@ -409,9 +517,9 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_panning_enabled(mt32emu_const_con * thus making the behaviour more predictable. * This mode is disabled by default. */ -MT32EMU_EXPORT void mt32emu_set_nice_partial_mixing_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_set_nice_partial_mixing_enabled(mt32emu_const_context context, const mt32emu_boolean enabled); /** Returns whether NicePartialMixing mode is enabled. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_partial_mixing_enabled(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_nice_partial_mixing_enabled(mt32emu_const_context context); /** * Renders samples to the specified output stream as if they were sampled at the analog stereo output at the desired sample rate. @@ -419,9 +527,9 @@ MT32EMU_EXPORT mt32emu_boolean mt32emu_is_nice_partial_mixing_enabled(mt32emu_co * mode of analog circuitry emulation. See mt32emu_analog_output_mode. * The length is in frames, not bytes (in 16-bit stereo, one frame is 4 bytes). Uses NATIVE byte ordering. */ -MT32EMU_EXPORT void mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_bit16s(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); /** Same as above but outputs to a float stereo stream. */ -MT32EMU_EXPORT void mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_float(mt32emu_const_context context, float *stream, mt32emu_bit32u len); /** * Renders samples to the specified output streams as if they appeared at the DAC entrance. @@ -429,25 +537,25 @@ MT32EMU_EXPORT void mt32emu_render_float(mt32emu_const_context context, float *s * NULL may be specified in place of any or all of the stream buffers to skip it. * The length is in samples, not bytes. Uses NATIVE byte ordering. */ -MT32EMU_EXPORT void mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_bit16s_streams(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); /** Same as above but outputs to float streams. */ -MT32EMU_EXPORT void mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_render_float_streams(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); /** Returns true when there is at least one active partial, otherwise false. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_has_active_partials(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_has_active_partials(mt32emu_const_context context); /** Returns true if mt32emu_has_active_partials() returns true, or reverb is (somewhat unreliably) detected as being active. */ -MT32EMU_EXPORT mt32emu_boolean mt32emu_is_active(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_boolean MT32EMU_C_CALL mt32emu_is_active(mt32emu_const_context context); /** Returns the maximum number of partials playing simultaneously. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_partial_count(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_partial_count(mt32emu_const_context context); /** * Returns current states of all the parts as a bit set. The least significant bit corresponds to the state of part 1, * total of 9 bits hold the states of all the parts. If the returned bit for a part is set, there is at least one active * non-releasing partial playing on this part. This info is useful in emulating behaviour of LCD display of the hardware units. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context context); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_part_states(mt32emu_const_context context); /** * Fills in current states of all the partials into the array provided. Each byte in the array holds states of 4 partials @@ -455,7 +563,7 @@ MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_part_states(mt32emu_const_context cont * The array must be large enough to accommodate states of all the partials. * @see getPartialCount() */ -MT32EMU_EXPORT void mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_get_partial_states(mt32emu_const_context context, mt32emu_bit8u *partial_states); /** * Fills in information about currently playing notes on the specified part into the arrays provided. The arrays must be large enough @@ -463,16 +571,71 @@ MT32EMU_EXPORT void mt32emu_get_partial_states(mt32emu_const_context context, mt * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. * Returns the number of currently playing notes on the specified part. */ -MT32EMU_EXPORT mt32emu_bit32u mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); +MT32EMU_EXPORT mt32emu_bit32u MT32EMU_C_CALL mt32emu_get_playing_notes(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); /** * Returns name of the patch set on the specified part. * Argument partNumber should be 0..7 for Part 1..8, or 8 for Rhythm. + * The returned value is a null-terminated string which is guaranteed to remain valid until the next call to one of functions + * that perform sample rendering or immediate SysEx processing (e.g. mt32emu_play_sysex_now). */ -MT32EMU_EXPORT const char *mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number); +MT32EMU_EXPORT const char * MT32EMU_C_CALL mt32emu_get_patch_name(mt32emu_const_context context, mt32emu_bit8u part_number); + +/** + * Retrieves the name of the sound group the timbre identified by arguments timbre_group and timbre_number is associated with. + * Values 0-3 of timbre_group correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + * For all but the RHYTHM timbre bank, allowed values of timbre_number are in range 0-63. The number of timbres + * contained in the RHYTHM bank depends on the used control ROM version. + * The argument sound_group_name must point to an array of at least 8 characters. The result is a null-terminated string. + * Returns whether the specified timbre has been found and the result written in sound_group_name. + */ +MT32EMU_EXPORT_V(2.7) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_group_name(mt32emu_const_context context, char *sound_group_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); +/** + * Retrieves the name of the timbre identified by arguments timbre_group and timbre_number. + * Values 0-3 of timbre_group correspond to the timbre banks GROUP A, GROUP B, MEMORY and RHYTHM. + * For all but the RHYTHM timbre bank, allowed values of timbre_number are in range 0-63. The number of timbres + * contained in the RHYTHM bank depends on the used control ROM version. + * The argument sound_name must point to an array of at least 11 characters. The result is a null-terminated string. + * Returns whether the specified timbre has been found and the result written in sound_name. + */ +MT32EMU_EXPORT_V(2.7) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_sound_name(mt32emu_const_context context, char *sound_name, mt32emu_bit8u timbreGroup, mt32emu_bit8u timbreNumber); /** Stores internal state of emulated synth into an array provided (as it would be acquired from hardware). */ -MT32EMU_EXPORT void mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); +MT32EMU_EXPORT void MT32EMU_C_CALL mt32emu_read_memory(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); + +/** + * Retrieves the current state of the emulated MT-32 display facilities. + * Typically, the state is updated during the rendering. When that happens, a related callback from mt32emu_report_handler_i_v1 + * is invoked. However, there might be no need to invoke this method after each update, e.g. when the render buffer is just + * a few milliseconds long. + * The argument target_buffer must point to an array of at least 21 characters. The result is a null-terminated string. + * The argument narrow_lcd enables a condensed representation of the displayed information in some cases. This is mainly intended + * to route the result to a hardware LCD that is only 16 characters wide. Automatic scrolling of longer strings is not supported. + * Returns whether the MIDI MESSAGE LED is ON and fills the target_buffer parameter. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_get_display_state(mt32emu_const_context context, char *target_buffer, const mt32emu_boolean narrow_lcd); + +/** + * Resets the emulated LCD to the main mode (Master Volume). This has the same effect as pressing the Master Volume button + * while the display shows some other message. Useful for the new-gen devices as those require a special Display Reset SysEx + * to return to the main mode e.g. from showing a custom display message or a checksum error. + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_main_display_mode(mt32emu_const_context context); + +/** + * Permits to select an arbitrary display emulation model that does not necessarily match the actual behaviour implemented + * in the control ROM version being used. + * Invoking this method with the argument set to true forces emulation of the old-gen MT-32 display features. + * Otherwise, emulation of the new-gen devices is enforced (these include CM-32L and LAPC-I as if these were connected to an LCD). + */ +MT32EMU_EXPORT_V(2.6) void MT32EMU_C_CALL mt32emu_set_display_compatibility(mt32emu_const_context context, mt32emu_boolean old_mt32_compatibility_enabled); +/** Returns whether the currently configured features of the emulated display are compatible with the old-gen MT-32 devices. */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_is_display_old_mt32_compatible(mt32emu_const_context context); +/** + * Returns whether the emulated display features configured by default depending on the actual control ROM version + * are compatible with the old-gen MT-32 devices. + */ +MT32EMU_EXPORT_V(2.6) mt32emu_boolean MT32EMU_C_CALL mt32emu_is_default_display_old_mt32_compatible(mt32emu_const_context context); #ifdef __cplusplus } // extern "C" diff --git a/src/sound/munt/c_interface/c_types.h b/src/sound/munt/c_interface/c_types.h index 74bae8df4..8928bfeae 100644 --- a/src/sound/munt/c_interface/c_types.h +++ b/src/sound/munt/c_interface/c_types.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -27,6 +27,12 @@ #include "../Enumerations.h" #undef MT32EMU_C_ENUMERATIONS +#ifdef _WIN32 +# define MT32EMU_C_CALL __cdecl +#else +# define MT32EMU_C_CALL +#endif + typedef unsigned int mt32emu_bit32u; typedef signed int mt32emu_bit32s; typedef unsigned short int mt32emu_bit16u; @@ -45,6 +51,8 @@ typedef enum { MT32EMU_RC_OK = 0, MT32EMU_RC_ADDED_CONTROL_ROM = 1, MT32EMU_RC_ADDED_PCM_ROM = 2, + MT32EMU_RC_ADDED_PARTIAL_CONTROL_ROM = 3, + MT32EMU_RC_ADDED_PARTIAL_PCM_ROM = 4, /* Definite error occurred. */ MT32EMU_RC_ROM_NOT_IDENTIFIED = -1, @@ -53,6 +61,8 @@ typedef enum { MT32EMU_RC_MISSING_ROMS = -4, MT32EMU_RC_NOT_OPENED = -5, MT32EMU_RC_QUEUE_FULL = -6, + MT32EMU_RC_ROMS_NOT_PAIRABLE = -7, + MT32EMU_RC_MACHINE_NOT_IDENTIFIED = -8, /* Undefined error occurred. */ MT32EMU_RC_FAILED = -100 @@ -107,7 +117,8 @@ typedef struct { /** Report handler interface versions */ typedef enum { MT32EMU_REPORT_HANDLER_VERSION_0 = 0, - MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_0 + MT32EMU_REPORT_HANDLER_VERSION_1 = 1, + MT32EMU_REPORT_HANDLER_VERSION_CURRENT = MT32EMU_REPORT_HANDLER_VERSION_1 } mt32emu_report_handler_version; /** MIDI receiver interface versions */ @@ -122,7 +133,10 @@ typedef enum { MT32EMU_SERVICE_VERSION_1 = 1, MT32EMU_SERVICE_VERSION_2 = 2, MT32EMU_SERVICE_VERSION_3 = 3, - MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_3 + MT32EMU_SERVICE_VERSION_4 = 4, + MT32EMU_SERVICE_VERSION_5 = 5, + MT32EMU_SERVICE_VERSION_6 = 6, + MT32EMU_SERVICE_VERSION_CURRENT = MT32EMU_SERVICE_VERSION_6 } mt32emu_service_version; /* === Report Handler Interface === */ @@ -130,42 +144,59 @@ typedef enum { typedef union mt32emu_report_handler_i mt32emu_report_handler_i; /** Interface for handling reported events (initial version) */ -typedef struct { - /** Returns the actual interface version ID */ - mt32emu_report_handler_version (*getVersionID)(mt32emu_report_handler_i i); - - /** Callback for debug messages, in vprintf() format */ - void (*printDebug)(void *instance_data, const char *fmt, va_list list); - /** Callbacks for reporting errors */ - void (*onErrorControlROM)(void *instance_data); - void (*onErrorPCMROM)(void *instance_data); - /** Callback for reporting about displaying a new custom message on LCD */ - void (*showLCDMessage)(void *instance_data, const char *message); - /** Callback for reporting actual processing of a MIDI message */ - void (*onMIDIMessagePlayed)(void *instance_data); +#define MT32EMU_REPORT_HANDLER_I_V0 \ + /** Returns the actual interface version ID */ \ + mt32emu_report_handler_version (MT32EMU_C_CALL *getVersionID)(mt32emu_report_handler_i i); \ +\ + /** Callback for debug messages, in vprintf() format */ \ + void (MT32EMU_C_CALL *printDebug)(void *instance_data, const char *fmt, va_list list); \ + /** Callbacks for reporting errors */ \ + void (MT32EMU_C_CALL *onErrorControlROM)(void *instance_data); \ + void (MT32EMU_C_CALL *onErrorPCMROM)(void *instance_data); \ + /** Callback for reporting about displaying a new custom message on LCD */ \ + void (MT32EMU_C_CALL *showLCDMessage)(void *instance_data, const char *message); \ + /** Callback for reporting actual processing of a MIDI message */ \ + void (MT32EMU_C_CALL *onMIDIMessagePlayed)(void *instance_data); \ /** * Callback for reporting an overflow of the input MIDI queue. * Returns MT32EMU_BOOL_TRUE if a recovery action was taken * and yet another attempt to enqueue the MIDI event is desired. - */ - mt32emu_boolean (*onMIDIQueueOverflow)(void *instance_data); + */ \ + mt32emu_boolean (MT32EMU_C_CALL *onMIDIQueueOverflow)(void *instance_data); \ /** * Callback invoked when a System Realtime MIDI message is detected in functions * mt32emu_parse_stream and mt32emu_play_short_message and the likes. - */ - void (*onMIDISystemRealtime)(void *instance_data, mt32emu_bit8u system_realtime); - /** Callbacks for reporting system events */ - void (*onDeviceReset)(void *instance_data); - void (*onDeviceReconfig)(void *instance_data); - /** Callbacks for reporting changes of reverb settings */ - void (*onNewReverbMode)(void *instance_data, mt32emu_bit8u mode); - void (*onNewReverbTime)(void *instance_data, mt32emu_bit8u time); - void (*onNewReverbLevel)(void *instance_data, mt32emu_bit8u level); - /** Callbacks for reporting various information */ - void (*onPolyStateChanged)(void *instance_data, mt32emu_bit8u part_num); - void (*onProgramChanged)(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name); + */ \ + void (MT32EMU_C_CALL *onMIDISystemRealtime)(void *instance_data, mt32emu_bit8u system_realtime); \ + /** Callbacks for reporting system events */ \ + void (MT32EMU_C_CALL *onDeviceReset)(void *instance_data); \ + void (MT32EMU_C_CALL *onDeviceReconfig)(void *instance_data); \ + /** Callbacks for reporting changes of reverb settings */ \ + void (MT32EMU_C_CALL *onNewReverbMode)(void *instance_data, mt32emu_bit8u mode); \ + void (MT32EMU_C_CALL *onNewReverbTime)(void *instance_data, mt32emu_bit8u time); \ + void (MT32EMU_C_CALL *onNewReverbLevel)(void *instance_data, mt32emu_bit8u level); \ + /** Callbacks for reporting various information */ \ + void (MT32EMU_C_CALL *onPolyStateChanged)(void *instance_data, mt32emu_bit8u part_num); \ + void (MT32EMU_C_CALL *onProgramChanged)(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name); + +#define MT32EMU_REPORT_HANDLER_I_V1 \ + /** + * Invoked to signal about a change of the emulated LCD state. Use mt32emu_get_display_state to retrieve the actual data. + * This callback will not be invoked on further changes, until the client retrieves the LCD state. + */ \ + void (MT32EMU_C_CALL *onLCDStateUpdated)(void *instance_data); \ + /** Invoked when the emulated MIDI MESSAGE LED changes state. The led_state parameter represents whether the LED is ON. */ \ + void (MT32EMU_C_CALL *onMidiMessageLEDStateUpdated)(void *instance_data, mt32emu_boolean led_state); + +typedef struct { + MT32EMU_REPORT_HANDLER_I_V0 } mt32emu_report_handler_i_v0; +typedef struct { + MT32EMU_REPORT_HANDLER_I_V0 + MT32EMU_REPORT_HANDLER_I_V1 +} mt32emu_report_handler_i_v1; + /** * Extensible interface for handling reported events. * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. @@ -173,8 +204,12 @@ typedef struct { */ union mt32emu_report_handler_i { const mt32emu_report_handler_i_v0 *v0; + const mt32emu_report_handler_i_v1 *v1; }; +#undef MT32EMU_REPORT_HANDLER_I_V0 +#undef MT32EMU_REPORT_HANDLER_I_V1 + /* === MIDI Receiver Interface === */ typedef union mt32emu_midi_receiver_i mt32emu_midi_receiver_i; @@ -182,16 +217,16 @@ typedef union mt32emu_midi_receiver_i mt32emu_midi_receiver_i; /** Interface for receiving MIDI messages generated by MIDI stream parser (initial version) */ typedef struct { /** Returns the actual interface version ID */ - mt32emu_midi_receiver_version (*getVersionID)(mt32emu_midi_receiver_i i); + mt32emu_midi_receiver_version (MT32EMU_C_CALL *getVersionID)(mt32emu_midi_receiver_i i); /** Invoked when a complete short MIDI message is parsed in the input MIDI stream. */ - void (*handleShortMessage)(void *instance_data, const mt32emu_bit32u message); + void (MT32EMU_C_CALL *handleShortMessage)(void *instance_data, const mt32emu_bit32u message); /** Invoked when a complete well-formed System Exclusive MIDI message is parsed in the input MIDI stream. */ - void (*handleSysex)(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length); + void (MT32EMU_C_CALL *handleSysex)(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length); /** Invoked when a System Realtime MIDI message is parsed in the input MIDI stream. */ - void (*handleSystemRealtimeMessage)(void *instance_data, const mt32emu_bit8u realtime); + void (MT32EMU_C_CALL *handleSystemRealtimeMessage)(void *instance_data, const mt32emu_bit8u realtime); } mt32emu_midi_receiver_i_v0; /** @@ -216,101 +251,124 @@ typedef union mt32emu_service_i mt32emu_service_i; */ #define MT32EMU_SERVICE_I_V0 \ /** Returns the actual interface version ID */ \ - mt32emu_service_version (*getVersionID)(mt32emu_service_i i); \ - mt32emu_report_handler_version (*getSupportedReportHandlerVersionID)(void); \ - mt32emu_midi_receiver_version (*getSupportedMIDIReceiverVersionID)(void); \ + mt32emu_service_version (MT32EMU_C_CALL *getVersionID)(mt32emu_service_i i); \ + mt32emu_report_handler_version (MT32EMU_C_CALL *getSupportedReportHandlerVersionID)(void); \ + mt32emu_midi_receiver_version (MT32EMU_C_CALL *getSupportedMIDIReceiverVersionID)(void); \ \ - mt32emu_bit32u (*getLibraryVersionInt)(void); \ - const char *(*getLibraryVersionString)(void); \ + mt32emu_bit32u (MT32EMU_C_CALL *getLibraryVersionInt)(void); \ + const char *(MT32EMU_C_CALL *getLibraryVersionString)(void); \ \ - mt32emu_bit32u (*getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); \ + mt32emu_bit32u (MT32EMU_C_CALL *getStereoOutputSamplerate)(const mt32emu_analog_output_mode analog_output_mode); \ \ - mt32emu_context (*createContext)(mt32emu_report_handler_i report_handler, void *instance_data); \ - void (*freeContext)(mt32emu_context context); \ - mt32emu_return_code (*addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); \ - mt32emu_return_code (*addROMFile)(mt32emu_context context, const char *filename); \ - void (*getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); \ - void (*setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); \ - void (*setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); \ - mt32emu_return_code (*openSynth)(mt32emu_const_context context); \ - void (*closeSynth)(mt32emu_const_context context); \ - mt32emu_boolean (*isOpen)(mt32emu_const_context context); \ - mt32emu_bit32u (*getActualStereoOutputSamplerate)(mt32emu_const_context context); \ - void (*flushMIDIQueue)(mt32emu_const_context context); \ - mt32emu_bit32u (*setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); \ - void (*setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); \ + mt32emu_context (MT32EMU_C_CALL *createContext)(mt32emu_report_handler_i report_handler, void *instance_data); \ + void (MT32EMU_C_CALL *freeContext)(mt32emu_context context); \ + mt32emu_return_code (MT32EMU_C_CALL *addROMData)(mt32emu_context context, const mt32emu_bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest); \ + mt32emu_return_code (MT32EMU_C_CALL *addROMFile)(mt32emu_context context, const char *filename); \ + void (MT32EMU_C_CALL *getROMInfo)(mt32emu_const_context context, mt32emu_rom_info *rom_info); \ + void (MT32EMU_C_CALL *setPartialCount)(mt32emu_context context, const mt32emu_bit32u partial_count); \ + void (MT32EMU_C_CALL *setAnalogOutputMode)(mt32emu_context context, const mt32emu_analog_output_mode analog_output_mode); \ + mt32emu_return_code (MT32EMU_C_CALL *openSynth)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *closeSynth)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isOpen)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getActualStereoOutputSamplerate)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *flushMIDIQueue)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *setMIDIEventQueueSize)(mt32emu_const_context context, const mt32emu_bit32u queue_size); \ + void (MT32EMU_C_CALL *setMIDIReceiver)(mt32emu_context context, mt32emu_midi_receiver_i midi_receiver, void *instance_data); \ \ - void (*parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); \ - void (*parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); \ - void (*playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); \ - void (*playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); \ - mt32emu_return_code (*playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); \ - mt32emu_return_code (*playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ - mt32emu_return_code (*playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); \ - mt32emu_return_code (*playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); \ + void (MT32EMU_C_CALL *parseStream)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length); \ + void (MT32EMU_C_CALL *parseStream_At)(mt32emu_const_context context, const mt32emu_bit8u *stream, mt32emu_bit32u length, mt32emu_bit32u timestamp); \ + void (MT32EMU_C_CALL *playShortMessage)(mt32emu_const_context context, mt32emu_bit32u message); \ + void (MT32EMU_C_CALL *playShortMessageAt)(mt32emu_const_context context, mt32emu_bit32u message, mt32emu_bit32u timestamp); \ + mt32emu_return_code (MT32EMU_C_CALL *playMsg)(mt32emu_const_context context, mt32emu_bit32u msg); \ + mt32emu_return_code (MT32EMU_C_CALL *playSysex)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + mt32emu_return_code (MT32EMU_C_CALL *playMsgAt)(mt32emu_const_context context, mt32emu_bit32u msg, mt32emu_bit32u timestamp); \ + mt32emu_return_code (MT32EMU_C_CALL *playSysexAt)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len, mt32emu_bit32u timestamp); \ \ - void (*playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); \ - void (*playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); \ - void (*playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ - void (*writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *playMsgNow)(mt32emu_const_context context, mt32emu_bit32u msg); \ + void (MT32EMU_C_CALL *playMsgOnPart)(mt32emu_const_context context, mt32emu_bit8u part, mt32emu_bit8u code, mt32emu_bit8u note, mt32emu_bit8u velocity); \ + void (MT32EMU_C_CALL *playSysexNow)(mt32emu_const_context context, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *writeSysex)(mt32emu_const_context context, mt32emu_bit8u channel, const mt32emu_bit8u *sysex, mt32emu_bit32u len); \ \ - void (*setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); \ - mt32emu_boolean (*isReverbEnabled)(mt32emu_const_context context); \ - void (*setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); \ - mt32emu_boolean (*isReverbOverridden)(mt32emu_const_context context); \ - void (*setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); \ - mt32emu_boolean (*isMT32ReverbCompatibilityMode)(mt32emu_const_context context); \ - mt32emu_boolean (*isDefaultReverbMT32Compatible)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbEnabled)(mt32emu_const_context context, const mt32emu_boolean reverb_enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isReverbEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbOverridden)(mt32emu_const_context context, const mt32emu_boolean reverb_overridden); \ + mt32emu_boolean (MT32EMU_C_CALL *isReverbOverridden)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbCompatibilityMode)(mt32emu_const_context context, const mt32emu_boolean mt32_compatible_mode); \ + mt32emu_boolean (MT32EMU_C_CALL *isMT32ReverbCompatibilityMode)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isDefaultReverbMT32Compatible)(mt32emu_const_context context); \ \ - void (*setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); \ - mt32emu_dac_input_mode (*getDACInputMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setDACInputMode)(mt32emu_const_context context, const mt32emu_dac_input_mode mode); \ + mt32emu_dac_input_mode (MT32EMU_C_CALL *getDACInputMode)(mt32emu_const_context context); \ \ - void (*setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); \ - mt32emu_midi_delay_mode (*getMIDIDelayMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setMIDIDelayMode)(mt32emu_const_context context, const mt32emu_midi_delay_mode mode); \ + mt32emu_midi_delay_mode (MT32EMU_C_CALL *getMIDIDelayMode)(mt32emu_const_context context); \ \ - void (*setOutputGain)(mt32emu_const_context context, float gain); \ - float (*getOutputGain)(mt32emu_const_context context); \ - void (*setReverbOutputGain)(mt32emu_const_context context, float gain); \ - float (*getReverbOutputGain)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setOutputGain)(mt32emu_const_context context, float gain); \ + float (MT32EMU_C_CALL *getOutputGain)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReverbOutputGain)(mt32emu_const_context context, float gain); \ + float (MT32EMU_C_CALL *getReverbOutputGain)(mt32emu_const_context context); \ \ - void (*setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isReversedStereoEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setReversedStereoEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isReversedStereoEnabled)(mt32emu_const_context context); \ \ - void (*renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); \ - void (*renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); \ - void (*renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); \ - void (*renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderBit16s)(mt32emu_const_context context, mt32emu_bit16s *stream, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderFloat)(mt32emu_const_context context, float *stream, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderBit16sStreams)(mt32emu_const_context context, const mt32emu_dac_output_bit16s_streams *streams, mt32emu_bit32u len); \ + void (MT32EMU_C_CALL *renderFloatStreams)(mt32emu_const_context context, const mt32emu_dac_output_float_streams *streams, mt32emu_bit32u len); \ \ - mt32emu_boolean (*hasActivePartials)(mt32emu_const_context context); \ - mt32emu_boolean (*isActive)(mt32emu_const_context context); \ - mt32emu_bit32u (*getPartialCount)(mt32emu_const_context context); \ - mt32emu_bit32u (*getPartStates)(mt32emu_const_context context); \ - void (*getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); \ - mt32emu_bit32u (*getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); \ - const char *(*getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); \ - void (*readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); + mt32emu_boolean (MT32EMU_C_CALL *hasActivePartials)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isActive)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPartialCount)(mt32emu_const_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPartStates)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *getPartialStates)(mt32emu_const_context context, mt32emu_bit8u *partial_states); \ + mt32emu_bit32u (MT32EMU_C_CALL *getPlayingNotes)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u *keys, mt32emu_bit8u *velocities); \ + const char *(MT32EMU_C_CALL *getPatchName)(mt32emu_const_context context, mt32emu_bit8u part_number); \ + void (MT32EMU_C_CALL *readMemory)(mt32emu_const_context context, mt32emu_bit32u addr, mt32emu_bit32u len, mt32emu_bit8u *data); #define MT32EMU_SERVICE_I_V1 \ - mt32emu_analog_output_mode (*getBestAnalogOutputMode)(const double target_samplerate); \ - void (*setStereoOutputSampleRate)(mt32emu_context context, const double samplerate); \ - void (*setSamplerateConversionQuality)(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); \ - void (*selectRendererType)(mt32emu_context context, mt32emu_renderer_type renderer_type); \ - mt32emu_renderer_type (*getSelectedRendererType)(mt32emu_context context); \ - mt32emu_bit32u (*convertOutputToSynthTimestamp)(mt32emu_const_context context, mt32emu_bit32u output_timestamp); \ - mt32emu_bit32u (*convertSynthToOutputTimestamp)(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); + mt32emu_analog_output_mode (MT32EMU_C_CALL *getBestAnalogOutputMode)(const double target_samplerate); \ + void (MT32EMU_C_CALL *setStereoOutputSampleRate)(mt32emu_context context, const double samplerate); \ + void (MT32EMU_C_CALL *setSamplerateConversionQuality)(mt32emu_context context, const mt32emu_samplerate_conversion_quality quality); \ + void (MT32EMU_C_CALL *selectRendererType)(mt32emu_context context, mt32emu_renderer_type renderer_type); \ + mt32emu_renderer_type (MT32EMU_C_CALL *getSelectedRendererType)(mt32emu_context context); \ + mt32emu_bit32u (MT32EMU_C_CALL *convertOutputToSynthTimestamp)(mt32emu_const_context context, mt32emu_bit32u output_timestamp); \ + mt32emu_bit32u (MT32EMU_C_CALL *convertSynthToOutputTimestamp)(mt32emu_const_context context, mt32emu_bit32u synth_timestamp); #define MT32EMU_SERVICE_I_V2 \ - mt32emu_bit32u (*getInternalRenderedSampleCount)(mt32emu_const_context context); \ - void (*setNiceAmpRampEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isNiceAmpRampEnabled)(mt32emu_const_context context); + mt32emu_bit32u (MT32EMU_C_CALL *getInternalRenderedSampleCount)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setNiceAmpRampEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNiceAmpRampEnabled)(mt32emu_const_context context); #define MT32EMU_SERVICE_I_V3 \ - void (*setNicePanningEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isNicePanningEnabled)(mt32emu_const_context context); \ - void (*setNicePartialMixingEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - mt32emu_boolean (*isNicePartialMixingEnabled)(mt32emu_const_context context); \ - void (*preallocateReverbMemory)(mt32emu_const_context context, const mt32emu_boolean enabled); \ - void (*configureMIDIEventQueueSysexStorage)(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); + void (MT32EMU_C_CALL *setNicePanningEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNicePanningEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setNicePartialMixingEnabled)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isNicePartialMixingEnabled)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *preallocateReverbMemory)(mt32emu_const_context context, const mt32emu_boolean enabled); \ + void (MT32EMU_C_CALL *configureMIDIEventQueueSysexStorage)(mt32emu_const_context context, const mt32emu_bit32u storage_buffer_size); + +#define MT32EMU_SERVICE_I_V4 \ + size_t (MT32EMU_C_CALL *getMachineIDs)(const char **machine_ids, size_t machine_ids_size); \ + size_t (MT32EMU_C_CALL *getROMIDs)(const char **rom_ids, size_t rom_ids_size, const char *machine_id); \ + mt32emu_return_code (MT32EMU_C_CALL *identifyROMData)(mt32emu_rom_info *rom_info, const mt32emu_bit8u *data, size_t data_size, const char *machine_id); \ + mt32emu_return_code (MT32EMU_C_CALL *identifyROMFile)(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id); \ +\ + mt32emu_return_code (MT32EMU_C_CALL *mergeAndAddROMData)(mt32emu_context context, const mt32emu_bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const mt32emu_bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest); \ + mt32emu_return_code (MT32EMU_C_CALL *mergeAndAddROMFiles)(mt32emu_context context, const char *part1_filename, const char *part2_filename); \ + mt32emu_return_code (MT32EMU_C_CALL *addMachineROMFile)(mt32emu_context context, const char *machine_id, const char *filename); + +#define MT32EMU_SERVICE_I_V5 \ + mt32emu_boolean (MT32EMU_C_CALL *getDisplayState)(mt32emu_const_context context, char *target_buffer, const mt32emu_boolean narrow_lcd); \ + void (MT32EMU_C_CALL *setMainDisplayMode)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setDisplayCompatibility)(mt32emu_const_context context, mt32emu_boolean old_mt32_compatibility_enabled); \ + mt32emu_boolean (MT32EMU_C_CALL *isDisplayOldMT32Compatible)(mt32emu_const_context context); \ + mt32emu_boolean (MT32EMU_C_CALL *isDefaultDisplayOldMT32Compatible)(mt32emu_const_context context); \ + void (MT32EMU_C_CALL *setPartVolumeOverride)(mt32emu_const_context context, mt32emu_bit8u part_number, mt32emu_bit8u volume_override); \ + mt32emu_bit8u (MT32EMU_C_CALL *getPartVolumeOverride)(mt32emu_const_context context, mt32emu_bit8u part_number); + +#define MT32EMU_SERVICE_I_V6 \ + mt32emu_boolean (MT32EMU_C_CALL *getSoundGroupName)(mt32emu_const_context context, char *sound_group_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); \ + mt32emu_boolean (MT32EMU_C_CALL *getSoundName)(mt32emu_const_context context, char *sound_name, mt32emu_bit8u timbre_group, mt32emu_bit8u timbre_number); typedef struct { MT32EMU_SERVICE_I_V0 @@ -334,6 +392,33 @@ typedef struct { MT32EMU_SERVICE_I_V3 } mt32emu_service_i_v3; +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 +} mt32emu_service_i_v4; + +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 + MT32EMU_SERVICE_I_V5 +} mt32emu_service_i_v5; + +typedef struct { + MT32EMU_SERVICE_I_V0 + MT32EMU_SERVICE_I_V1 + MT32EMU_SERVICE_I_V2 + MT32EMU_SERVICE_I_V3 + MT32EMU_SERVICE_I_V4 + MT32EMU_SERVICE_I_V5 + MT32EMU_SERVICE_I_V6 +} mt32emu_service_i_v6; + /** * Extensible interface for all the library services. * Union intended to view an interface of any subsequent version as any parent interface not requiring a cast. @@ -344,11 +429,17 @@ union mt32emu_service_i { const mt32emu_service_i_v1 *v1; const mt32emu_service_i_v2 *v2; const mt32emu_service_i_v3 *v3; + const mt32emu_service_i_v4 *v4; + const mt32emu_service_i_v5 *v5; + const mt32emu_service_i_v6 *v6; }; #undef MT32EMU_SERVICE_I_V0 #undef MT32EMU_SERVICE_I_V1 #undef MT32EMU_SERVICE_I_V2 #undef MT32EMU_SERVICE_I_V3 +#undef MT32EMU_SERVICE_I_V4 +#undef MT32EMU_SERVICE_I_V5 +#undef MT32EMU_SERVICE_I_V6 #endif /* #ifndef MT32EMU_C_TYPES_H */ diff --git a/src/sound/munt/c_interface/cpp_interface.h b/src/sound/munt/c_interface/cpp_interface.h index 82fa44b2e..d22897b74 100644 --- a/src/sound/munt/c_interface/cpp_interface.h +++ b/src/sound/munt/c_interface/cpp_interface.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -41,10 +41,17 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_library_version_string i.v0->getLibraryVersionString #define mt32emu_get_stereo_output_samplerate i.v0->getStereoOutputSamplerate #define mt32emu_get_best_analog_output_mode iV1()->getBestAnalogOutputMode +#define mt32emu_get_machine_ids iV4()->getMachineIDs +#define mt32emu_get_rom_ids iV4()->getROMIDs +#define mt32emu_identify_rom_data iV4()->identifyROMData +#define mt32emu_identify_rom_file iV4()->identifyROMFile #define mt32emu_create_context i.v0->createContext #define mt32emu_free_context i.v0->freeContext #define mt32emu_add_rom_data i.v0->addROMData #define mt32emu_add_rom_file i.v0->addROMFile +#define mt32emu_merge_and_add_rom_data iV4()->mergeAndAddROMData +#define mt32emu_merge_and_add_rom_files iV4()->mergeAndAddROMFiles +#define mt32emu_add_machine_rom_file iV4()->addMachineROMFile #define mt32emu_get_rom_info i.v0->getROMInfo #define mt32emu_set_partial_count i.v0->setPartialCount #define mt32emu_set_analog_output_mode i.v0->setAnalogOutputMode @@ -91,6 +98,8 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_output_gain i.v0->getOutputGain #define mt32emu_set_reverb_output_gain i.v0->setReverbOutputGain #define mt32emu_get_reverb_output_gain i.v0->getReverbOutputGain +#define mt32emu_set_part_volume_override iV5()->setPartVolumeOverride +#define mt32emu_get_part_volume_override iV5()->getPartVolumeOverride #define mt32emu_set_reversed_stereo_enabled i.v0->setReversedStereoEnabled #define mt32emu_is_reversed_stereo_enabled i.v0->isReversedStereoEnabled #define mt32emu_set_nice_amp_ramp_enabled iV2()->setNiceAmpRampEnabled @@ -110,7 +119,14 @@ mt32emu_service_i mt32emu_get_service_i(); #define mt32emu_get_partial_states i.v0->getPartialStates #define mt32emu_get_playing_notes i.v0->getPlayingNotes #define mt32emu_get_patch_name i.v0->getPatchName +#define mt32emu_get_sound_group_name iV6()->getSoundGroupName +#define mt32emu_get_sound_name iV6()->getSoundName #define mt32emu_read_memory i.v0->readMemory +#define mt32emu_get_display_state iV5()->getDisplayState +#define mt32emu_set_main_display_mode iV5()->setMainDisplayMode +#define mt32emu_set_display_compatibility iV5()->setDisplayCompatibility +#define mt32emu_is_display_old_mt32_compatible iV5()->isDisplayOldMT32Compatible +#define mt32emu_is_default_display_old_mt32_compatible iV5()->isDefaultDisplayOldMT32Compatible #else // #if MT32EMU_API_TYPE == 2 @@ -123,7 +139,7 @@ namespace MT32Emu { namespace CppInterfaceImpl { static const mt32emu_report_handler_i NULL_REPORT_HANDLER = { NULL }; -static mt32emu_report_handler_i getReportHandlerThunk(); +static mt32emu_report_handler_i getReportHandlerThunk(mt32emu_report_handler_version); static mt32emu_midi_receiver_i getMidiReceiverThunk(); } @@ -136,8 +152,8 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk(); * See c_types.h and c_interface.h for description of the corresponding interface methods. */ -// Defines the interface for handling reported events. -// Corresponds to the current version of mt32emu_report_handler_i interface. +// Defines the interface for handling reported events (initial version). +// Corresponds to the mt32emu_report_handler_i_v0 interface. class IReportHandler { public: virtual void printDebug(const char *fmt, va_list list) = 0; @@ -159,6 +175,17 @@ protected: ~IReportHandler() {} }; +// Extends IReportHandler, so that the client may supply callbacks for reporting signals about updated display state. +// Corresponds to the mt32emu_report_handler_i_v1 interface. +class IReportHandlerV1 : public IReportHandler { +public: + virtual void onLCDStateUpdated() = 0; + virtual void onMidiMessageLEDStateUpdated(bool ledState) = 0; + +protected: + ~IReportHandlerV1() {} +}; + // Defines the interface for receiving MIDI messages generated by MIDI stream parser. // Corresponds to the current version of mt32emu_midi_receiver_i interface. class IMidiReceiver { @@ -196,14 +223,24 @@ public: Bit32u getStereoOutputSamplerate(const AnalogOutputMode analog_output_mode) { return mt32emu_get_stereo_output_samplerate(static_cast(analog_output_mode)); } AnalogOutputMode getBestAnalogOutputMode(const double target_samplerate) { return static_cast(mt32emu_get_best_analog_output_mode(target_samplerate)); } + size_t getMachineIDs(const char **machine_ids, size_t machine_ids_size) { return mt32emu_get_machine_ids(machine_ids, machine_ids_size); } + size_t getROMIDs(const char **rom_ids, size_t rom_ids_size, const char *machine_id) { return mt32emu_get_rom_ids(rom_ids, rom_ids_size, machine_id); } + mt32emu_return_code identifyROMData(mt32emu_rom_info *rom_info, const Bit8u *data, size_t data_size, const char *machine_id) { return mt32emu_identify_rom_data(rom_info, data, data_size, machine_id); } + mt32emu_return_code identifyROMFile(mt32emu_rom_info *rom_info, const char *filename, const char *machine_id) { return mt32emu_identify_rom_file(rom_info, filename, machine_id); } + // Context-dependent methods mt32emu_context getContext() { return c; } void createContext(mt32emu_report_handler_i report_handler = CppInterfaceImpl::NULL_REPORT_HANDLER, void *instance_data = NULL) { freeContext(); c = mt32emu_create_context(report_handler, instance_data); } - void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(), &report_handler); } + void createContext(IReportHandler &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_0), &report_handler); } + void createContext(IReportHandlerV1 &report_handler) { createContext(CppInterfaceImpl::getReportHandlerThunk(MT32EMU_REPORT_HANDLER_VERSION_1), &report_handler); } void freeContext() { if (c != NULL) { mt32emu_free_context(c); c = NULL; } } mt32emu_return_code addROMData(const Bit8u *data, size_t data_size, const mt32emu_sha1_digest *sha1_digest = NULL) { return mt32emu_add_rom_data(c, data, data_size, sha1_digest); } mt32emu_return_code addROMFile(const char *filename) { return mt32emu_add_rom_file(c, filename); } + mt32emu_return_code mergeAndAddROMData(const Bit8u *part1_data, size_t part1_data_size, const Bit8u *part2_data, size_t part2_data_size) { return mt32emu_merge_and_add_rom_data(c, part1_data, part1_data_size, NULL, part2_data, part2_data_size, NULL); } + mt32emu_return_code mergeAndAddROMData(const Bit8u *part1_data, size_t part1_data_size, const mt32emu_sha1_digest *part1_sha1_digest, const Bit8u *part2_data, size_t part2_data_size, const mt32emu_sha1_digest *part2_sha1_digest) { return mt32emu_merge_and_add_rom_data(c, part1_data, part1_data_size, part1_sha1_digest, part2_data, part2_data_size, part2_sha1_digest); } + mt32emu_return_code mergeAndAddROMFiles(const char *part1_filename, const char *part2_filename) { return mt32emu_merge_and_add_rom_files(c, part1_filename, part2_filename); } + mt32emu_return_code addMachineROMFile(const char *machine_id, const char *filename) { return mt32emu_add_machine_rom_file(c, machine_id, filename); } void getROMInfo(mt32emu_rom_info *rom_info) { mt32emu_get_rom_info(c, rom_info); } void setPartialCount(const Bit32u partial_count) { mt32emu_set_partial_count(c, partial_count); } void setAnalogOutputMode(const AnalogOutputMode analog_output_mode) { mt32emu_set_analog_output_mode(c, static_cast(analog_output_mode)); } @@ -258,6 +295,9 @@ public: void setReverbOutputGain(float gain) { mt32emu_set_reverb_output_gain(c, gain); } float getReverbOutputGain() { return mt32emu_get_reverb_output_gain(c); } + void setPartVolumeOverride(Bit8u part_number, Bit8u volume_override) { mt32emu_set_part_volume_override(c, part_number, volume_override); } + Bit8u getPartVolumeOverride(Bit8u part_number) { return mt32emu_get_part_volume_override(c, part_number); } + void setReversedStereoEnabled(const bool enabled) { mt32emu_set_reversed_stereo_enabled(c, enabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } bool isReversedStereoEnabled() { return mt32emu_is_reversed_stereo_enabled(c) != MT32EMU_BOOL_FALSE; } @@ -282,8 +322,17 @@ public: void getPartialStates(Bit8u *partial_states) { mt32emu_get_partial_states(c, partial_states); } Bit32u getPlayingNotes(Bit8u part_number, Bit8u *keys, Bit8u *velocities) { return mt32emu_get_playing_notes(c, part_number, keys, velocities); } const char *getPatchName(Bit8u part_number) { return mt32emu_get_patch_name(c, part_number); } + bool getSoundGroupName(char *soundGroupName, Bit8u timbreGroup, Bit8u timbreNumber) { return mt32emu_get_sound_group_name(c, soundGroupName, timbreGroup, timbreNumber) != MT32EMU_BOOL_FALSE; } + bool getSoundName(char *soundName, Bit8u timbreGroup, Bit8u timbreNumber) { return mt32emu_get_sound_name(c, soundName, timbreGroup, timbreNumber) != MT32EMU_BOOL_FALSE; } void readMemory(Bit32u addr, Bit32u len, Bit8u *data) { mt32emu_read_memory(c, addr, len, data); } + bool getDisplayState(char *target_buffer, const bool narrow_lcd) { return mt32emu_get_display_state(c, target_buffer, narrow_lcd ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE) != MT32EMU_BOOL_FALSE; } + void setMainDisplayMode() { mt32emu_set_main_display_mode(c); } + + void setDisplayCompatibility(const bool oldMT32CompatibilityEnabled) { mt32emu_set_display_compatibility(c, oldMT32CompatibilityEnabled ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE); } + bool isDisplayOldMT32Compatible() { return mt32emu_is_display_old_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + bool isDefaultDisplayOldMT32Compatible() { return mt32emu_is_default_display_old_mt32_compatible(c) != MT32EMU_BOOL_FALSE; } + private: #if MT32EMU_API_TYPE == 2 const mt32emu_service_i i; @@ -294,108 +343,137 @@ private: const mt32emu_service_i_v1 *iV1() { return (getVersionID() < MT32EMU_SERVICE_VERSION_1) ? NULL : i.v1; } const mt32emu_service_i_v2 *iV2() { return (getVersionID() < MT32EMU_SERVICE_VERSION_2) ? NULL : i.v2; } const mt32emu_service_i_v3 *iV3() { return (getVersionID() < MT32EMU_SERVICE_VERSION_3) ? NULL : i.v3; } + const mt32emu_service_i_v4 *iV4() { return (getVersionID() < MT32EMU_SERVICE_VERSION_4) ? NULL : i.v4; } + const mt32emu_service_i_v5 *iV5() { return (getVersionID() < MT32EMU_SERVICE_VERSION_5) ? NULL : i.v5; } + const mt32emu_service_i_v6 *iV6() { return (getVersionID() < MT32EMU_SERVICE_VERSION_6) ? NULL : i.v6; } #endif + + Service(const Service &); // prevent copy-construction + Service& operator=(const Service &); // prevent assignment }; namespace CppInterfaceImpl { -static mt32emu_report_handler_version getReportHandlerVersionID(mt32emu_report_handler_i) { - return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; -} +static mt32emu_report_handler_version MT32EMU_C_CALL getReportHandlerVersionID(mt32emu_report_handler_i); -static void printDebug(void *instance_data, const char *fmt, va_list list) { +static void MT32EMU_C_CALL printDebug(void *instance_data, const char *fmt, va_list list) { static_cast(instance_data)->printDebug(fmt, list); } -static void onErrorControlROM(void *instance_data) { +static void MT32EMU_C_CALL onErrorControlROM(void *instance_data) { static_cast(instance_data)->onErrorControlROM(); } -static void onErrorPCMROM(void *instance_data) { +static void MT32EMU_C_CALL onErrorPCMROM(void *instance_data) { static_cast(instance_data)->onErrorPCMROM(); } -static void showLCDMessage(void *instance_data, const char *message) { +static void MT32EMU_C_CALL showLCDMessage(void *instance_data, const char *message) { static_cast(instance_data)->showLCDMessage(message); } -static void onMIDIMessagePlayed(void *instance_data) { +static void MT32EMU_C_CALL onMIDIMessagePlayed(void *instance_data) { static_cast(instance_data)->onMIDIMessagePlayed(); } -static mt32emu_boolean onMIDIQueueOverflow(void *instance_data) { +static mt32emu_boolean MT32EMU_C_CALL onMIDIQueueOverflow(void *instance_data) { return static_cast(instance_data)->onMIDIQueueOverflow() ? MT32EMU_BOOL_TRUE : MT32EMU_BOOL_FALSE; } -static void onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) { +static void MT32EMU_C_CALL onMIDISystemRealtime(void *instance_data, mt32emu_bit8u system_realtime) { static_cast(instance_data)->onMIDISystemRealtime(system_realtime); } -static void onDeviceReset(void *instance_data) { +static void MT32EMU_C_CALL onDeviceReset(void *instance_data) { static_cast(instance_data)->onDeviceReset(); } -static void onDeviceReconfig(void *instance_data) { +static void MT32EMU_C_CALL onDeviceReconfig(void *instance_data) { static_cast(instance_data)->onDeviceReconfig(); } -static void onNewReverbMode(void *instance_data, mt32emu_bit8u mode) { +static void MT32EMU_C_CALL onNewReverbMode(void *instance_data, mt32emu_bit8u mode) { static_cast(instance_data)->onNewReverbMode(mode); } -static void onNewReverbTime(void *instance_data, mt32emu_bit8u time) { +static void MT32EMU_C_CALL onNewReverbTime(void *instance_data, mt32emu_bit8u time) { static_cast(instance_data)->onNewReverbTime(time); } -static void onNewReverbLevel(void *instance_data, mt32emu_bit8u level) { +static void MT32EMU_C_CALL onNewReverbLevel(void *instance_data, mt32emu_bit8u level) { static_cast(instance_data)->onNewReverbLevel(level); } -static void onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) { +static void MT32EMU_C_CALL onPolyStateChanged(void *instance_data, mt32emu_bit8u part_num) { static_cast(instance_data)->onPolyStateChanged(part_num); } -static void onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) { +static void MT32EMU_C_CALL onProgramChanged(void *instance_data, mt32emu_bit8u part_num, const char *sound_group_name, const char *patch_name) { static_cast(instance_data)->onProgramChanged(part_num, sound_group_name, patch_name); } -static mt32emu_report_handler_i getReportHandlerThunk() { - static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = { - getReportHandlerVersionID, - printDebug, - onErrorControlROM, - onErrorPCMROM, - showLCDMessage, - onMIDIMessagePlayed, - onMIDIQueueOverflow, - onMIDISystemRealtime, - onDeviceReset, - onDeviceReconfig, - onNewReverbMode, - onNewReverbTime, - onNewReverbLevel, - onPolyStateChanged, - onProgramChanged - }; - - static const mt32emu_report_handler_i REPORT_HANDLER_THUNK = { &REPORT_HANDLER_V0_THUNK }; - - return REPORT_HANDLER_THUNK; +static void MT32EMU_C_CALL onLCDStateUpdated(void *instance_data) { + static_cast(instance_data)->onLCDStateUpdated(); } -static mt32emu_midi_receiver_version getMidiReceiverVersionID(mt32emu_midi_receiver_i) { +static void MT32EMU_C_CALL onMidiMessageLEDStateUpdated(void *instance_data, mt32emu_boolean led_state) { + static_cast(instance_data)->onMidiMessageLEDStateUpdated(led_state != MT32EMU_BOOL_FALSE); +} + +#define MT32EMU_REPORT_HANDLER_V0_THUNK \ + getReportHandlerVersionID, \ + printDebug, \ + onErrorControlROM, \ + onErrorPCMROM, \ + showLCDMessage, \ + onMIDIMessagePlayed, \ + onMIDIQueueOverflow, \ + onMIDISystemRealtime, \ + onDeviceReset, \ + onDeviceReconfig, \ + onNewReverbMode, \ + onNewReverbTime, \ + onNewReverbLevel, \ + onPolyStateChanged, \ + onProgramChanged + +static const mt32emu_report_handler_i_v0 REPORT_HANDLER_V0_THUNK = { + MT32EMU_REPORT_HANDLER_V0_THUNK +}; + +static const mt32emu_report_handler_i_v1 REPORT_HANDLER_V1_THUNK = { + MT32EMU_REPORT_HANDLER_V0_THUNK, + onLCDStateUpdated, + onMidiMessageLEDStateUpdated +}; + +#undef MT32EMU_REPORT_HANDLER_THUNK_V0 + +static mt32emu_report_handler_version MT32EMU_C_CALL getReportHandlerVersionID(mt32emu_report_handler_i thunk) { + if (thunk.v0 == &REPORT_HANDLER_V0_THUNK) return MT32EMU_REPORT_HANDLER_VERSION_0; + return MT32EMU_REPORT_HANDLER_VERSION_CURRENT; +} + +static mt32emu_report_handler_i getReportHandlerThunk(mt32emu_report_handler_version versionID) { + mt32emu_report_handler_i thunk; + if (versionID == MT32EMU_REPORT_HANDLER_VERSION_0) thunk.v0 = &REPORT_HANDLER_V0_THUNK; + else thunk.v1 = &REPORT_HANDLER_V1_THUNK; + return thunk; +} + +static mt32emu_midi_receiver_version MT32EMU_C_CALL getMidiReceiverVersionID(mt32emu_midi_receiver_i) { return MT32EMU_MIDI_RECEIVER_VERSION_CURRENT; } -static void handleShortMessage(void *instance_data, const mt32emu_bit32u message) { +static void MT32EMU_C_CALL handleShortMessage(void *instance_data, const mt32emu_bit32u message) { static_cast(instance_data)->handleShortMessage(message); } -static void handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) { +static void MT32EMU_C_CALL handleSysex(void *instance_data, const mt32emu_bit8u stream[], const mt32emu_bit32u length) { static_cast(instance_data)->handleSysex(stream, length); } -static void handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) { +static void MT32EMU_C_CALL handleSystemRealtimeMessage(void *instance_data, const mt32emu_bit8u realtime) { static_cast(instance_data)->handleSystemRealtimeMessage(realtime); } @@ -424,10 +502,17 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_get_library_version_string #undef mt32emu_get_stereo_output_samplerate #undef mt32emu_get_best_analog_output_mode +#undef mt32emu_get_machine_ids +#undef mt32emu_get_rom_ids +#undef mt32emu_identify_rom_data +#undef mt32emu_identify_rom_file #undef mt32emu_create_context #undef mt32emu_free_context #undef mt32emu_add_rom_data #undef mt32emu_add_rom_file +#undef mt32emu_merge_and_add_rom_data +#undef mt32emu_merge_and_add_rom_files +#undef mt32emu_add_machine_rom_file #undef mt32emu_get_rom_info #undef mt32emu_set_partial_count #undef mt32emu_set_analog_output_mode @@ -493,7 +578,14 @@ static mt32emu_midi_receiver_i getMidiReceiverThunk() { #undef mt32emu_get_partial_states #undef mt32emu_get_playing_notes #undef mt32emu_get_patch_name +#undef mt32emu_get_sound_group_name +#undef mt32emu_get_sound_name #undef mt32emu_read_memory +#undef mt32emu_get_display_state +#undef mt32emu_set_main_display_mode +#undef mt32emu_set_display_compatibility +#undef mt32emu_is_display_old_mt32_compatible +#undef mt32emu_is_default_display_old_mt32_compatible #endif // #if MT32EMU_API_TYPE == 2 diff --git a/src/sound/munt/config.h b/src/sound/munt/config.h index e41d4664b..906c23d56 100644 --- a/src/sound/munt/config.h +++ b/src/sound/munt/config.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2017 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -18,9 +18,9 @@ #ifndef MT32EMU_CONFIG_H #define MT32EMU_CONFIG_H -#define MT32EMU_VERSION "2.4.0" +#define MT32EMU_VERSION "2.7.0" #define MT32EMU_VERSION_MAJOR 2 -#define MT32EMU_VERSION_MINOR 4 +#define MT32EMU_VERSION_MINOR 7 #define MT32EMU_VERSION_PATCH 0 /* Library Exports Configuration diff --git a/src/sound/munt/config.h.in b/src/sound/munt/config.h.in index 48dfb0076..b120f407c 100644 --- a/src/sound/munt/config.h.in +++ b/src/sound/munt/config.h.in @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -33,6 +33,35 @@ * is exported, and thus the client application may ONLY use MT32EMU_API_TYPE 2. * 3: All the available API types are provided by the library build. */ -#define MT32EMU_EXPORTS_TYPE @libmt32emu_EXPORTS_TYPE@ +#define MT32EMU_EXPORTS_TYPE @libmt32emu_EXPORTS_TYPE@ +/* Type of library build. + * + * For shared library builds, MT32EMU_SHARED is defined, so that compiler-specific attributes are assigned + * to all the exported symbols as appropriate. MT32EMU_SHARED is undefined for static library builds. + */ +@libmt32emu_SHARED_DEFINITION@ + +/* Whether the library is built as a shared object with a version tag to enable runtime version checks. */ +#define MT32EMU_WITH_VERSION_TAGGING @libmt32emu_RUNTIME_VERSION_CHECK@ + +/* Automatic runtime check of the shared library version in client applications. + * + * When the shared library is built with version tagging enabled, the client application may rely on an automatic + * version check that ensures forward compatibility. See VersionTagging.h for more info. + * 0: Disables the automatic runtime version check in the client application. Implied for static library builds + * and when version tagging is not used in a shared object. + * 1: Enables an automatic runtime version check in client applications that utilise low-level C++ API, + * i.e. when MT32EMU_API_TYPE 0. Client applications that rely on the C-compatible API are supposed + * to check the version of the shared object by other means (e.g. using versioned C symbols, etc.). + * 2: Enables an automatic runtime version check for C++ and C client applications. + */ +#if MT32EMU_WITH_VERSION_TAGGING +# ifndef MT32EMU_RUNTIME_VERSION_CHECK +# define MT32EMU_RUNTIME_VERSION_CHECK @libmt32emu_RUNTIME_VERSION_CHECK@ +# endif +#else +# undef MT32EMU_RUNTIME_VERSION_CHECK #endif + +#endif /* #ifndef MT32EMU_CONFIG_H */ diff --git a/src/sound/munt/globals.h b/src/sound/munt/globals.h index 243ff82ae..86ac1ca5b 100644 --- a/src/sound/munt/globals.h +++ b/src/sound/munt/globals.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -20,27 +20,35 @@ #include "config.h" -/* Support for compiling shared library. */ +/* Support for compiling shared library. + * MT32EMU_SHARED and mt32emu_EXPORTS are defined when building a shared library. + * MT32EMU_SHARED should also be defined for Windows platforms that provides for a small performance benefit, + * and it _must_ be defined along with MT32EMU_RUNTIME_VERSION_CHECK when using MSVC. + */ #ifdef MT32EMU_SHARED -#if defined _WIN32 || defined __CYGWIN__ -#ifdef _MSC_VER -#ifdef mt32emu_EXPORTS -#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) -#else /* #ifdef mt32emu_EXPORTS */ -#define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) -#endif /* #ifdef mt32emu_EXPORTS */ -#else /* #ifdef _MSC_VER */ -#ifdef mt32emu_EXPORTS -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) -#else /* #ifdef mt32emu_EXPORTS */ -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) -#endif /* #ifdef mt32emu_EXPORTS */ -#endif /* #ifdef _MSC_VER */ -#else /* #if defined _WIN32 || defined __CYGWIN__ */ -#define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) -#endif /* #if defined _WIN32 || defined __CYGWIN__ */ +# if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ +# ifdef _MSC_VER +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllexport) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE _declspec(dllimport) +# endif /* #ifdef mt32emu_EXPORTS */ +# else /* #ifdef _MSC_VER */ +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllexport)) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((dllimport)) +# endif /* #ifdef mt32emu_EXPORTS */ +# endif /* #ifdef _MSC_VER */ +# else /* #if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ */ +# ifdef mt32emu_EXPORTS +# define MT32EMU_EXPORT_ATTRIBUTE __attribute__ ((visibility("default"))) +# else /* #ifdef mt32emu_EXPORTS */ +# define MT32EMU_EXPORT_ATTRIBUTE +# endif /* #ifdef mt32emu_EXPORTS */ +# endif /* #if defined _WIN32 || defined __CYGWIN__ || defined __OS2__ */ #else /* #ifdef MT32EMU_SHARED */ -#define MT32EMU_EXPORT_ATTRIBUTE +# define MT32EMU_EXPORT_ATTRIBUTE #endif /* #ifdef MT32EMU_SHARED */ #if MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2 @@ -49,6 +57,33 @@ #define MT32EMU_EXPORT MT32EMU_EXPORT_ATTRIBUTE #endif +/* Facilitates easier tracking of the library version when an external symbol was introduced. + * Particularly useful for shared library builds on POSIX systems that support symbol versioning, + * so that the version map file can be generated automatically. + */ +#define MT32EMU_EXPORT_V(symbol_version_tag) MT32EMU_EXPORT + +/* Helpers for compile-time version checks */ + +/* Encodes the given version components to a single integer value to simplify further checks. */ +#define MT32EMU_VERSION_INT(major, minor, patch) ((major << 16) | (minor << 8) | patch) + +/* The version of this library build, as an integer. */ +#define MT32EMU_CURRENT_VERSION_INT MT32EMU_VERSION_INT(MT32EMU_VERSION_MAJOR, MT32EMU_VERSION_MINOR, MT32EMU_VERSION_PATCH) + +/* Compares the current library version with the given version components. Intended for feature checks. */ +#define MT32EMU_VERSION_ATLEAST(major, minor, patch) (MT32EMU_CURRENT_VERSION_INT >= MT32EMU_VERSION_INT(major, minor, patch)) + +/* Implements a simple version check that ensures full API compatibility of this library build + * with the application requirements. The latter can be derived from the versions of used public symbols. + * + * Note: This macro is intended for a quick compile-time check. To ensure compatibility of an application + * linked with a shared library, an automatic version check can be engaged with help of the build option + * libmt32emu_WITH_VERSION_TAGGING. For a fine-grained feature checking in run-time, see functions + * mt32emu_get_library_version_int and Synth::getLibraryVersionInt. + */ +#define MT32EMU_IS_COMPATIBLE(major, minor) (MT32EMU_VERSION_MAJOR == major && MT32EMU_VERSION_MINOR >= minor) + /* Useful constants */ /* Sample rate to use in mixing. With the progress of development, we've found way too many thing dependent. diff --git a/src/sound/munt/internals.h b/src/sound/munt/internals.h index 8a609546c..1b3ad0de2 100644 --- a/src/sound/munt/internals.h +++ b/src/sound/munt/internals.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/mmath.h b/src/sound/munt/mmath.h index a66fad566..3164c7bfc 100644 --- a/src/sound/munt/mmath.h +++ b/src/sound/munt/mmath.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/mt32emu.h b/src/sound/munt/mt32emu.h index cfb50fb28..571b25571 100644 --- a/src/sound/munt/mt32emu.h +++ b/src/sound/munt/mt32emu.h @@ -1,5 +1,5 @@ /* Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Dean Beeler, Jerome Fisher - * Copyright (C) 2011-2020 Dean Beeler, Jerome Fisher, Sergey V. Mikayev + * Copyright (C) 2011-2022 Dean Beeler, Jerome Fisher, Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by @@ -37,28 +37,23 @@ */ #ifdef MT32EMU_API_TYPE -#if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=0 -#elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=1 -#elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) -#error Incompatible setting MT32EMU_API_TYPE=2 -#elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) -#error Incompatible setting MT32EMU_API_TYPE=3 -#endif +# if MT32EMU_API_TYPE == 0 && (MT32EMU_EXPORTS_TYPE == 1 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=0 +# elif MT32EMU_API_TYPE == 1 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=1 +# elif MT32EMU_API_TYPE == 2 && (MT32EMU_EXPORTS_TYPE == 0) +# error Incompatible setting MT32EMU_API_TYPE=2 +# elif MT32EMU_API_TYPE == 3 && (MT32EMU_EXPORTS_TYPE == 0 || MT32EMU_EXPORTS_TYPE == 2) +# error Incompatible setting MT32EMU_API_TYPE=3 +# endif #else /* #ifdef MT32EMU_API_TYPE */ -#if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 -#define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE -#else -#define MT32EMU_API_TYPE 0 -#endif +# if 0 < MT32EMU_EXPORTS_TYPE && MT32EMU_EXPORTS_TYPE < 3 +# define MT32EMU_API_TYPE MT32EMU_EXPORTS_TYPE +# else +# define MT32EMU_API_TYPE 0 +# endif #endif /* #ifdef MT32EMU_API_TYPE */ -/* MT32EMU_SHARED should be defined when building shared library, especially for Windows platforms. */ -/* -#define MT32EMU_SHARED -*/ - #include "globals.h" #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 @@ -79,6 +74,14 @@ #include "MidiStreamParser.h" #include "SampleRateConverter.h" +#if MT32EMU_RUNTIME_VERSION_CHECK == 1 +#include "VersionTagging.h" +#endif + #endif /* #if !defined(__cplusplus) || MT32EMU_API_TYPE == 1 */ +#if MT32EMU_RUNTIME_VERSION_CHECK == 2 +#include "VersionTagging.h" +#endif + #endif /* #ifndef MT32EMU_MT32EMU_H */ diff --git a/src/sound/munt/srchelper/InternalResampler.cpp b/src/sound/munt/srchelper/InternalResampler.cpp index 56bd1ac05..4e6a5a748 100644 --- a/src/sound/munt/srchelper/InternalResampler.cpp +++ b/src/sound/munt/srchelper/InternalResampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/InternalResampler.h b/src/sound/munt/srchelper/InternalResampler.h index cf08c8261..a80cc7dc4 100644 --- a/src/sound/munt/srchelper/InternalResampler.h +++ b/src/sound/munt/srchelper/InternalResampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/SamplerateAdapter.cpp b/src/sound/munt/srchelper/SamplerateAdapter.cpp index 2a417ed2e..aeb695a2e 100644 --- a/src/sound/munt/srchelper/SamplerateAdapter.cpp +++ b/src/sound/munt/srchelper/SamplerateAdapter.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/SamplerateAdapter.h b/src/sound/munt/srchelper/SamplerateAdapter.h index eed9799a9..6da9b64aa 100644 --- a/src/sound/munt/srchelper/SamplerateAdapter.h +++ b/src/sound/munt/srchelper/SamplerateAdapter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/SoxrAdapter.cpp b/src/sound/munt/srchelper/SoxrAdapter.cpp index a88c133ec..754f55d56 100644 --- a/src/sound/munt/srchelper/SoxrAdapter.cpp +++ b/src/sound/munt/srchelper/SoxrAdapter.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/SoxrAdapter.h b/src/sound/munt/srchelper/SoxrAdapter.h index c6b9d3ade..2abcbdd8a 100644 --- a/src/sound/munt/srchelper/SoxrAdapter.h +++ b/src/sound/munt/srchelper/SoxrAdapter.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/FIRResampler.h b/src/sound/munt/srchelper/srctools/include/FIRResampler.h index 9032131dc..b8e0be1bb 100644 --- a/src/sound/munt/srchelper/srctools/include/FIRResampler.h +++ b/src/sound/munt/srchelper/srctools/include/FIRResampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/FloatSampleProvider.h b/src/sound/munt/srchelper/srctools/include/FloatSampleProvider.h index 4056db373..9b88bced6 100644 --- a/src/sound/munt/srchelper/srctools/include/FloatSampleProvider.h +++ b/src/sound/munt/srchelper/srctools/include/FloatSampleProvider.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/IIR2xResampler.h b/src/sound/munt/srchelper/srctools/include/IIR2xResampler.h index ea150f9db..247d57568 100644 --- a/src/sound/munt/srchelper/srctools/include/IIR2xResampler.h +++ b/src/sound/munt/srchelper/srctools/include/IIR2xResampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/LinearResampler.h b/src/sound/munt/srchelper/srctools/include/LinearResampler.h index 0e30ea2e9..a55f4ae67 100644 --- a/src/sound/munt/srchelper/srctools/include/LinearResampler.h +++ b/src/sound/munt/srchelper/srctools/include/LinearResampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/ResamplerModel.h b/src/sound/munt/srchelper/srctools/include/ResamplerModel.h index b7a64f02e..622ccbb18 100644 --- a/src/sound/munt/srchelper/srctools/include/ResamplerModel.h +++ b/src/sound/munt/srchelper/srctools/include/ResamplerModel.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/ResamplerStage.h b/src/sound/munt/srchelper/srctools/include/ResamplerStage.h index edd7678c1..2d8507037 100644 --- a/src/sound/munt/srchelper/srctools/include/ResamplerStage.h +++ b/src/sound/munt/srchelper/srctools/include/ResamplerStage.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/include/SincResampler.h b/src/sound/munt/srchelper/srctools/include/SincResampler.h index bac844043..8db9f5bb9 100644 --- a/src/sound/munt/srchelper/srctools/include/SincResampler.h +++ b/src/sound/munt/srchelper/srctools/include/SincResampler.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/src/FIRResampler.cpp b/src/sound/munt/srchelper/srctools/src/FIRResampler.cpp index b5ab5585c..0df8acd16 100644 --- a/src/sound/munt/srchelper/srctools/src/FIRResampler.cpp +++ b/src/sound/munt/srchelper/srctools/src/FIRResampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/src/IIR2xResampler.cpp b/src/sound/munt/srchelper/srctools/src/IIR2xResampler.cpp index 98f7a3a5b..a09b48d9e 100644 --- a/src/sound/munt/srchelper/srctools/src/IIR2xResampler.cpp +++ b/src/sound/munt/srchelper/srctools/src/IIR2xResampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/src/LinearResampler.cpp b/src/sound/munt/srchelper/srctools/src/LinearResampler.cpp index 1ca143a38..18224833b 100644 --- a/src/sound/munt/srchelper/srctools/src/LinearResampler.cpp +++ b/src/sound/munt/srchelper/srctools/src/LinearResampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/src/ResamplerModel.cpp b/src/sound/munt/srchelper/srctools/src/ResamplerModel.cpp index 2a7f75822..504024629 100644 --- a/src/sound/munt/srchelper/srctools/src/ResamplerModel.cpp +++ b/src/sound/munt/srchelper/srctools/src/ResamplerModel.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/sound/munt/srchelper/srctools/src/SincResampler.cpp b/src/sound/munt/srchelper/srctools/src/SincResampler.cpp index 60a18256c..de9048093 100644 --- a/src/sound/munt/srchelper/srctools/src/SincResampler.cpp +++ b/src/sound/munt/srchelper/srctools/src/SincResampler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2015-2020 Sergey V. Mikayev +/* Copyright (C) 2015-2022 Sergey V. Mikayev * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 75b847dde..e072adadc 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -397,7 +397,7 @@ endif ifeq ($(MUNT), y) OPTS += -DUSE_MUNT MUNTOBJ := midi_mt32.o \ - Analog.o BReverbModel.o File.o FileStream.o LA32Ramp.o \ + Analog.o BReverbModel.o Display.o File.o FileStream.o LA32Ramp.o \ LA32FloatWaveGenerator.o LA32WaveGenerator.o \ MidiStreamParser.o Part.o Partial.o PartialManager.o \ Poly.o ROMInfo.o SampleRateConverter.o \