diff options
author | Wohlstand <admin@wohlnet.ru> | 2025-03-29 00:44:52 +0300 |
---|---|---|
committer | Wohlstand <admin@wohlnet.ru> | 2025-03-29 00:44:52 +0300 |
commit | e92a4d7285197ab2868aa36f975d73ceee915bea (patch) | |
tree | b5e3af6e6c58145a62ec6394a40e9e65d26a5fcf /utils | |
parent | 7afda0483ab0af9db624052321b42c3d2a245e75 (diff) | |
download | libADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.tar.gz libADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.tar.bz2 libADLMIDI-e92a4d7285197ab2868aa36f975d73ceee915bea.zip |
Refactored DOS support
Diffstat (limited to 'utils')
-rw-r--r-- | utils/midiplay/adlmidiplay.cpp | 972 |
1 files changed, 597 insertions, 375 deletions
diff --git a/utils/midiplay/adlmidiplay.cpp b/utils/midiplay/adlmidiplay.cpp index f5f30d8..fff74d5 100644 --- a/utils/midiplay/adlmidiplay.cpp +++ b/utils/midiplay/adlmidiplay.cpp @@ -29,12 +29,14 @@ #include <cstdlib> #include <cstring> #include <cstdarg> -#include <deque> #include <vector> #include <algorithm> -#include <signal.h> #include <stdint.h> -#include "utf8main.h" +#ifndef ADLMIDI_ENABLE_HW_DOS +# include <deque> +# include <signal.h> +# include "utf8main.h" +#endif #if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) # ifdef ADLMIDI_USE_SDL2 @@ -265,7 +267,7 @@ void mch_delay(int32_t msec) #include <adlmidi.h> -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS #ifndef OUTPUT_WAVE_ONLY # include "audio.h" @@ -406,7 +408,7 @@ const char* audio_format_to_str(int format, int is_msb) return "UNK"; } -#endif //HARDWARE_OPL3 +#endif //ADLMIDI_ENABLE_HW_DOS const char* volume_model_to_str(int vm) { @@ -475,7 +477,7 @@ static void printError(const char *err, const char *what = NULL) } static int stop = 0; -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS static void sighandler(int dum) { switch(dum) @@ -528,7 +530,7 @@ static void debugPrint(void * /*userdata*/, const char *fmt, ...) } } -#ifdef HARDWARE_OPL3 +#ifdef ADLMIDI_ENABLE_HW_DOS static inline void keyWait() { std::printf("\n<press any key to continue...>"); @@ -551,7 +553,7 @@ static void printBanks() for(int a = 0; a < banksCount; ++a) { std::printf("%10s%2u = %s\n", a ? "" : "Banks:", a, banknames[a]); -#ifdef HARDWARE_OPL3 +#ifdef ADLMIDI_ENABLE_HW_DOS if(((a - 15) % 23 == 0 && a != 0)) keyWait(); #endif @@ -624,7 +626,7 @@ static struct TimeCounter double realTimeStart; #endif -#ifdef HARDWARE_OPL3 +#ifdef ADLMIDI_ENABLE_HW_DOS unsigned newTimerFreq; unsigned timerPeriod; int haveYield; @@ -652,7 +654,7 @@ static struct TimeCounter printsCounter = 0; complete_prev = -1; -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS printsCounterPeriod = 1; #else printsCounterPeriod = 20; @@ -666,7 +668,7 @@ static struct TimeCounter #endif } -#ifdef HARDWARE_OPL3 +#ifdef ADLMIDI_ENABLE_HW_DOS void initDosTimer() { # ifdef __DJGPP__ @@ -986,7 +988,7 @@ static void runHWSerialLoop(ADL_MIDIPlayer *myDevice) #endif // ADLMIDI_ENABLE_HW_SERIAL -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS # ifndef OUTPUT_WAVE_ONLY static int runAudioLoop(ADL_MIDIPlayer *myDevice, AudioOutputSpec &spec) { @@ -1233,7 +1235,7 @@ static int runWaveOutLoopLoop(ADL_MIDIPlayer *myDevice, const std::string &musPa return 0; } -#else // HARDWARE_OPL3 +#else // ADLMIDI_ENABLE_HW_DOS static void runDOSLoop(ADL_MIDIPlayer *myDevice) { double tick_delay = 0.0; @@ -1272,374 +1274,592 @@ static void runDOSLoop(ADL_MIDIPlayer *myDevice) adl_panic(myDevice); //Shut up all sustaining notes } -#endif // HARDWARE_OPL3 +#endif // ADLMIDI_ENABLE_HW_DOS -int main(int argc, char **argv) + + +static struct Args { - std::fprintf(stdout, "==========================================\n" - #ifdef HARDWARE_OPL3 - " libADLMIDI demo utility (HW OPL)\n" - #else - " libADLMIDI demo utility\n" - #endif - "==========================================\n\n"); - flushout(stdout); + int setHwVibrato; + int setHwTremolo; + int setScaleMods; - if(argc >= 2 && std::string(argv[1]) == "--list-banks") - { - printBanks(); - return 0; - } + int setBankNo; + std::string setBankFile; + int setNum4op; + int setNumChips; - if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h") - { - std::printf( - "Usage: adlmidi <midifilename> [ <options> ] \n" - " [ <bank> [ <numchips> [ <numfourops>] ] ]\n" - // " -p Enables adlib percussion instrument mode\n" - " -t Enables tremolo amplification mode\n" - " -v Enables vibrato amplification mode\n" - " -s Enables scaling of modulator volumes\n" - " -vm <num> Chooses one of volume models: \n" - " 0 auto (default)\n" - " 1 Generic\n" - " 2 Native OPL3\n" - " 3 DMX\n" - " 4 Apogee Sound System\n" - " 5 9x SB16\n" - " 6 DMX (Fixed AM voices)\n" - " 7 Apogee Sound System (Fixed AM voices)\n" - " 8 Audio Interface Library (AIL)\n" - " 9 9x Generic FM\n" - " 10 HMI Sound Operating System\n" - ); -#ifdef HARDWARE_OPL3 - keyWait(); -#endif - std::printf( - " -frb Enables full-ranged CC74 XG Brightness controller\n" - " -nl Quit without looping\n" - " -w Write WAV file rather than playing\n" - " -mb Run the test of multibank over embedded. 62, 14, 68, and 74'th banks\n" - " will be combined into one\n" - " --solo <track> Selects a solo track to play\n" - " --only <track1,...,trackN> Selects a subset of tracks to play\n" - " --song <song ID 0...N-1> Selects a song to play (if XMI)\n" - " -ea Enable the auto-arpeggio\n" -#ifndef HARDWARE_OPL3 - " -fp Enables full-panning stereo support\n" - " --gain <value> Set the gaining factor (default 2.0)\n" - " --emu-nuked Uses Nuked OPL3 v 1.8 emulator\n" - " --emu-nuked7 Uses Nuked OPL3 v 1.7.4 emulator\n" - " --emu-dosbox Uses DosBox 0.74 OPL3 emulator\n" - " --emu-opal Uses Opal OPL3 emulator\n" - " --emu-java Uses Java OPL3 emulator\n" - " --emu-esfmu Uses ESFMu OPL3/ESFM emulator\n" -#endif -#ifdef HARDWARE_OPL3 - "\n" - " --time-freq <hz> Uses a different time value, DEFAULT 209\n" - " --list-banks Print a lost of all built-in FM banks\n" -#endif - "\n" - "Where <bank> - number of embeeded bank or filepath to custom WOPL bank file\n" - "\n" - "Note: To create WOPL bank files use OPL Bank Editor you can get here: \n" - "https://github.com/Wohlstand/OPL3BankEditor\n" - "\n" - ); + std::string musPath; + + int setFullRangeBright; -#ifndef HARDWARE_OPL3 - printBanks(); + int enableFullPanning; + +#ifndef OUTPUT_WAVE_ONLY + bool recordWave; + int loopEnabled; #endif - return 0; - } - unsigned int sampleRate = 44100; -#if !defined(HARDWARE_OPL3) && !defined(OUTPUT_WAVE_ONLY) + unsigned int sampleRate; + +#if !defined(ADLMIDI_ENABLE_HW_DOS) && !defined(OUTPUT_WAVE_ONLY) //const unsigned MaxSamplesAtTime = 512; // 512=dbopl limitation // How long is SDL buffer, in seconds? // The smaller the value, the more often SDL_AudioCallBack() // is called. - const double AudioBufferLength = 0.08; + const double AudioBufferLength; AudioOutputSpec spec; - spec.freq = sampleRate; - spec.format = ADLMIDI_SampleType_S16; - spec.is_msb = 0; - spec.channels = 2; - spec.samples = uint16_t((double)spec.freq * AudioBufferLength); -#endif // !HARDWARE_OPL3 && !OUTPUT_WAVE_ONLY - -#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) - bool hwSerial = false; - std::string serialName; - unsigned serialBaud = 115200; - unsigned serialProto = ADLMIDI_SerialProtocol_RetroWaveOPL3; #endif - ADL_MIDIPlayer *myDevice; - - //Initialize libADLMIDI and create the instance (you can initialize multiple of them!) - myDevice = adl_init(sampleRate); - if(myDevice == NULL) - { - printError("Failed to init MIDI device!\n"); - return 1; - } +#ifdef ADLMIDI_ENABLE_HW_DOS + ADL_UInt16 setHwAddress; + int setChipType; +#endif - //Set internal debug messages hook to print all libADLMIDI's internal debug messages - adl_setDebugMessageHook(myDevice, debugPrint, NULL); +#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) + bool hwSerial; + std::string serialName; + unsigned serialBaud; + unsigned serialProto; +#endif /* * Set library options by parsing of command line arguments */ - bool multibankFromEnbededTest = false; + bool multibankFromEnbededTest; + + int autoArpeggioEnabled; + int chanAlloc; + +#ifndef ADLMIDI_ENABLE_HW_DOS + int emulator; +#endif + int volumeModel; + + size_t soloTrack; + int songNumLoad; + std::vector<size_t> onlyTracks; + + + Args() : + setHwVibrato(-1) + , setHwTremolo(-1) + , setScaleMods(-1) + , setBankNo(-1) + , setNum4op(-1) + , setNumChips(-1) + + , setFullRangeBright(-1) + , enableFullPanning(-1) #ifndef OUTPUT_WAVE_ONLY - bool recordWave = false; - int loopEnabled = 1; + , recordWave(false) + , loopEnabled(1) #endif - int autoArpeggioEnabled = 0; - int chanAlloc = ADLMIDI_ChanAlloc_AUTO; -#ifndef HARDWARE_OPL3 - int emulator = ADLMIDI_EMU_NUKED; + , sampleRate(44100) + +#if !defined(ADLMIDI_ENABLE_HW_DOS) && !defined(OUTPUT_WAVE_ONLY) + , AudioBufferLength(0.08) #endif - int volumeModel = ADLMIDI_VolumeModel_AUTO; - size_t soloTrack = ~(size_t)0; - int songNumLoad = -1; - std::vector<size_t> onlyTracks; +#ifdef ADLMIDI_ENABLE_HW_DOS + , setHwAddress(0) + , setChipType(ADLMIDI_DOS_ChipAuto) +#endif -#if !defined(HARDWARE_OPL3) && !defined(OUTPUT_WAVE_ONLY) - g_audioFormat.type = ADLMIDI_SampleType_S16; - g_audioFormat.containerSize = sizeof(int16_t); - g_audioFormat.sampleOffset = sizeof(int16_t) * 2; +#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) + , hwSerial(false) + , serialBaud(115200) + , serialProto(ADLMIDI_SerialProtocol_RetroWaveOPL3) #endif + , multibankFromEnbededTest(false) + , autoArpeggioEnabled(0) + , chanAlloc(ADLMIDI_ChanAlloc_AUTO) - while(argc > 2) +#ifndef ADLMIDI_ENABLE_HW_DOS + , emulator(ADLMIDI_EMU_NUKED) +#endif + , volumeModel(ADLMIDI_VolumeModel_AUTO) + , soloTrack(~(size_t)0) + , songNumLoad(-1) { - bool had_option = false; +#if !defined(ADLMIDI_ENABLE_HW_DOS) && !defined(OUTPUT_WAVE_ONLY) + spec.freq = sampleRate; + spec.format = ADLMIDI_SampleType_S16; + spec.is_msb = 0; + spec.channels = 2; + spec.samples = uint16_t((double)spec.freq * AudioBufferLength); +#endif + } - if(!std::strcmp("-p", argv[2])) - fprintf(stderr, "Warning: -p argument is deprecated and useless!\n"); //adl_setPercMode(myDevice, 1);//Turn on AdLib percussion mode - else if(!std::strcmp("-v", argv[2])) - adl_setHVibrato(myDevice, 1);//Force turn on deep vibrato -#if !defined(OUTPUT_WAVE_ONLY) && !defined(HARDWARE_OPL3) - else if(!std::strcmp("-w", argv[2])) + int parseArgs(int argc, char **argv_arr, bool *quit) + { + const char* const* argv = argv_arr; + + if(argc >= 2 && std::string(argv[1]) == "--list-banks") { - //Current Wave output implementation allows only SINT16 output - g_audioFormat.type = ADLMIDI_SampleType_S16; - g_audioFormat.containerSize = sizeof(int16_t); - g_audioFormat.sampleOffset = sizeof(int16_t) * 2; - recordWave = true;//Record library output into WAV file + printBanks(); + *quit = true; + return 0; } - else if(!std::strcmp("-s8", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_S8; - else if(!std::strcmp("-u8", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_U8; - else if(!std::strcmp("-s16", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_S16; - else if(!std::strcmp("-u16", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_U16; - else if(!std::strcmp("-s32", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_S32; - else if(!std::strcmp("-f32", argv[2]) && !recordWave) - spec.format = ADLMIDI_SampleType_F32; -#endif - - else if(!std::strcmp("-t", argv[2])) - adl_setHTremolo(myDevice, 1);//Force turn on deep tremolo - - else if(!std::strcmp("-frb", argv[2])) - adl_setFullRangeBrightness(myDevice, 1);//Turn on a full-ranged XG CC74 Brightness -#ifndef OUTPUT_WAVE_ONLY - else if(!std::strcmp("-nl", argv[2])) - loopEnabled = 0; //Enable loop -#endif - else if(!std::strcmp("-na", argv[2])) // Deprecated - autoArpeggioEnabled = 0; //Enable auto-arpeggio - else if(!std::strcmp("-ea", argv[2])) - autoArpeggioEnabled = 1; //Enable auto-arpeggio - -#ifndef HARDWARE_OPL3 - else if(!std::strcmp("--emu-nuked", argv[2])) - emulator = ADLMIDI_EMU_NUKED; - else if(!std::strcmp("--emu-nuked7", argv[2])) - emulator = ADLMIDI_EMU_NUKED_174; - else if(!std::strcmp("--emu-dosbox", argv[2])) - emulator = ADLMIDI_EMU_DOSBOX; - else if(!std::strcmp("--emu-opal", argv[2])) - emulator = ADLMIDI_EMU_OPAL; - else if(!std::strcmp("--emu-java", argv[2])) - emulator = ADLMIDI_EMU_JAVA; - else if(!std::strcmp("--emu-esfmu", argv[2])) - emulator = ADLMIDI_EMU_ESFMu; - else if(!std::strcmp("--emu-mame-opl2", argv[2])) - emulator = ADLMIDI_EMU_MAME_OPL2; - else if(!std::strcmp("--emu-ymfm-opl2", argv[2])) - emulator = ADLMIDI_EMU_YMFM_OPL2; - else if(!std::strcmp("--emu-ymfm-opl3", argv[2])) - emulator = ADLMIDI_EMU_YMFM_OPL3; -#endif -#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) - else if(!std::strcmp("--serial", argv[2])) + if(argc < 2 || std::string(argv[1]) == "--help" || std::string(argv[1]) == "-h") { - if(argc <= 3) + const char *help_text = + "Usage: adlmidi <midifilename> [ <options> ] \n" + " [ <bank> [ <numchips> [ <numfourops>] ] ]\n" + "\n" + //------------------------------------------------------------------------------| + "Where <bank> - number of embeeded bank or filepath to custom WOPL bank file\n" + "Where <numchips> - total number of parallel emulated chips running to\n" + " extend poliphony (on hardware 1 chip can only be used)\n" + "Where <numfourops> - total number of 4-operator channels on OPL3 chips.\n" + " By defaullt value depends on the used bank.\n" + "\n" + // " -p Enables adlib percussion instrument mode\n" + " -t Enables force deep tremolo mode (Default: depens on bank)\n" + " -v Enables force deep vibrato mode (Default: depens on bank)\n" + " -s Enables scaling of modulator volumes\n" + " -vm <num> Chooses one of volume models: \n" + " 0 auto (default)\n" + " 1 Generic\n" + " 2 Native OPL3\n" + " 3 DMX\n" + " 4 Apogee Sound System\n" + " 5 9x SB16\n" + " 6 DMX (Fixed AM voices)\n" + " 7 Apogee Sound System (Fixed AM voices)\n" + " 8 Audio Interface Library (AIL)\n" + " 9 9x Generic FM\n" + " 10 HMI Sound Operating System\n" + " -frb Enables full-ranged CC74 XG Brightness controller\n" + " -nl Quit without looping\n" + " -w Write WAV file rather than playing\n" + //------------------------------------------------------------------------------| + " -mb Run the test of multibank over embedded. 62, 14, 68, and 74'th banks\n" + " will be combined into one\n" + " --solo <track> Selects a solo track to play\n" + " --only <track1,...,trackN> Selects a subset of tracks to play\n" + " --song <song ID 0...N-1> Selects a song to play (if XMI)\n" + " -ea Enable the auto-arpeggio\n" +#ifndef ADLMIDI_ENABLE_HW_DOS + " -fp Enables full-panning stereo support\n" + " --gain <value> Set the gaining factor (default 2.0)\n" + " --emu-nuked Uses Nuked OPL3 v 1.8 emulator\n" + " --emu-nuked7 Uses Nuked OPL3 v 1.7.4 emulator\n" + " --emu-dosbox Uses DosBox 0.74 OPL3 emulator\n" + " --emu-opal Uses Opal OPL3 emulator\n" + " --emu-java Uses Java OPL3 emulator\n" + " --emu-esfmu Uses ESFMu OPL3/ESFM emulator\n" +#else + "\n" + //------------------------------------------------------------------------------| + " --time-freq <hz> Uses a different time value, DEFAULT 209\n" + " --type <opl2|opl3> Type of hardware chip ('opl2' or 'opl3'), default AUTO\n" + " --addr <hex> Hardware address of the chip, DEFAULT 0x388\n" + " --list-banks Print a lost of all built-in FM banks\n" +#endif + "\n" + //------------------------------------------------------------------------------| + "Note: To create WOPL bank files use OPL Bank Editor you can get here: \n" + "https://github.com/Wohlstand/OPL3BankEditor\n" +#ifdef ADLMIDI_ENABLE_HW_DOS + "\n\n" + //------------------------------------------------------------------------------| + "TIP: If you have the SoundBlaster Pro with Dual OPL2, you can use two cips\n" + "if you specify the base address of sound card itself (for example 0x220) and\n" + "set two chips. However, keep a note that SBPro's chips were designed for the\n" + "Stereo, not for polyphony, and therefore, you will hear voices randomly going\n" + "between left and right speaker.\n" +#endif + "\n" + ; + +#ifdef ADLMIDI_ENABLE_HW_DOS + int lines = 5; + const char *cur = help_text; + + for(; *cur != '\0'; ++cur) { - printError("The option --serial requires an argument!\n"); - return 1; + char c = *cur; + std::putc(c, stdout); + if(c == '\n') + lines++; + + if(lines >= 23) + { + keyWait(); + lines = 0; + } } - had_option = true; - hwSerial = true; - serialName = argv[3]; +#else + std::printf("%s", help_text); + flushout(stdout); +#endif + +#ifndef ADLMIDI_ENABLE_HW_DOS + printBanks(); +#endif + *quit = true; + return 0; } - else if(!std::strcmp("--serial-baud", argv[2])) + + musPath = argv[1]; + + while(argc > 2) { - if(argc <= 3) + bool had_option = false; + + if(!std::strcmp("-p", argv[2])) + fprintf(stderr, "Warning: -p argument is deprecated and useless!\n"); //adl_setPercMode(myDevice, 1);//Turn on AdLib percussion mode + else if(!std::strcmp("-v", argv[2])) + setHwVibrato = 1; + +#if !defined(OUTPUT_WAVE_ONLY) && !defined(ADLMIDI_ENABLE_HW_DOS) + else if(!std::strcmp("-w", argv[2])) { - printError("The option --serial-baud requires an argument!\n"); - return 1; + //Current Wave output implementation allows only SINT16 output + g_audioFormat.type = ADLMIDI_SampleType_S16; + g_audioFormat.containerSize = sizeof(int16_t); + g_audioFormat.sampleOffset = sizeof(int16_t) * 2; + recordWave = true;//Record library output into WAV file } - had_option = true; - serialBaud = std::strtol(argv[3], NULL, 10); - } - else if(!std::strcmp("--serial-proto", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("-s8", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_S8; + else if(!std::strcmp("-u8", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_U8; + else if(!std::strcmp("-s16", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_S16; + else if(!std::strcmp("-u16", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_U16; + else if(!std::strcmp("-s32", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_S32; + else if(!std::strcmp("-f32", argv[2]) && !recordWave) + spec.format = ADLMIDI_SampleType_F32; +#endif + + else if(!std::strcmp("-t", argv[2])) + setHwTremolo = 1; + + else if(!std::strcmp("-frb", argv[2])) + setFullRangeBright = 1; + +#ifndef OUTPUT_WAVE_ONLY + else if(!std::strcmp("-nl", argv[2])) + loopEnabled = 0; //Enable loop +#endif + else if(!std::strcmp("-na", argv[2])) // Deprecated + autoArpeggioEnabled = 0; //Enable auto-arpeggio + else if(!std::strcmp("-ea", argv[2])) + autoArpeggioEnabled = 1; //Enable auto-arpeggio + +#ifndef ADLMIDI_ENABLE_HW_DOS + else if(!std::strcmp("--emu-nuked", argv[2])) + emulator = ADLMIDI_EMU_NUKED; + else if(!std::strcmp("--emu-nuked7", argv[2])) + emulator = ADLMIDI_EMU_NUKED_174; + else if(!std::strcmp("--emu-dosbox", argv[2])) + emulator = ADLMIDI_EMU_DOSBOX; + else if(!std::strcmp("--emu-opal", argv[2])) + emulator = ADLMIDI_EMU_OPAL; + else if(!std::strcmp("--emu-java", argv[2])) + emulator = ADLMIDI_EMU_JAVA; + else if(!std::strcmp("--emu-esfmu", argv[2])) + emulator = ADLMIDI_EMU_ESFMu; + else if(!std::strcmp("--emu-mame-opl2", argv[2])) + emulator = ADLMIDI_EMU_MAME_OPL2; + else if(!std::strcmp("--emu-ymfm-opl2", argv[2])) + emulator = ADLMIDI_EMU_YMFM_OPL2; + else if(!std::strcmp("--emu-ymfm-opl3", argv[2])) + emulator = ADLMIDI_EMU_YMFM_OPL3; +#endif + +#if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) + else if(!std::strcmp("--serial", argv[2])) { - printError("The option --serial-proto requires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option --serial requires an argument!\n"); + *quit = true; + return 1; + } + had_option = true; + hwSerial = true; + serialName = argv[3]; + } + else if(!std::strcmp("--serial-baud", argv[2])) + { + if(argc <= 3) + { + printError("The option --serial-baud requires an argument!\n"); + *quit = true; + return 1; + } + had_option = true; + serialBaud = std::strtol(argv[3], NULL, 10); + } + else if(!std::strcmp("--serial-proto", argv[2])) + { + if(argc <= 3) + { + printError("The option --serial-proto requires an argument!\n"); + *quit = true; + return 1; + } + had_option = true; + serialProto = std::strtol(argv[3], NULL, 10); } - had_option = true; - serialProto = std::strtol(argv[3], NULL, 10); - } #endif - else if(!std::strcmp("-fp", argv[2])) - adl_setSoftPanEnabled(myDevice, 1); - else if(!std::strcmp("-mb", argv[2])) - multibankFromEnbededTest = true; - else if(!std::strcmp("-s", argv[2])) - adl_setScaleModulators(myDevice, 1);//Turn on modulators scaling by volume + else if(!std::strcmp("-fp", argv[2])) + enableFullPanning = 1; + else if(!std::strcmp("-mb", argv[2])) + multibankFromEnbededTest = true; + else if(!std::strcmp("-s", argv[2])) + setScaleMods = 1; #ifndef ADLMIDI_HW_OPL - else if(!std::strcmp("--gain", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("--gain", argv[2])) { - printError("The option --gain requires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option --gain requires an argument!\n"); + *quit = true; + return 1; + } + had_option = true; + g_gaining = std::atof(argv[3]); } - had_option = true; - g_gaining = std::atof(argv[3]); - } -#endif // HARDWARE_OPL3 +#endif // ADLMIDI_ENABLE_HW_DOS -#ifdef HARDWARE_OPL3 - else if(!std::strcmp("--time-freq", argv[2])) - { - if(argc <= 3) +#ifdef ADLMIDI_ENABLE_HW_DOS + else if(!std::strcmp("--time-freq", argv[2])) { - printError("The option --time-freq requires an argument!\n"); - return 1; - } + if(argc <= 3) + { + printError("The option --time-freq requires an argument!\n"); + *quit = true; + return 1; + } + + unsigned timerFreq = std::strtoul(argv[3], NULL, 0); + if(timerFreq == 0) + { + printError("The option --time-freq requires a non-zero integer argument!\n"); + *quit = true; + return 1; + } + + s_timeCounter.setDosTimerHZ(timerFreq); - unsigned timerFreq = std::strtoul(argv[3], NULL, 0); - if(timerFreq == 0) + had_option = true; + } + else if(!std::strcmp("--type", argv[2])) { - printError("The option --time-freq requires a non-zero integer argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option --type requires an argument!\n"); + *quit = true; + return 1; + } + + if(!std::strcmp(argv[3], "opl3") || !std::strcmp(argv[3], "OPL3")) + setChipType = ADLMIDI_DOS_ChipOPL3; + else if(!std::strcmp(argv[3], "opl2") || !std::strcmp(argv[3], "OPL2")) + setChipType = ADLMIDI_DOS_ChipOPL2; + else + { + printError("Given invalid option for --type: accepted 'opl2' or 'opl3'!\n"); + *quit = true; + return 1; + } + + had_option = true; } + else if(!std::strcmp("--addr", argv[2])) + { + if(argc <= 3) + { + printError("The option --addr requires an argument!\n"); + *quit = true; + return 1; + } - s_timeCounter.setDosTimerHZ(timerFreq); + setHwAddress = std::strtoul(argv[3], NULL, 0); + if(setHwAddress == 0) + { + printError("The option --time-freq requires a non-zero integer argument!\n"); + *quit = true; + return 1; + } - had_option = true; - } + had_option = true; + } #endif - else if(!std::strcmp("-vm", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("-vm", argv[2])) { - printError("The option -vm requires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option -vm requires an argument!\n"); + *quit = true; + return 1; + } + volumeModel = std::strtol(argv[3], NULL, 10); + had_option = true; } - volumeModel = std::strtol(argv[3], NULL, 10); - had_option = true; - } - else if(!std::strcmp("-ca", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("-ca", argv[2])) { - printError("The option -carequires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option -carequires an argument!\n"); + *quit = true; + return 1; + } + + chanAlloc = std::strtol(argv[3], NULL, 10); + had_option = true; } - chanAlloc = std::strtol(argv[3], NULL, 10); - had_option = true; - } - else if(!std::strcmp("--solo", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("--solo", argv[2])) { - printError("The option --solo requires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option --solo requires an argument!\n"); + *quit = true; + return 1; + } + soloTrack = std::strtoul(argv[3], NULL, 10); + had_option = true; } - soloTrack = std::strtoul(argv[3], NULL, 10); - had_option = true; - } - else if(!std::strcmp("--song", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("--song", argv[2])) { - printError("The option --song requires an argument!\n"); - return 1; + if(argc <= 3) + { + printError("The option --song requires an argument!\n"); + *quit = true; + return 1; + } + songNumLoad = std::strtol(argv[3], NULL, 10); + had_option = true; } - songNumLoad = std::strtol(argv[3], NULL, 10); - had_option = true; - } - else if(!std::strcmp("--only", argv[2])) - { - if(argc <= 3) + else if(!std::strcmp("--only", argv[2])) { - printError("The option --only requires an argument!\n"); - return 1; - } + if(argc <= 3) + { + printError("The option --only requires an argument!\n"); + *quit = true; + return 1; + } - const char *strp = argv[3]; - unsigned long value; - unsigned size; - bool err = std::sscanf(strp, "%lu%n", &value, &size) != 1; - while(!err && *(strp += size)) - { + const char *strp = argv[3]; + unsigned long value; + unsigned size; + bool err = std::sscanf(strp, "%lu%n", &value, &size) != 1; + while(!err && *(strp += size)) + { + onlyTracks.push_back(value); + err = std::sscanf(strp, ",%lu%n", &value, &size) != 1; + } + if(err) + { + printError("Invalid argument to --only!\n"); + *quit = true; + return 1; + } onlyTracks.push_back(value); - err = std::sscanf(strp, ",%lu%n", &value, &size) != 1; - } - if(err) - { - printError("Invalid argument to --only!\n"); - return 1; + had_option = true; } - onlyTracks.push_back(value); - had_option = true; + else + break; + + argv += (had_option ? 2 : 1); + argc -= (had_option ? 2 : 1); } - else break; + if(argc >= 3) + { + if(is_number(argv[2])) + setBankNo = std::atoi(argv[2]); + else + setBankFile = argv[2]; + } + + if(argc >= 4) + setNumChips = std::atoi(argv[3]); - std::copy(argv + (had_option ? 4 : 3), - argv + argc, - argv + 2); - argc -= (had_option ? 2 : 1); + if(argc >= 5) + setNum4op = std::atoi(argv[4]); + + *quit = false; + return 0; } +} s_devSetup; + + +int main(int argc, char **argv) +{ + std::fprintf(stdout, "==========================================\n" + #ifdef ADLMIDI_ENABLE_HW_DOS + " libADLMIDI demo utility (HW OPL)\n" + #else + " libADLMIDI demo utility\n" + #endif + "==========================================\n\n"); + flushout(stdout); + + bool doQuit = false; + int parseRet = s_devSetup.parseArgs(argc, argv, &doQuit); + + if(doQuit) + return parseRet; + + ADL_MIDIPlayer *myDevice; + +#ifdef ADLMIDI_ENABLE_HW_DOS + if(s_devSetup.setHwAddress > 0 || s_devSetup.setChipType != ADLMIDI_DOS_ChipAuto) + adl_switchDOSHW(s_devSetup.setChipType, s_devSetup.setHwAddress); +#endif + + //Initialize libADLMIDI and create the instance (you can initialize multiple of them!) + myDevice = adl_init(s_devSetup.sampleRate); + if(myDevice == NULL) + { + printError("Failed to init MIDI device!\n"); + return 1; + } + + //Set internal debug messages hook to print all libADLMIDI's internal debug messages + adl_setDebugMessageHook(myDevice, debugPrint, NULL); + +#if !defined(ADLMIDI_ENABLE_HW_DOS) && !defined(OUTPUT_WAVE_ONLY) + g_audioFormat.type = ADLMIDI_SampleType_S16; + g_audioFormat.containerSize = sizeof(int16_t); + g_audioFormat.sampleOffset = sizeof(int16_t) * 2; +#endif + + if(s_devSetup.setHwVibrato >= 0) + adl_setHVibrato(myDevice, s_devSetup.setHwVibrato);//Force turn on deep vibrato + + if(s_devSetup.setHwTremolo >= 0) + adl_setHTremolo(myDevice, s_devSetup.setHwTremolo);//Force turn on deep tremolo + + if(s_devSetup.setScaleMods >= 0) + adl_setScaleModulators(myDevice, s_devSetup.setScaleMods);//Turn on modulators scaling by volume + + if(s_devSetup.setFullRangeBright >= 0) + adl_setFullRangeBrightness(myDevice, s_devSetup.setFullRangeBright);//Turn on a full-ranged XG CC74 Brightness + + if(s_devSetup.enableFullPanning >= 0) + adl_setSoftPanEnabled(myDevice, s_devSetup.enableFullPanning); #ifndef OUTPUT_WAVE_ONLY //Turn loop on/off (for WAV recording loop must be disabled!) - adl_setLoopEnabled(myDevice, recordWave ? 0 : loopEnabled); + adl_setLoopEnabled(myDevice, s_devSetup.recordWave ? 0 : s_devSetup.loopEnabled); #endif - adl_setAutoArpeggio(myDevice, autoArpeggioEnabled); - adl_setChannelAllocMode(myDevice, chanAlloc); + adl_setAutoArpeggio(myDevice, s_devSetup.autoArpeggioEnabled); + adl_setChannelAllocMode(myDevice, s_devSetup.chanAlloc); #ifdef DEBUG_TRACE_ALL_EVENTS //Hook all MIDI events are ticking while generating an output buffer @@ -1648,60 +1868,59 @@ int main(int argc, char **argv) #endif #if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) - if(hwSerial) - adl_switchSerialHW(myDevice, serialName.c_str(), serialBaud, serialProto); + if(s_devSetup.hwSerial) + adl_switchSerialHW(myDevice, s_devSetup.serialName.c_str(), s_devSetup.serialBaud, s_devSetup.serialProto); else #endif -#ifndef HARDWARE_OPL3 - adl_switchEmulator(myDevice, emulator); +#ifndef ADLMIDI_ENABLE_HW_DOS + adl_switchEmulator(myDevice, s_devSetup.emulator); #endif std::fprintf(stdout, " - Library version %s\n", adl_linkedLibraryVersion()); -#ifdef HARDWARE_OPL3 - std::fprintf(stdout, " - Hardware OPL3 chip in use\n"); +#ifdef ADLMIDI_ENABLE_HW_DOS + std::fprintf(stdout, " - Hardware chip in use: %s\n", adl_chipEmulatorName(myDevice)); #elif defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) - if(hwSerial) - std::fprintf(stdout, " - %s [device %s] in use\n", adl_chipEmulatorName(myDevice), serialName.c_str()); + if(s_devSetup.hwSerial) + std::fprintf(stdout, " - %s [device %s] in use\n", adl_chipEmulatorName(myDevice), s_devSetup.serialName.c_str()); else std::fprintf(stdout, " - %s Emulator in use\n", adl_chipEmulatorName(myDevice)); #else std::fprintf(stdout, " - %s Emulator in use\n", adl_chipEmulatorName(myDevice)); #endif - if(argc >= 3) + if(s_devSetup.setBankNo >= 0) { - if(is_number(argv[2])) + //Choose one of embedded banks + if(adl_setBank(myDevice, s_devSetup.setBankNo) != 0) { - int bankno = std::atoi(argv[2]); - //Choose one of embedded banks - if(adl_setBank(myDevice, bankno) != 0) - { - printError(adl_errorInfo(myDevice), "Can't set an embedded bank"); - adl_close(myDevice); - return 1; - } - std::fprintf(stdout, " - Use embedded bank #%d [%s]\n", bankno, adl_getBankNames()[bankno]); + printError(adl_errorInfo(myDevice), "Can't set an embedded bank"); + adl_close(myDevice); + return 1; } - else + + std::fprintf(stdout, " - Use embedded bank #%d [%s]\n", s_devSetup.setBankNo, adl_getBankNames()[s_devSetup.setBankNo]); + } + else if(!s_devSetup.setBankFile.empty()) + { + std::fprintf(stdout, " - Use custom bank [%s]...", s_devSetup.setBankFile.c_str()); + flushout(stdout); + + //Open external bank file (WOPL format is supported) + //to create or edit them, use OPL3 Bank Editor you can take here https://github.com/Wohlstand/OPL3BankEditor + if(adl_openBankFile(myDevice, s_devSetup.setBankFile.c_str()) != 0) { - std::string bankPath = argv[2]; - std::fprintf(stdout, " - Use custom bank [%s]...", bankPath.c_str()); + std::fprintf(stdout, "FAILED!\n"); flushout(stdout); - //Open external bank file (WOPL format is supported) - //to create or edit them, use OPL3 Bank Editor you can take here https://github.com/Wohlstand/OPL3BankEditor - if(adl_openBankFile(myDevice, bankPath.c_str()) != 0) - { - std::fprintf(stdout, "FAILED!\n"); - flushout(stdout); - printError(adl_errorInfo(myDevice), "Can't open a custom bank file"); - adl_close(myDevice); - return 1; - } - std::fprintf(stdout, "OK!\n"); + printError(adl_errorInfo(myDevice), "Can't open a custom bank file"); + adl_close(myDevice); + return 1; } + + std::fprintf(stdout, "OK!\n"); } - if(multibankFromEnbededTest) + + if(s_devSetup.multibankFromEnbededTest) { ADL_BankId id[] = { @@ -1736,13 +1955,14 @@ int main(int argc, char **argv) std::fprintf(stdout, " - Ran a test of multibank over embedded\n"); } -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS int numOfChips = 4; - if(argc >= 4) - numOfChips = std::atoi(argv[3]); + + if(s_devSetup.setNumChips >= 0) + numOfChips = s_devSetup.setNumChips; #if defined(ADLMIDI_ENABLE_HW_SERIAL) && !defined(OUTPUT_WAVE_ONLY) - if(hwSerial) + if(s_devSetup.hwSerial) numOfChips = 1; #endif @@ -1753,18 +1973,19 @@ int main(int argc, char **argv) adl_close(myDevice); return 1; } + #else - int numOfChips = 1; - adl_setNumChips(myDevice, numOfChips); + if(s_devSetup.setNumChips >= 0 && s_devSetup.setNumChips <= 2 && s_devSetup.setChipType == ADLMIDI_DOS_ChipOPL2) + adl_setNumChips(myDevice, s_devSetup.setNumChips); #endif - if(volumeModel != ADLMIDI_VolumeModel_AUTO) - adl_setVolumeRangeModel(myDevice, volumeModel); + if(s_devSetup.volumeModel != ADLMIDI_VolumeModel_AUTO) + adl_setVolumeRangeModel(myDevice, s_devSetup.volumeModel); - if(argc >= 5) + if(s_devSetup.setNum4op >= 0) { //Set total count of 4-operator channels between all emulated chips - if(adl_setNumFourOpsChn(myDevice, std::atoi(argv[4])) != 0) + if(adl_setNumFourOpsChn(myDevice, s_devSetup.setNum4op) != 0) { printError(adl_errorInfo(myDevice), "Can't set number of 4-op channels"); adl_close(myDevice); @@ -1778,8 +1999,8 @@ int main(int argc, char **argv) adl_setLoopHooksOnly(myDevice, 1); #endif - if(songNumLoad >= 0) - adl_selectSongNum(myDevice, songNumLoad); + if(s_devSetup.songNumLoad >= 0) + adl_selectSongNum(myDevice, s_devSetup.songNumLoad); #if defined(DEBUG_SONG_SWITCHING) || defined(ENABLE_TERMINAL_HOTKEYS) set_conio_terminal_mode(); @@ -1789,10 +2010,11 @@ int main(int argc, char **argv) # endif #endif - std::string musPath = argv[1]; //Open MIDI file to play - if(adl_openFile(myDevice, musPath.c_str()) != 0) + if(adl_openFile(myDevice, s_devSetup.musPath.c_str()) != 0) { + std::fprintf(stdout, " - File [%s] failed!\n", s_devSetup.musPath.c_str()); + flushout(stdout); printError(adl_errorInfo(myDevice), "Can't open MIDI file"); adl_close(myDevice); return 2; @@ -1804,9 +2026,9 @@ int main(int argc, char **argv) std::fprintf(stdout, " - Volume model: %s\n", volume_model_to_str(adl_getVolumeRangeModel(myDevice))); std::fprintf(stdout, " - Channel allocation mode: %s\n", chanalloc_to_str(adl_getChannelAllocMode(myDevice))); -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS # ifdef ADLMIDI_ENABLE_HW_SERIAL - if(!hwSerial) + if(!s_devSetup.hwSerial) # endif { std::fprintf(stdout, " - Gain level: %g\n", g_gaining); @@ -1814,26 +2036,26 @@ int main(int argc, char **argv) #endif int songsCount = adl_getSongsCount(myDevice); - if(songNumLoad >= 0) - std::fprintf(stdout, " - Attempt to load song number: %d / %d\n", songNumLoad, songsCount); + if(s_devSetup.songNumLoad >= 0) + std::fprintf(stdout, " - Attempt to load song number: %d / %d\n", s_devSetup.songNumLoad, songsCount); else if(songsCount > 0) std::fprintf(stdout, " - File contains %d song(s)\n", songsCount); - if(soloTrack != ~static_cast<size_t>(0)) + if(s_devSetup.soloTrack != ~static_cast<size_t>(0)) { - std::fprintf(stdout, " - Solo track: %lu\n", static_cast<unsigned long>(soloTrack)); - adl_setTrackOptions(myDevice, soloTrack, ADLMIDI_TrackOption_Solo); + std::fprintf(stdout, " - Solo track: %lu\n", static_cast<unsigned long>(s_devSetup.soloTrack)); + adl_setTrackOptions(myDevice, s_devSetup.soloTrack, ADLMIDI_TrackOption_Solo); } - if(!onlyTracks.empty()) + if(!s_devSetup.onlyTracks.empty()) { size_t count = adl_trackCount(myDevice); for(size_t track = 0; track < count; ++track) adl_setTrackOptions(myDevice, track, ADLMIDI_TrackOption_Off); std::fprintf(stdout, " - Only tracks:"); - for(size_t i = 0, n = onlyTracks.size(); i < n; ++i) + for(size_t i = 0, n = s_devSetup.onlyTracks.size(); i < n; ++i) { - size_t track = onlyTracks[i]; + size_t track = s_devSetup.onlyTracks[i]; adl_setTrackOptions(myDevice, track, ADLMIDI_TrackOption_On); std::fprintf(stdout, " %lu", static_cast<unsigned long>(track)); } @@ -1842,46 +2064,46 @@ int main(int argc, char **argv) std::fprintf(stdout, " - Automatic arpeggio is turned %s\n", adl_getAutoArpeggio(myDevice) ? "ON" : "OFF"); - std::fprintf(stdout, " - File [%s] opened!\n", musPath.c_str()); + std::fprintf(stdout, " - File [%s] opened!\n", s_devSetup.musPath.c_str()); flushout(stdout); -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS signal(SIGINT, sighandler); signal(SIGTERM, sighandler); # if !defined(_WIN32) && !defined(__WATCOMC__) signal(SIGHUP, sighandler); # endif -#else // HARDWARE_OPL3 +#else // ADLMIDI_ENABLE_HW_DOS //disable(); s_timeCounter.initDosTimer(); s_timeCounter.flushDosTimer(); //enable(); -#endif//HARDWARE_OPL3 +#endif//ADLMIDI_ENABLE_HW_DOS s_timeCounter.setTotal(adl_totalTimeLength(myDevice)); #ifndef OUTPUT_WAVE_ONLY s_timeCounter.setLoop(adl_loopStartTime(myDevice), adl_loopEndTime(myDevice)); -# ifndef HARDWARE_OPL3 - if(!recordWave) +# ifndef ADLMIDI_ENABLE_HW_DOS + if(!s_devSetup.recordWave) # endif { - std::fprintf(stdout, " - Loop is turned %s\n", loopEnabled ? "ON" : "OFF"); + std::fprintf(stdout, " - Loop is turned %s\n", s_devSetup.loopEnabled ? "ON" : "OFF"); if(s_timeCounter.hasLoop) std::fprintf(stdout, " - Has loop points: %s ... %s\n", s_timeCounter.loopStartHMS, s_timeCounter.loopEndHMS); std::fprintf(stdout, "\n==========================================\n"); flushout(stdout); -# ifndef HARDWARE_OPL3 +# ifndef ADLMIDI_ENABLE_HW_DOS # ifdef ADLMIDI_ENABLE_HW_SERIAL - if(hwSerial) + if(s_devSetup.hwSerial) runHWSerialLoop(myDevice); else # endif { - int ret = runAudioLoop(myDevice, spec); + int ret = runAudioLoop(myDevice, s_devSetup.spec); if (ret != 0) { adl_close(myDevice); @@ -1896,22 +2118,22 @@ int main(int argc, char **argv) } #endif //OUTPUT_WAVE_ONLY -#ifndef HARDWARE_OPL3 +#ifndef ADLMIDI_ENABLE_HW_DOS # ifndef OUTPUT_WAVE_ONLY else # endif //OUTPUT_WAVE_ONLY { - int ret = runWaveOutLoopLoop(myDevice, musPath, sampleRate); + int ret = runWaveOutLoopLoop(myDevice, s_devSetup.musPath, s_devSetup.sampleRate); if(ret != 0) { adl_close(myDevice); return ret; } } -#endif //HARDWARE_OPL3 +#endif //ADLMIDI_ENABLE_HW_DOS -#ifdef HARDWARE_OPL3 +#ifdef ADLMIDI_ENABLE_HW_DOS s_timeCounter.restoreDosTimer(); #endif |