├── .gitignore ├── LICENSE ├── README.md ├── amiga_bin ├── mt32-pi-ctl ├── mt32-pi-ctl.readme └── mt32pi.bmp ├── amiga_src ├── Makefile ├── delay.c ├── midi_dev.c └── version.c ├── atari_bin ├── MT32-PI.TTP ├── MT32-PI.TXT └── mt32pi.bmp ├── atari_src ├── Makefile ├── delay.c ├── midi_dev.c └── mt32pi.bmp ├── common_src ├── delay.h ├── file_io.h ├── getopt.c ├── getopt.h ├── midi_dev.h ├── miniwift.h └── mt32-pi.c ├── dos_bin ├── MT32-PI.EXE ├── MT32-PI.TXT └── mt32pi.bmp ├── dos_src ├── LICENSE.txt ├── MAKEFILE ├── MPU401.C ├── MPU401.H ├── UPXCOMP.BAT ├── delay.c ├── midi_dev.c └── mt32pi.bmp ├── images ├── mt32pictl_1.jpg └── mt32pictl_2.jpg ├── linux_src ├── Makefile ├── completion_scripts │ ├── mt32-pi-ctl.bash │ ├── mt32-pi-ctl.fish │ └── mt32-pi-ctl.zsh ├── delay.c ├── midi_dev.c ├── mt32-pi-ctl.readme └── mt32pi.bmp ├── win32_bin ├── mt32-pi-ctl.exe ├── mt32-pi-ctl.txt └── mt32pi.bmp └── win32_src ├── Makefile ├── delay.c └── midi_dev.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (C) 2021 Andreas Zdziarstek 4 | with parts 5 | Copyright (C) 2020 Dale Whinham (Amiga camd/serial code), 6 | Copyright (C) 2015-2016 Josuah Demangeon (miniwi font), 7 | Copyright (C) 2014-2018 Mateusz Viste (MPU401 lib), 8 | Copyright (C) 2002 Todd C. Miller (getopt). 9 | and Copyright (C) 2000 The NetBSD Foundation, Inc. (getopt) 10 | 11 | See the individual source files for details. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | 1. Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | 19 | 2. Redistributions in binary form must reproduce the above copyright 20 | notice, this list of conditions and the following disclaimer in the 21 | documentation and/or other materials provided with the distribution. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 27 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 28 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 29 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 30 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 31 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 32 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33 | POSSIBILITY OF SUCH DAMAGE. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # An mt32-pi control program for DOS, Amiga, Atari ST, Linux, and Windows 2 | 3 | `MT32-PI.EXE`/`MT32-PI.TTP`/`mt32-pi-ctl` is a control program for the [mt32-pi MIDI synthesizer](https://github.com/dwhinham/mt32-pi) available for DOS PCs, Atari ST and Amiga computers as well as modern systems running Linux and Windows. 4 | 5 | 6 | - [Features](#features) 7 | - [Usage Summary](#usage-summary) 8 | * [Platform-specific options](#platform-specific-options) 9 | + [DOS-specific options](#dos-specific-options) 10 | + [AmigaOS-specific options](#amigaos-specific-options) 11 | + [Atari-ST-specific options](#atari-st-specific-options) 12 | + [Linux-specific options](#linux-specific-options) 13 | + [Windows-specific options](#windows-specific-options) 14 | * [Common options](#common-options) 15 | - [Building](#building) 16 | * [DOS](#dos) 17 | * [Amiga](#amiga) 18 | * [Atari ST](#atari-st) 19 | * [Linux](#linux) 20 | * [Windows](#windows) 21 | - [Installation](#installation) 22 | * [Linux shell completion scripts](#linux-shell-completion-scripts) 23 | + [bash completion script](#bash-completion-script) 24 | + [fish completion script](#fish-completion-script) 25 | + [zsh completion script](#zsh-completion-script) 26 | - [Showcase](#showcase) 27 | 28 | ## Features 29 | * Supports sending mt32-pi's custom System Exclusive messages for temporary configuration of SoundFonts & Co. 30 | * Can send standard MT-32, GM, and GS reset sequences 31 | * Can send screen text in MT-32 mode and 16x16 1bpp bitmaps in SoundFont mode to the mt32-pi's display 32 | * If that's not gimmicky enough for you, it can also send 8 characters of text as a 16x16 bitmap using the Miniwi 4x8 font 33 | * Custom MIDI messages from the command line 34 | * Can send SYX-files of any length containing any number of SysEx messages 35 | * DOS version compiled for 8086 Real Mode, so should work on pretty much any system with an MPU401-compatible interface 36 | * Amiga version is compiled for AmigaOS ≥1.3 and supports both `camd.library` and direct serial port access, so it likewise should work on pretty much any system 37 | * Useful for making game/application-specific start-up scripts to correctly set up the synth 38 | * NEW: Smart option autocompletion on Linux using the bash, fish, and zsh shells 39 | 40 | ## Usage Summary 41 | `MT32-PI.EXE`/`MT32-PI.TTP`/`mt32-pi-ctl` accept the following UNIX-style parameters: 42 | 43 | ### Platform-specific options 44 | #### DOS-specific options 45 | 46 | ``` 47 | USAGE: MT32-PI.EXE [OPTIONS] 48 | OPTIONS: 49 | -p [ADDR]: Set the port address of the MPU401 interface. Default: 330. 50 | ``` 51 | 52 | #### AmigaOS-specific options 53 | 54 | ``` 55 | USAGE: mt32-pi-ctl [OPTIONS] 56 | OPTIONS: 57 | -S: Use direct serial port access instead of camd.library. 58 | -l: The camd output location to connect to. (Default: out.0) 59 | ``` 60 | 61 | #### Atari-ST-specific options 62 | 63 | ``` 64 | USAGE: MT32-PI.TTP [OPTIONS] 65 | OPTIONS: 66 | [No special options as the Atari ST has a built-in MIDI port which is automatically used] 67 | ``` 68 | This is a CLI/TTP-utility. If you launch it directly from TOS a graphical prompt will let 69 | you input the parameters. Though for more convenience I would recommend a text based shell 70 | like EmuCON or Okami. 71 | 72 | Note that quoting in Atari ST shells seems to work a bit different than in other systems (or 73 | maybe I don't get it but the following works, in any case). 74 | Quoted parameters must use single-quotes, be located in front, and the parameter needs to 75 | be within the quotes. 76 | 77 | Example: When on other systems you'd write `mt32-pi-ctl -m -t "Hello, World!"`, on Atari ST 78 | it should be `MT32-PI.TTP '-t Hello, World!' -m`. 79 | 80 | #### Linux-specific options 81 | 82 | ``` 83 | USAGE: mt32-pi-ctl [OPTIONS] 84 | OPTIONS: 85 | -p "[CLIENT]:[PORT]" : The ALSA MIDI client and port address to output to (*MANDATORY*). 86 | ``` 87 | To find out which client/port to use, you can run `aplaymidi -l` or `aconnect -l` to list available devices. 88 | 89 | Since 1.0.1a, the Linux version of `mt32-pi-ctl` also comes with intelligent bash, fish, and zsh completion scripts 90 | that will smartly autocomplete (long) options, romsets, filenames and, if `aplaymidi` is in your 91 | PATH, even available MIDI ports. 92 | 93 | Examples for bash: 94 | ``` 95 | $ mt32-pi-ctl -p 96 | 14:0 28:0 97 | ``` 98 | ``` 99 | $ mt32-pi-ctl -p 28:0 --mt 100 | --mt32 --mt32-reset --mt32-rstereo --mt32-txt 101 | ``` 102 | ``` 103 | $ mt32-pi-ctl -p 28:0 --romset 104 | cm32l new old 105 | ``` 106 | For this to work mt32-pi-ctl must be in your PATH, e.g. in `/usr/local/bin` and the 107 | bash/fish completion scripts must be in a special completion script directory or for bash, sourced 108 | by `.bashrc`. [See below for installation instructions.](#linux-shell-completion-scripts) 109 | 110 | 111 | 112 | #### Windows-specific options 113 | 114 | ``` 115 | USAGE: mt32-pi-ctl.exe [OPTIONS] 116 | OPTIONS: 117 | -p PORT : Set MIDI output port number (*MANDATORY*). 118 | -l : List available MIDI output ports and exit. 119 | ``` 120 | 121 | ### Common options 122 | ``` 123 | -h/--help: Print this info. 124 | -v/--verbose: Be verbose about what is going on. 125 | -r/--reboot: Reboot the Pi. Will block for a few secs to give it time. 126 | -m/--mt32: Switch mt32-pi to MT-32 mode. 127 | -g/--fluidsynth: Switch mt32-pi to FluidSynth mode. 128 | -b/--romset [old, new, cm32l]: Switch MT-32 romset. 129 | -s/--soundfont [NUMBER]: Set FluidSynth SoundFont. 130 | -S/--mt32-rstereo [0, 1]: Enable/disable MT-32 reversed stereo. 131 | --mt32-reset: Send an MT-32 reset SysEx message. 132 | --gm-reset: Send a GM reset SysEx message. 133 | --gs-reset: Send a GS reset SysEx message. 134 | -t/--mt32-txt "Some text": Send an MT-32 text display SysEx. 135 | -T/--sc55-txt "Some text": Send an SC-55 text display SysEx. 136 | -P/--sc55-bmp FILE.BMP: Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx) 137 | -X/--sc55-btxt "SomeText": Display a string on the screen as a Bitmap using the 138 | miniwi 4x8 font. Max. 8 characters (SC-55) 139 | -N/--negative: Reverse image color. Use with '-P/--sc55-bmp'. 140 | -M/--midi "C0 01 C0 DE": Send a list of custom MIDI bytes. 141 | ``` 142 | 143 | You may specify multiple options, i.e. `MT32-PI.EXE -m -t "Hello, World!"` will first send the MT-32 mode command and then the screen text. 144 | 145 | ## Building 146 | Note that binaries are available on the [releases page](https://github.com/gmcn42/mt32-pi-control/releases) for all platforms apart from Linux. Building `mt32-pi-ctl` on most Linux distros is very starightforward though as you'll see below. For Arch Linux users, there's also a [package on the AUR](https://aur.archlinux.org/packages/mt32-pi-control/) now. 147 | 148 | ### DOS 149 | The `MAKEFILE` is written for the DOS-version of [Open Watcom C 1.9](https://sourceforge.net/projects/openwatcom/files/open-watcom-1.9/) as it can generate Real Mode executables. The Sourceforge release works perfectly in DosBox. 150 | Make sure you have the environment variables correctly set up. For that, DosBox users will need to run the `AUTOEXEC.BAT` code supplied by the installer. 151 | 152 | Then run `wmake` in `dos_src/` and compilation should run. Optionally, if you also have [upx](https://upx.github.io/) installed in your DOS environment, you can run `UPXCOMP.BAT` afterwards to pack the EXE and save a couple KB of executable size. `wmake dist` will also compress and then copy the resulting file to `dos_bin/`. 153 | 154 | ### Amiga 155 | The `Makefile` is written for [bebbo's amiga-gcc](https://github.com/bebbo/amiga-gcc). After you have installed the toolchain, you also need to run `make sdk=camd` in your `amiga-gcc` source directory to install the `camd` library. 156 | 157 | After that, you can run `make` and `make dist` in the `amiga-src/` folder. If you have installed amiga-gcc somewhere else than `/opt/amiga`, you will need to adjust the `PREFIX` variable in the `Makefile` accordingly. 158 | 159 | ### Atari ST 160 | The `Makefile` is written for [Vincent Rivière's m68k-atari-mint cross-tools](http://vincent.riviere.free.fr/soft/m68k-atari-mint/) on Linux or (probably) WSL. For Debian/Ubuntu I recommend using the repositories on the site. After installation of the toolchain, run `make` and `make dist` in the `atari_src` folder. 161 | 162 | ### Linux 163 | You need to have `gcc`, `make`, and the ALSA/libasound development headers installed. On Debian/Ubuntu `sudo apt install build-essential libasound2-dev` does the job. Afterwards, run `make` in the `linux_src` folder. 164 | 165 | ### Windows 166 | The `Makefile` is meant to be used on a Linux host with the `i686-w64-mingw32` toolchain. On Debian/Ubuntu `sudo apt install mingw-w64*` does the job. Afterwards, run `make` and `make dist` in the `win32_src` folder. If you want to compile on Windows, MSYS should work but you might need to adjust the executable names in the Makefile. 167 | 168 | ## Installation 169 | On most platforms installation just means copying the executable file somewhere in your `PATH`, e.g. `/usr/local/bin` on Linux, `C:` on Amiga, `C:\DOS\` on DOS, and similar. Alternatively you could add the path to `mt32-pi-ctl` to your `PATH` variable if that's more to your liking. 170 | 171 | ### Linux shell completion scripts 172 | For Linux platforms, there's the additional feature of smart command-line completion for the `bash`, `zsh`, and `fish` shells. In order for this to work you need to copy the completion scripts in `linux_src/completion_scripts/` to some special folders so your shell can find them automatically. The folders may be distro-specific but the ones below should work for Debian/Ubuntu at the very least. If in doubt, consult your distro's documentation. 173 | 174 | Note that you need `aplaymidi` installed if you want autocompletion to show you available MIDI output ports for the `-p` option. On most distros—including Debian, Ubuntu, and Arch—the package you need is called `alsa-utils`. 175 | 176 | #### bash completion script 177 | On modern Debian/Ubuntu systems, install the script using 178 | ``` 179 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.bash /usr/share/bash-completion/completions/mt32-pi-ctl 180 | ``` 181 | On other or older distros, the bash completion script may need to go to `/etc/bash_completion.d/mt32-pi-ctl` instead. 182 | 183 | Alternatively you can add the line 184 | ``` 185 | source /path/to/mt32-pi-ctl.bash 186 | ``` 187 | to your `.bashrc`. After installing, you may need to relogin or reboot. 188 | 189 | #### fish completion script 190 | Install the script using 191 | ``` 192 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.fish /usr/share/fish/vendor_completions.d/mt32-pi-ctl.fish 193 | ``` 194 | The fish completion script may be located in your home directory under `~/.config/fish/completions` instead, if you prefer. 195 | Once copied, completions should be available in fish immediately. 196 | 197 | #### zsh completion script 198 | Install the script using 199 | ``` 200 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.zsh /usr/local/share/zsh/site-functions/_mt32-pi-ctl 201 | ``` 202 | Alternatively you may use any of the directories reported by `echo $fpath` but note that the 203 | script has to be renamed to `_mt32-pi-ctl` or zsh won't associate it with the program. 204 | After installing, you may need to relogin or reboot. 205 | 206 | ## Showcase 207 | 208 | 209 | 210 | 211 | The BMP file is in the archive. I made it with GIMP but MS Paint and others should work just fine as long as the file is 16x16px monochrome (1-bit indexed). 212 | -------------------------------------------------------------------------------- /amiga_bin/mt32-pi-ctl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/amiga_bin/mt32-pi-ctl -------------------------------------------------------------------------------- /amiga_bin/mt32-pi-ctl.readme: -------------------------------------------------------------------------------- 1 | #### mt32-pi-ctl 1.0.1 AmigaOS ReadMe #### 2 | 3 | mt32-pi-ctl is a control program for the mt32-pi MIDI synthesizer. 4 | 5 | Program source, build instructions and ports to different modern and 6 | retro platforms available at https://github.com/gmcn42/mt32-pi-control 7 | 8 | The mt32-pi synthesizer lives at https://github.com/dwhinham/mt32-pi 9 | 10 | 11 | #### Installation #### 12 | 13 | Just copy mt32-pi-ctl somewhere in your PATH. 14 | 15 | 16 | #### Usage Summary #### 17 | 18 | mt32-pi-ctl accepts the following UNIX-style parameters: 19 | 20 | USAGE: mt32-pi-ctl [OPTIONS] 21 | OPTIONS: 22 | -h/--help: Print this info. 23 | -v/--verbose: Be verbose about what is going on. 24 | -r/--reboot: Reboot the Pi. Will block for a few secs to give it time. 25 | -S: Use direct serial port access instead of camd.library. 26 | -l: The camd output location to connect to. (Default: out.0) 27 | -m/--mt32: Switch mt32-pi to MT-32 mode. 28 | -g/--fluidsynth: Switch mt32-pi to FluidSynth mode. 29 | -b/--romset [old, new, cm32l]: Switch MT-32 romset. 30 | -s/--soundfont [NUMBER]: Set FluidSynth SoundFont. 31 | -S/--mt32-rstereo [0, 1]: Enable/disable MT-32 reversed stereo. 32 | --mt32-reset: Send an MT-32 reset SysEx message. 33 | --gm-reset: Send a GM reset SysEx message. 34 | --gs-reset: Send a GS reset SysEx message. 35 | -t/--mt32-txt "Some text": Send an MT-32 text display SysEx. 36 | -T/--sc55-txt "Some text": Send an SC-55 text display SysEx. 37 | -P/--sc55-bmp FILE.BMP: Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx) 38 | -X/--sc55-btxt "SomeText": Display a string on the screen as a Bitmap. (SC-55) 39 | -N/--negative: Reverse image color. Use with '-P/--sc55-bmp'. 40 | -M/--midi "C0 01 C0 DE": Send a list of custom MIDI bytes. 41 | -Y/--syx file.syx : Send the contents of a SYX-file. 42 | 43 | You may specify multiple options, i.e. mt32-pi-ctl -m -t "Hello, World!" will 44 | first send the MT-32 mode command and then the screen text. 45 | 46 | 47 | #### BSD-License #### 48 | 49 | Copyright (C) 2021 Andreas Zdziarstek 50 | with parts 51 | Copyright (C) 2020 Dale Whinham (camd/serial code), 52 | Copyright (C) 2015-2016 Josuah Demangeon (miniwi font), 53 | Copyright (C) 2002 Todd C. Miller (getopt). 54 | and Copyright (C) 2000 The NetBSD Foundation, Inc. (getopt) 55 | 56 | See the individual source files for details. 57 | 58 | Redistribution and use in source and binary forms, with or without 59 | modification, are permitted provided that the following conditions are met: 60 | 61 | 1. Redistributions of source code must retain the above copyright notice, 62 | this list of conditions and the following disclaimer. 63 | 64 | 2. Redistributions in binary form must reproduce the above copyright 65 | notice, this list of conditions and the following disclaimer in the 66 | documentation and/or other materials provided with the distribution. 67 | 68 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 69 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 70 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 71 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 72 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 73 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 74 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 75 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 76 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 77 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 78 | POSSIBILITY OF SUCH DAMAGE. 79 | -------------------------------------------------------------------------------- /amiga_bin/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/amiga_bin/mt32pi.bmp -------------------------------------------------------------------------------- /amiga_src/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME := mt32-pi-ctl 2 | 3 | PREFIX := /opt/amiga/bin 4 | 5 | CXX = $(PREFIX)/m68k-amigaos-g++ 6 | CC = $(PREFIX)/m68k-amigaos-gcc 7 | AR = $(PREFIX)/m68k-amigaos-ar 8 | AS = $(PREFIX)/m68k-amigaos-as 9 | 10 | VPATH = .:../common_src 11 | INCLUDE = -I../common_src 12 | DEFINE = -DPROGRAM_NAME='"$(PROGRAM_NAME)"' -DREPLACE_GETOPT -DUSE_CUSTOM_DELAY 13 | 14 | #CFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -mcrt=nix13 -msmall-code -fbaserel -mregparm -Wall -Werror -Wno-pointer-sign -std=gnu99 15 | 16 | CFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -mcrt=nix13 -msmall-code -fbaserel -Wall -Werror -Wno-pointer-sign -std=gnu99 $(DEFINE) 17 | 18 | CXXFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -mcrt=nix13 -msmall-code -fbaserel -Wall -Werror $(DEFINE) 19 | 20 | LIBS = -lamiga 21 | 22 | OBJ = mt32-pi.o midi_dev.o getopt.o delay.o version.o 23 | 24 | %.o : %.c 25 | $(CC) -c -o $@ $< $(CFLAGS) 26 | 27 | %.o : %.C 28 | $(CC) -c -o $@ $< $(CFLAGS) 29 | 30 | 31 | $(PROGRAM_NAME) : $(OBJ) 32 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 33 | 34 | .PHONY: clean 35 | clean : 36 | rm -f $(OBJ) $(PROGRAM_NAME) 37 | 38 | .PHONY: dist 39 | dist: $(PROGRAM_NAME) 40 | cp -v $(PROGRAM_NAME) ../amiga_bin/ 41 | cp -v *.bmp ../amiga_bin/ 42 | 43 | -------------------------------------------------------------------------------- /amiga_src/delay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | #include "delay.h" 16 | 17 | void delay_ms(unsigned int delay) { 18 | if(DOSBase) { 19 | //System-friendly delay, uses 50Hz ticks normally 20 | Delay(delay/(1000/TICKS_PER_SECOND) + 1); 21 | } else { 22 | // with mcrt=nix13 we should get a DOSBase through proto/dos.h so this should 23 | // (hopefully) never happen 24 | fprintf(stderr, "WARNING: Delay seems not to be working due to missing DOSBase.\n"); 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /amiga_src/midi_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | * parts of camd and serial code taken from 10 | * https://github.com/dwhinham/minisysex 11 | * 12 | * Copyright (c) 2020 Dale Whinham 13 | * Thanks! :) 14 | * 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | /* FIXME: Kludge to get CAMD includes working with -mcrt=nix13 */ 22 | #include 23 | typedef ULONG Tag; 24 | #define TAG_USER ((ULONG)(1L<<31)) 25 | #define TAG_END (0L) 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "midi_dev.h" 35 | 36 | #define MIDI_BAUD_RATE 31250L 37 | 38 | // Serial stuff 39 | static struct MsgPort* msg_port = NULL; 40 | static struct IOExtSer* io_serial = NULL; 41 | 42 | // Camd stuff 43 | struct Library* CamdBase = NULL; 44 | static struct MidiNode* midi_node = NULL; 45 | static struct MidiLink* midi_link = NULL; 46 | 47 | static char *dev_optstr = "Sl:"; 48 | static bool use_direct_serial = false; 49 | static char camd_out[32] = "out.0"; 50 | 51 | int mididev_init(void) { 52 | if(use_direct_serial) { 53 | if (!(msg_port = CreatePort(NULL, 0L))) { 54 | fprintf(stderr, "Failed to create port.\n"); 55 | return -1; 56 | } 57 | 58 | if (!(io_serial = (struct IOExtSer*) CreateExtIO(msg_port, sizeof(struct IOExtSer)))) { 59 | fprintf(stderr, "Failed to create I/O request.\n"); 60 | return -1; 61 | } 62 | 63 | if (OpenDevice(SERIALNAME, 0L, (struct IORequest*) io_serial, 0L)) { 64 | fprintf(stderr, "Failed to open " SERIALNAME); 65 | return false; 66 | } 67 | 68 | io_serial->io_Baud = MIDI_BAUD_RATE; 69 | io_serial->io_SerFlags = SERF_RAD_BOOGIE | SERF_XDISABLED; 70 | io_serial->io_WriteLen = 8; 71 | io_serial->io_StopBits = 1; 72 | io_serial->IOSer.io_Command = SDCMD_SETPARAMS; 73 | DoIO((struct IORequest*) io_serial); 74 | 75 | return 0; 76 | } 77 | 78 | if (!(CamdBase = OpenLibrary("camd.library", 0L))) { 79 | fprintf(stderr, "Failed to open camd.library.\n"); 80 | return -1; 81 | } 82 | 83 | if (!(midi_node = CreateMidi(MIDI_MsgQueue, 16, MIDI_SysExSize, 512L, TAG_END))) { 84 | fprintf(stderr, "Failed to create MIDI node.\n"); 85 | return -1; 86 | } 87 | 88 | midi_link = AddMidiLink( 89 | midi_node, 90 | MLTYPE_Sender, 91 | MLINK_Name, (ULONG) "mt32-pi-ctl.out", 92 | MLINK_Location, (ULONG) camd_out, 93 | TAG_END 94 | ); 95 | 96 | if(!midi_link) { 97 | fprintf(stderr, "Failed to create MIDI link to %s.\n", camd_out); 98 | return -1; 99 | } 100 | 101 | return 0; 102 | } 103 | 104 | int mididev_deinit(void) { 105 | if(use_direct_serial) { 106 | if (io_serial) { 107 | if (io_serial->IOSer.io_Device) 108 | CloseDevice((struct IORequest*) io_serial); 109 | 110 | DeleteExtIO((struct IORequest*) io_serial); 111 | io_serial = NULL; 112 | } 113 | 114 | if (msg_port) { 115 | DeletePort(msg_port); 116 | msg_port = NULL; 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | if (midi_link) { 123 | RemoveMidiLink(midi_link); 124 | midi_link = NULL; 125 | } 126 | if (midi_node) { 127 | DeleteMidi(midi_node); 128 | midi_node = NULL; 129 | } 130 | 131 | CloseLibrary(CamdBase); 132 | return 0; 133 | } 134 | 135 | int mididev_send_bytes(const unsigned char *buf, int len) { 136 | if(len==0) return 0; 137 | 138 | // We're not doing any sanity checking on serial port writes 139 | // so if someone wants to send corrupt MIDI bytes 140 | // with -M for debugging or whatever they can do that 141 | if(use_direct_serial) { 142 | io_serial->IOSer.io_Length = (ULONG) len; 143 | io_serial->IOSer.io_Data = (APTR) buf; 144 | io_serial->IOSer.io_Command = CMD_WRITE; 145 | DoIO((struct IORequest*) io_serial); 146 | return 0; 147 | } 148 | 149 | // CAMD however gets unhappy if we try to send weird stuff 150 | // so here we're doing minimal checking beforehand. 151 | if(buf[0] == MS_SysEx && buf[len-1] == MS_EOX) { 152 | PutSysEx(midi_link, (UBYTE*)buf); 153 | } else if (len==3) { 154 | MidiMsg midi_message = 155 | { 156 | .mm_Status = buf[0], 157 | .mm_Data1 = buf[1], 158 | .mm_Data2 = buf[2], 159 | .mm_Port = 0 160 | }; 161 | PutMidi(midi_link, midi_message.mm_Msg); 162 | } else { 163 | fprintf(stderr, "Error: Non-SysEx message with more than 3 bytes.\n"); 164 | return -1; 165 | } 166 | 167 | return 0; 168 | } 169 | 170 | void mididev_print_usage(void) { 171 | printf("-S: Use direct serial port access instead of camd.library.\n"); 172 | printf("-l: The camd output location to connect to. (Default: out.0)\n"); 173 | } 174 | 175 | void mididev_add_optstr(char *optstr) { 176 | int main_len = strlen(optstr); 177 | strcpy(optstr+main_len, dev_optstr); 178 | } 179 | 180 | int mididev_parse_arg(int c, const char *optarg) { 181 | if(c=='S') { 182 | use_direct_serial = true; 183 | return 0; 184 | } 185 | if(c=='l') { 186 | if(strlen(optarg) > 31) { 187 | fprintf(stderr, "Sorry, the camd output location's name is too long.\n"); 188 | return -1; 189 | } 190 | strcpy(camd_out, optarg); 191 | return 0; 192 | } 193 | return -1; 194 | } 195 | 196 | -------------------------------------------------------------------------------- /amiga_src/version.c: -------------------------------------------------------------------------------- 1 | const char* mt32_pi_ctl_version = "\0$VER: mt32-pi-ctl 1.0.1 (12.08.2021)"; 2 | -------------------------------------------------------------------------------- /atari_bin/MT32-PI.TTP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/atari_bin/MT32-PI.TTP -------------------------------------------------------------------------------- /atari_bin/MT32-PI.TXT: -------------------------------------------------------------------------------- 1 | #### MT32-PI.TTP 1.0.1 Atari ST ReadMe #### 2 | 3 | MT32-PI.TTP is a control program for the mt32-pi MIDI synthesizer. 4 | 5 | Note that this is a CLI/TTP-utility. If you launch it directly from 6 | TOS a graphical prompt will let you input the parameters. Though for 7 | more convenience I would recommend a text based shell like EmuCON 8 | or Okami. 9 | 10 | Program source, build instructions and ports to different modern and 11 | retro platforms available at https://github.com/gmcn42/mt32-pi-control 12 | 13 | The mt32-pi synthesizer lives at https://github.com/dwhinham/mt32-pi 14 | 15 | 16 | #### Usage Summary #### 17 | 18 | MT32-PI.TTP accepts the following UNIX-style parameters: 19 | 20 | USAGE: MT32-PI.TTP [OPTIONS] 21 | OPTIONS: 22 | -h/--help: Print this info. 23 | -v/--verbose: Be verbose about what is going on. 24 | -r/--reboot: Reboot the Pi. Will block for a few secs to give it time. 25 | -m/--mt32: Switch mt32-pi to MT-32 mode. 26 | -g/--fluidsynth: Switch mt32-pi to FluidSynth mode. 27 | -b/--romset [old, new, cm32l]: Switch MT-32 romset. 28 | -s/--soundfont [NUMBER]: Set FluidSynth SoundFont. 29 | -S/--mt32-rstereo [0, 1]: Enable/disable MT-32 reversed stereo. 30 | --mt32-reset: Send an MT-32 reset SysEx message. 31 | --gm-reset: Send a GM reset SysEx message. 32 | --gs-reset: Send a GS reset SysEx message. 33 | '-t/--mt32-txt Some text': Send an MT-32 text display SysEx. 34 | '-T/--sc55-txt Some text': Send an SC-55 text display SysEx. 35 | -P/--sc55-bmp FILE.BMP: Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx) 36 | '-X/--sc55-btxt SomeText': Display a string on the screen as a Bitmap. (SC-55) 37 | -N/--negative: Reverse image color. Use with '-P/--sc55-bmp'. 38 | '-M/--midi C0 01 C0 DE': Send a list of custom MIDI bytes. 39 | -Y/--syx file.syx : Send the contents of a SYX-file. 40 | NOTE: If you use quotes in arguments, put those before unquoted ones. 41 | 42 | You may specify multiple options, i.e. MT32-PI.TTP '-t Hello, World!' -m will 43 | first send the MT-32 mode command and then the screen text. 44 | 45 | 46 | #### BSD-License #### 47 | 48 | Copyright (C) 2021 Andreas Zdziarstek 49 | with parts 50 | Copyright (C) 2015-2016 Josuah Demangeon (miniwi font), 51 | Copyright (c) 2002 Todd C. Miller (getopt). 52 | and Copyright (c) 2000 The NetBSD Foundation, Inc. (getopt) 53 | 54 | See the individual source files for details. 55 | 56 | Redistribution and use in source and binary forms, with or without 57 | modification, are permitted provided that the following conditions are met: 58 | 59 | 1. Redistributions of source code must retain the above copyright notice, 60 | this list of conditions and the following disclaimer. 61 | 62 | 2. Redistributions in binary form must reproduce the above copyright 63 | notice, this list of conditions and the following disclaimer in the 64 | documentation and/or other materials provided with the distribution. 65 | 66 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 67 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 68 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 69 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 70 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 71 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 72 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 73 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 74 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 75 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 76 | POSSIBILITY OF SUCH DAMAGE. 77 | 78 | -------------------------------------------------------------------------------- /atari_bin/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/atari_bin/mt32pi.bmp -------------------------------------------------------------------------------- /atari_src/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME := MT32-PI.TTP 2 | 3 | CXX = m68k-atari-mint-g++ 4 | CC = m68k-atari-mint-gcc 5 | AR = m68k-atari-mint-ar 6 | AS = m68k-atari-mint-as 7 | 8 | VPATH = .:../common_src 9 | INCLUDE = -I../common_src 10 | DEFINE = -DARGV_NO_BASENAME -DNO_ERROR_ON_HELP -DATARI_ST_QUOTING -DUSE_CUSTOM_DELAY -DPROGRAM_NAME='"$(PROGRAM_NAME)"' -DREPLACE_GETOPT 11 | 12 | CFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -Wall -Werror -Wno-pointer-sign -std=c99 $(DEFINE) 13 | CXXFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -Wall -Werror $(DEFINE) 14 | 15 | OBJ = mt32-pi.o midi_dev.o getopt.o delay.o 16 | 17 | %.o : %.c 18 | $(CC) -c -o $@ $< $(CFLAGS) 19 | 20 | %.o : %.C 21 | $(CC) -c -o $@ $< $(CFLAGS) 22 | 23 | 24 | $(PROGRAM_NAME) : $(OBJ) 25 | $(CC) -o $@ $^ $(CFLAGS) 26 | 27 | .PHONY: clean 28 | clean : 29 | rm -f $(OBJ) $(PROGRAM_NAME) 30 | 31 | .PHONY: dist 32 | dist: $(PROGRAM_NAME) 33 | cp -v $(PROGRAM_NAME) ../atari_bin/ 34 | cp -v *.bmp ../atari_bin/ 35 | 36 | -------------------------------------------------------------------------------- /atari_src/delay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | 11 | #include 12 | 13 | #include "delay.h" 14 | 15 | #define TIM_A_MFP 13 16 | #define TIM_A_XBIOS 0 17 | 18 | #define MFP_ISR_RUNNING_REG *((volatile unsigned char*)0xFFFFFA0FL) 19 | #define MFP_VECTOR_REG *((volatile unsigned char*)0xFFFFFA17L) 20 | 21 | #define ISR_RUNNING_TIM_A 0x20 22 | #define VECTREG_SW_EOI 0x08 23 | 24 | volatile unsigned int ticks = 0; 25 | 26 | static void __attribute__((interrupt)) tim_a_isr(void) { 27 | ticks++; 28 | // Notify MFP of end-of-interrupt bc we're in 29 | // sw eoi mode 30 | MFP_ISR_RUNNING_REG &= ~ISR_RUNNING_TIM_A; 31 | } 32 | 33 | static void tim_a_set_sei( void ) { 34 | // Set software end-of-interrupt mode 35 | MFP_VECTOR_REG |= VECTREG_SW_EOI; 36 | } 37 | 38 | void delay_ms(unsigned int delay) { 39 | // Disable the timer int if it was enabled 40 | Jdisint(TIM_A_MFP); 41 | 42 | // reset elapsed ticks 43 | ticks = 0; 44 | 45 | // Initialize Timer A (for user progs) 46 | // Set Prescaler to 50 and counter start to 50 47 | // 50/(2.4576MHz) * 50 is approx 1ms 48 | // Set ISR to our handler 49 | Xbtimer(TIM_A_XBIOS, 4, 50, tim_a_isr); 50 | 51 | // NOt sure if this is 100% necessary or Xbtimer 52 | // always takes care of this. In any case, here 53 | // we set software EOI mode and enable the Timer A 54 | // interrupt 55 | Supexec(tim_a_set_sei); 56 | Jenabint(TIM_A_MFP); 57 | 58 | // Wait until delay ms have elapsed 59 | while(ticks < delay); 60 | // Disable the interrupt again 61 | Jdisint(TIM_A_MFP); 62 | } 63 | 64 | -------------------------------------------------------------------------------- /atari_src/midi_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | 11 | #include 12 | #include "mint/osbind.h" 13 | 14 | int mididev_init(void) { 15 | return 0; 16 | } 17 | 18 | int mididev_deinit(void) { 19 | return 0; 20 | } 21 | 22 | 23 | int mididev_send_bytes(const unsigned char *buf, int len) { 24 | if(len==0) return 0; 25 | 26 | if((buf[0] == 0xF0 && buf[len-1] == 0xF7) || len==3) { 27 | Midiws(len-1, buf); 28 | } else { 29 | fprintf(stderr, "Error: Non-SysEx message with more than 3 bytes.\n"); 30 | return -1; 31 | } 32 | 33 | //fprintf(stderr, "len:%d end:%02X\n", len, buf[len-1]); 34 | // for(int i=0; i 17 | 18 | static void delay_ms(unsigned int delay) { 19 | clock_t start = clock(); 20 | while(clock() - start < (1000UL*(unsigned long)delay)/CLOCKS_PER_SEC); 21 | } 22 | #endif 23 | 24 | #endif /* __DELAY_H__ */ 25 | -------------------------------------------------------------------------------- /common_src/file_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | * Custom file I/O interface based on 10 | * DOSMID source code 11 | * http://dosmid.sourceforge.net 12 | * 13 | * Copyright (C) 2018 Mateusz Viste 14 | * 15 | */ 16 | 17 | #ifndef __FILE_IO_H__ 18 | #define __FILE_IO_H__ 19 | 20 | #define FIO_SEEK_START 0 21 | #define FIO_SEEK_CUR 1 22 | #define FIO_SEEK_END 2 23 | 24 | #define FIO_OPEN_RD 0 25 | #define FIO_OPEN_WR 1 26 | #define FIO_OPEN_RW 2 27 | 28 | #ifdef USE_CUSTOM_FILE_IO /* In case the C stdlib file handling is buggy, like in DOS/OpenWatcom */ 29 | 30 | #define FIO_CACHE 32 31 | 32 | struct file_t { 33 | unsigned short fh; /* file handle */ 34 | unsigned long flen; /* file length */ 35 | unsigned long curpos; /* current offset position (ftell) */ 36 | unsigned char buff[FIO_CACHE]; /* buffer storage */ 37 | unsigned long bufoffs; /* offset of buffer */ 38 | unsigned char flags; /* flags */ 39 | }; 40 | 41 | /* open file fname and set fhandle with the associated file handle. returns 0 on success, non-zero otherwise */ 42 | int fio_open(struct file_t *f, const char *fname, int mode); 43 | 44 | /* reads count bytes from file pointed at by fhandle, and writes the data into buff. returns the number of bytes actually read */ 45 | int fio_read(struct file_t *f, void *buff, int count); 46 | 47 | /* seek to offset position of file pointed at by fhandle. returns current file position on success, a negative error otherwise */ 48 | signed long fio_seek(struct file_t *f, unsigned short origin, signed long offset); 49 | 50 | /* close file handle. returns 0 on success, non-zero otherwise */ 51 | int fio_close(struct file_t *f); 52 | 53 | #else /* Just use the C standard library */ 54 | #include 55 | 56 | struct file_t { 57 | FILE *fh; /* file handle */ 58 | long flen; /* file length */ 59 | long curpos; /* current offset position (ftell) */ 60 | }; 61 | 62 | /* open file fname and set fhandle with the associated file handle. returns 0 on success, non-zero otherwise */ 63 | static int fio_open(struct file_t *f, const char *fname, int mode) { 64 | switch(mode) { 65 | case FIO_OPEN_RD: 66 | f->fh = fopen(fname, "rb"); 67 | break; 68 | case FIO_OPEN_WR: 69 | f->fh = fopen(fname, "wb"); 70 | break; 71 | case FIO_OPEN_RW: 72 | f->fh = fopen(fname, "rb+"); 73 | break; 74 | default: 75 | return -1; 76 | } 77 | 78 | if(f->fh==NULL) { 79 | return -1; 80 | } 81 | 82 | if(fseek(f->fh, 0, SEEK_END) != 0) { 83 | return -1; 84 | } 85 | 86 | if( (f->flen = ftell(f->fh)) < 0 ) { 87 | return -1; 88 | } 89 | 90 | if(fseek(f->fh, 0, SEEK_SET) != 0) { 91 | return -1; 92 | } 93 | 94 | f->curpos = 0; 95 | return 0; 96 | } 97 | 98 | /* reads count bytes from file pointed at by fhandle, and writes the data into buff. returns the number of bytes actually read */ 99 | static int fio_read(struct file_t *f, void *buff, int count) { 100 | int out = fread(buff, 1, count, f->fh); 101 | if( (f->curpos = ftell(f->fh)) < 0 ) { 102 | return -1; 103 | } 104 | return out; 105 | } 106 | 107 | /* seek to offset position of file pointed at by fhandle. returns current file position on success, a negative error otherwise */ 108 | static signed long fio_seek(struct file_t *f, unsigned short origin, signed long offset) { 109 | int o = SEEK_SET; 110 | switch(origin) { 111 | case FIO_SEEK_START: 112 | o = SEEK_SET; 113 | break; 114 | case FIO_SEEK_CUR: 115 | o = SEEK_CUR; 116 | break; 117 | case FIO_SEEK_END: 118 | o = SEEK_END; 119 | break; 120 | default: 121 | return -1; 122 | } 123 | 124 | if(fseek(f->fh, offset, o) != 0) { 125 | return -1; 126 | } 127 | 128 | if( (f->curpos = ftell(f->fh)) < 0 ) { 129 | return -1; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | /* close file handle. returns 0 on success, non-zero otherwise */ 136 | static int fio_close(struct file_t *f) { 137 | if(fclose(f->fh) == EOF) { 138 | return -1; 139 | } 140 | return 0; 141 | } 142 | #endif /* USE_CUSTOM_FILE_IO */ 143 | 144 | #endif /* __FILE_IO_H__ */ 145 | -------------------------------------------------------------------------------- /common_src/getopt.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: getopt_long.c,v 1.23 2007/10/31 12:34:57 chl Exp $ */ 2 | /* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */ 3 | 4 | /* 5 | * Copyright (c) 2002 Todd C. Miller 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | * 19 | * Sponsored in part by the Defense Advanced Research Projects 20 | * Agency (DARPA) and Air Force Research Laboratory, Air Force 21 | * Materiel Command, USAF, under agreement number F39502-99-1-0512. 22 | */ 23 | /*- 24 | * Copyright (c) 2000 The NetBSD Foundation, Inc. 25 | * All rights reserved. 26 | * 27 | * This code is derived from software contributed to The NetBSD Foundation 28 | * by Dieter Baron and Thomas Klausner. 29 | * 30 | * Redistribution and use in source and binary forms, with or without 31 | * modification, are permitted provided that the following conditions 32 | * are met: 33 | * 1. Redistributions of source code must retain the above copyright 34 | * notice, this list of conditions and the following disclaimer. 35 | * 2. Redistributions in binary form must reproduce the above copyright 36 | * notice, this list of conditions and the following disclaimer in the 37 | * documentation and/or other materials provided with the distribution. 38 | * 39 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 40 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 41 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 43 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 44 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 45 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 46 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 47 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 48 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 49 | * POSSIBILITY OF SUCH DAMAGE. 50 | */ 51 | 52 | #include 53 | #include 54 | #include 55 | #include "getopt.h" 56 | #include 57 | #include 58 | 59 | 60 | int opterr = 1; /* if error message should be printed */ 61 | int optind = 1; /* index into parent argv vector */ 62 | int optopt = '?'; /* character checked for validity */ 63 | #undef optreset /* see getopt.h */ 64 | #define optreset __mingw_optreset 65 | int optreset; /* reset getopt */ 66 | char *optarg; /* argument associated with option */ 67 | 68 | #define PRINT_ERROR ((opterr) && (*options != ':')) 69 | 70 | #define FLAG_PERMUTE 0x01 /* permute non-options to the end of argv */ 71 | #define FLAG_ALLARGS 0x02 /* treat non-options as args to option "-1" */ 72 | #define FLAG_LONGONLY 0x04 /* operate as getopt_long_only */ 73 | 74 | /* return values */ 75 | #define BADCH (int)'?' 76 | #define BADARG ((*options == ':') ? (int)':' : (int)'?') 77 | #define INORDER (int)1 78 | 79 | 80 | #ifdef __CYGWIN__ 81 | static char EMSG[] = ""; 82 | #else 83 | #define EMSG "" 84 | #endif 85 | 86 | static int getopt_internal(int, char * const *, const char *, 87 | const struct option *, int *, int); 88 | static int parse_long_options(char * const *, const char *, 89 | const struct option *, int *, int); 90 | static int gcd(int, int); 91 | static void permute_args(int, int, int, char * const *); 92 | 93 | static char *place = EMSG; /* option letter processing */ 94 | 95 | /* XXX: set optreset to 1 rather than these two */ 96 | static int nonopt_start = -1; /* first non option argument (for permute) */ 97 | static int nonopt_end = -1; /* first option after non options (for permute) */ 98 | 99 | /* Error messages */ 100 | static const char recargchar[] = "option requires an argument -- %c"; 101 | static const char recargstring[] = "option requires an argument -- %s"; 102 | static const char ambig[] = "ambiguous option -- %.*s"; 103 | static const char noarg[] = "option doesn't take an argument -- %.*s"; 104 | static const char illoptchar[] = "unknown option -- %c"; 105 | static const char illoptstring[] = "unknown option -- %s"; 106 | 107 | static void 108 | _vwarnx(const char *fmt,va_list ap) 109 | { 110 | (void)fprintf(stderr,"Error: "); 111 | if (fmt != NULL) 112 | (void)vfprintf(stderr,fmt,ap); 113 | (void)fprintf(stderr,"\n"); 114 | } 115 | 116 | static void 117 | warnx(const char *fmt,...) 118 | { 119 | va_list ap; 120 | va_start(ap,fmt); 121 | _vwarnx(fmt,ap); 122 | va_end(ap); 123 | } 124 | 125 | /* 126 | * Compute the greatest common divisor of a and b. 127 | */ 128 | static int 129 | gcd(int a, int b) 130 | { 131 | int c; 132 | 133 | c = a % b; 134 | while (c != 0) { 135 | a = b; 136 | b = c; 137 | c = a % b; 138 | } 139 | 140 | return (b); 141 | } 142 | 143 | /* 144 | * Exchange the block from nonopt_start to nonopt_end with the block 145 | * from nonopt_end to opt_end (keeping the same order of arguments 146 | * in each block). 147 | */ 148 | static void 149 | permute_args(int panonopt_start, int panonopt_end, int opt_end, 150 | char * const *nargv) 151 | { 152 | int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos; 153 | char *swap; 154 | 155 | /* 156 | * compute lengths of blocks and number and size of cycles 157 | */ 158 | nnonopts = panonopt_end - panonopt_start; 159 | nopts = opt_end - panonopt_end; 160 | ncycle = gcd(nnonopts, nopts); 161 | cyclelen = (opt_end - panonopt_start) / ncycle; 162 | 163 | for (i = 0; i < ncycle; i++) { 164 | cstart = panonopt_end+i; 165 | pos = cstart; 166 | for (j = 0; j < cyclelen; j++) { 167 | if (pos >= panonopt_end) 168 | pos -= nnonopts; 169 | else 170 | pos += nopts; 171 | swap = nargv[pos]; 172 | /* LINTED const cast */ 173 | ((char **) nargv)[pos] = nargv[cstart]; 174 | /* LINTED const cast */ 175 | ((char **)nargv)[cstart] = swap; 176 | } 177 | } 178 | } 179 | 180 | /* 181 | * parse_long_options -- 182 | * Parse long options in argc/argv argument vector. 183 | * Returns -1 if short_too is set and the option does not match long_options. 184 | */ 185 | static int 186 | parse_long_options(char * const *nargv, const char *options, 187 | const struct option *long_options, int *idx, int short_too) 188 | { 189 | char *current_argv, *has_equal; 190 | size_t current_argv_len; 191 | int i, ambiguous, match; 192 | 193 | #define IDENTICAL_INTERPRETATION(_x, _y) \ 194 | (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \ 195 | long_options[(_x)].flag == long_options[(_y)].flag && \ 196 | long_options[(_x)].val == long_options[(_y)].val) 197 | 198 | current_argv = place; 199 | match = -1; 200 | ambiguous = 0; 201 | 202 | optind++; 203 | 204 | if ((has_equal = strchr(current_argv, '=')) != NULL) { 205 | /* argument found (--option=arg) */ 206 | current_argv_len = has_equal - current_argv; 207 | has_equal++; 208 | } else 209 | current_argv_len = strlen(current_argv); 210 | 211 | for (i = 0; long_options[i].name; i++) { 212 | /* find matching long option */ 213 | if (strncmp(current_argv, long_options[i].name, 214 | current_argv_len)) 215 | continue; 216 | 217 | if (strlen(long_options[i].name) == current_argv_len) { 218 | /* exact match */ 219 | match = i; 220 | ambiguous = 0; 221 | break; 222 | } 223 | /* 224 | * If this is a known short option, don't allow 225 | * a partial match of a single character. 226 | */ 227 | if (short_too && current_argv_len == 1) 228 | continue; 229 | 230 | if (match == -1) /* partial match */ 231 | match = i; 232 | else if (!IDENTICAL_INTERPRETATION(i, match)) 233 | ambiguous = 1; 234 | } 235 | if (ambiguous) { 236 | /* ambiguous abbreviation */ 237 | if (PRINT_ERROR) 238 | warnx(ambig, (int)current_argv_len, 239 | current_argv); 240 | optopt = 0; 241 | return (BADCH); 242 | } 243 | if (match != -1) { /* option found */ 244 | if (long_options[match].has_arg == no_argument 245 | && has_equal) { 246 | if (PRINT_ERROR) 247 | warnx(noarg, (int)current_argv_len, 248 | current_argv); 249 | /* 250 | * XXX: GNU sets optopt to val regardless of flag 251 | */ 252 | if (long_options[match].flag == NULL) 253 | optopt = long_options[match].val; 254 | else 255 | optopt = 0; 256 | return (BADARG); 257 | } 258 | if (long_options[match].has_arg == required_argument || 259 | long_options[match].has_arg == optional_argument) { 260 | if (has_equal) 261 | optarg = has_equal; 262 | else if (long_options[match].has_arg == 263 | required_argument) { 264 | /* 265 | * optional argument doesn't use next nargv 266 | */ 267 | optarg = nargv[optind++]; 268 | } 269 | } 270 | if ((long_options[match].has_arg == required_argument) 271 | && (optarg == NULL)) { 272 | /* 273 | * Missing argument; leading ':' indicates no error 274 | * should be generated. 275 | */ 276 | if (PRINT_ERROR) 277 | warnx(recargstring, 278 | current_argv); 279 | /* 280 | * XXX: GNU sets optopt to val regardless of flag 281 | */ 282 | if (long_options[match].flag == NULL) 283 | optopt = long_options[match].val; 284 | else 285 | optopt = 0; 286 | --optind; 287 | return (BADARG); 288 | } 289 | } else { /* unknown option */ 290 | if (short_too) { 291 | --optind; 292 | return (-1); 293 | } 294 | if (PRINT_ERROR) 295 | warnx(illoptstring, current_argv); 296 | optopt = 0; 297 | return (BADCH); 298 | } 299 | if (idx) 300 | *idx = match; 301 | if (long_options[match].flag) { 302 | *long_options[match].flag = long_options[match].val; 303 | return (0); 304 | } else 305 | return (long_options[match].val); 306 | #undef IDENTICAL_INTERPRETATION 307 | } 308 | 309 | /* 310 | * getopt_internal -- 311 | * Parse argc/argv argument vector. Called by user level routines. 312 | */ 313 | static int 314 | getopt_internal(int nargc, char * const *nargv, const char *options, 315 | const struct option *long_options, int *idx, int flags) 316 | { 317 | const char *oli; /* option letter list index */ 318 | int optchar, short_too; 319 | static int posixly_correct = -1; 320 | 321 | if (options == NULL) 322 | return (-1); 323 | 324 | /* 325 | * XXX Some GNU programs (like cvs) set optind to 0 instead of 326 | * XXX using optreset. Work around this braindamage. 327 | */ 328 | if (optind == 0) 329 | optind = optreset = 1; 330 | 331 | /* 332 | * Disable GNU extensions if POSIXLY_CORRECT is set or options 333 | * string begins with a '+'. 334 | * 335 | * CV, 2009-12-14: Check POSIXLY_CORRECT anew if optind == 0 or 336 | * optreset != 0 for GNU compatibility. 337 | */ 338 | if (posixly_correct == -1 || optreset != 0) 339 | posixly_correct = (getenv("POSIXLY_CORRECT") != NULL); 340 | if (*options == '-') 341 | flags |= FLAG_ALLARGS; 342 | else if (posixly_correct || *options == '+') 343 | flags &= ~FLAG_PERMUTE; 344 | if (*options == '+' || *options == '-') 345 | options++; 346 | 347 | optarg = NULL; 348 | if (optreset) 349 | nonopt_start = nonopt_end = -1; 350 | start: 351 | if (optreset || !*place) { /* update scanning pointer */ 352 | optreset = 0; 353 | if (optind >= nargc) { /* end of argument vector */ 354 | place = EMSG; 355 | if (nonopt_end != -1) { 356 | /* do permutation, if we have to */ 357 | permute_args(nonopt_start, nonopt_end, 358 | optind, nargv); 359 | optind -= nonopt_end - nonopt_start; 360 | } 361 | else if (nonopt_start != -1) { 362 | /* 363 | * If we skipped non-options, set optind 364 | * to the first of them. 365 | */ 366 | optind = nonopt_start; 367 | } 368 | nonopt_start = nonopt_end = -1; 369 | return (-1); 370 | } 371 | if (*(place = nargv[optind]) != '-' || 372 | (place[1] == '\0' && strchr(options, '-') == NULL)) { 373 | place = EMSG; /* found non-option */ 374 | if (flags & FLAG_ALLARGS) { 375 | /* 376 | * GNU extension: 377 | * return non-option as argument to option 1 378 | */ 379 | optarg = nargv[optind++]; 380 | return (INORDER); 381 | } 382 | if (!(flags & FLAG_PERMUTE)) { 383 | /* 384 | * If no permutation wanted, stop parsing 385 | * at first non-option. 386 | */ 387 | return (-1); 388 | } 389 | /* do permutation */ 390 | if (nonopt_start == -1) 391 | nonopt_start = optind; 392 | else if (nonopt_end != -1) { 393 | permute_args(nonopt_start, nonopt_end, 394 | optind, nargv); 395 | nonopt_start = optind - 396 | (nonopt_end - nonopt_start); 397 | nonopt_end = -1; 398 | } 399 | optind++; 400 | /* process next argument */ 401 | goto start; 402 | } 403 | if (nonopt_start != -1 && nonopt_end == -1) 404 | nonopt_end = optind; 405 | 406 | /* 407 | * If we have "-" do nothing, if "--" we are done. 408 | */ 409 | if (place[1] != '\0' && *++place == '-' && place[1] == '\0') { 410 | optind++; 411 | place = EMSG; 412 | /* 413 | * We found an option (--), so if we skipped 414 | * non-options, we have to permute. 415 | */ 416 | if (nonopt_end != -1) { 417 | permute_args(nonopt_start, nonopt_end, 418 | optind, nargv); 419 | optind -= nonopt_end - nonopt_start; 420 | } 421 | nonopt_start = nonopt_end = -1; 422 | return (-1); 423 | } 424 | } 425 | 426 | /* 427 | * Check long options if: 428 | * 1) we were passed some 429 | * 2) the arg is not just "-" 430 | * 3) either the arg starts with -- we are getopt_long_only() 431 | */ 432 | if (long_options != NULL && place != nargv[optind] && 433 | (*place == '-' || (flags & FLAG_LONGONLY))) { 434 | short_too = 0; 435 | if (*place == '-') 436 | place++; /* --foo long option */ 437 | else if (*place != ':' && strchr(options, *place) != NULL) 438 | short_too = 1; /* could be short option too */ 439 | 440 | optchar = parse_long_options(nargv, options, long_options, 441 | idx, short_too); 442 | if (optchar != -1) { 443 | place = EMSG; 444 | return (optchar); 445 | } 446 | } 447 | 448 | if ((optchar = (int)*place++) == (int)':' || 449 | (optchar == (int)'-' && *place != '\0') || 450 | (oli = strchr(options, optchar)) == NULL) { 451 | /* 452 | * If the user specified "-" and '-' isn't listed in 453 | * options, return -1 (non-option) as per POSIX. 454 | * Otherwise, it is an unknown option character (or ':'). 455 | */ 456 | if (optchar == (int)'-' && *place == '\0') 457 | return (-1); 458 | if (!*place) 459 | ++optind; 460 | if (PRINT_ERROR) 461 | warnx(illoptchar, optchar); 462 | optopt = optchar; 463 | return (BADCH); 464 | } 465 | if (long_options != NULL && optchar == 'W' && oli[1] == ';') { 466 | /* -W long-option */ 467 | if (*place) /* no space */ 468 | /* NOTHING */; 469 | else if (++optind >= nargc) { /* no arg */ 470 | place = EMSG; 471 | if (PRINT_ERROR) 472 | warnx(recargchar, optchar); 473 | optopt = optchar; 474 | return (BADARG); 475 | } else /* white space */ 476 | place = nargv[optind]; 477 | optchar = parse_long_options(nargv, options, long_options, 478 | idx, 0); 479 | place = EMSG; 480 | return (optchar); 481 | } 482 | if (*++oli != ':') { /* doesn't take argument */ 483 | if (!*place) 484 | ++optind; 485 | } else { /* takes (optional) argument */ 486 | optarg = NULL; 487 | if (*place) /* no white space */ 488 | optarg = place; 489 | else if (oli[1] != ':') { /* arg not optional */ 490 | if (++optind >= nargc) { /* no arg */ 491 | place = EMSG; 492 | if (PRINT_ERROR) 493 | warnx(recargchar, optchar); 494 | optopt = optchar; 495 | return (BADARG); 496 | } else 497 | optarg = nargv[optind]; 498 | } 499 | place = EMSG; 500 | ++optind; 501 | } 502 | /* dump back option letter */ 503 | return (optchar); 504 | } 505 | 506 | #ifdef REPLACE_GETOPT 507 | /* 508 | * getopt -- 509 | * Parse argc/argv argument vector. 510 | * 511 | * [eventually this will replace the BSD getopt] 512 | */ 513 | int 514 | getopt(int nargc, char * const *nargv, const char *options) 515 | { 516 | 517 | /* 518 | * We don't pass FLAG_PERMUTE to getopt_internal() since 519 | * the BSD getopt(3) (unlike GNU) has never done this. 520 | * 521 | * Furthermore, since many privileged programs call getopt() 522 | * before dropping privileges it makes sense to keep things 523 | * as simple (and bug-free) as possible. 524 | */ 525 | return (getopt_internal(nargc, nargv, options, NULL, NULL, 0)); 526 | } 527 | #endif /* REPLACE_GETOPT */ 528 | 529 | /* 530 | * getopt_long -- 531 | * Parse argc/argv argument vector. 532 | */ 533 | int 534 | getopt_long(int nargc, char * const *nargv, const char *options, 535 | const struct option *long_options, int *idx) 536 | { 537 | 538 | return (getopt_internal(nargc, nargv, options, long_options, idx, 539 | FLAG_PERMUTE)); 540 | } 541 | 542 | /* 543 | * getopt_long_only -- 544 | * Parse argc/argv argument vector. 545 | */ 546 | int 547 | getopt_long_only(int nargc, char * const *nargv, const char *options, 548 | const struct option *long_options, int *idx) 549 | { 550 | 551 | return (getopt_internal(nargc, nargv, options, long_options, idx, 552 | FLAG_PERMUTE|FLAG_LONGONLY)); 553 | } 554 | -------------------------------------------------------------------------------- /common_src/getopt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * DISCLAIMER 3 | * This file has no copyright assigned and is placed in the Public Domain. 4 | * This file is a part of the w64 mingw-runtime package. 5 | * 6 | * The w64 mingw-runtime package and its code is distributed in the hope that it 7 | * will be useful but WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESSED OR 8 | * IMPLIED ARE HEREBY DISCLAIMED. This includes but is not limited to 9 | * warranties of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | */ 11 | #if !defined(__GETOPT_H__) 12 | #define __GETOPT_H__ 13 | 14 | /* All the headers include this file. */ 15 | //#include 16 | 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | extern int optind; /* index of first non-option in argv */ 23 | extern int optopt; /* single option character, as parsed */ 24 | extern int opterr; /* flag to enable built-in diagnostics... */ 25 | /* (user may set to zero, to suppress) */ 26 | 27 | extern char *optarg; /* pointer to argument of current option */ 28 | 29 | extern int getopt(int nargc, char * const *nargv, const char *options); 30 | 31 | #ifdef _BSD_SOURCE 32 | /* 33 | * BSD adds the non-standard `optreset' feature, for reinitialisation 34 | * of `getopt' parsing. We support this feature, for applications which 35 | * proclaim their BSD heritage, before including this header; however, 36 | * to maintain portability, developers are advised to avoid it. 37 | */ 38 | # define optreset __mingw_optreset 39 | extern int optreset; 40 | #endif 41 | #ifdef __cplusplus 42 | } 43 | #endif 44 | /* 45 | * POSIX requires the `getopt' API to be specified in `unistd.h'; 46 | * thus, `unistd.h' includes this header. However, we do not want 47 | * to expose the `getopt_long' or `getopt_long_only' APIs, when 48 | * included in this manner. Thus, close the standard __GETOPT_H__ 49 | * declarations block, and open an additional __GETOPT_LONG_H__ 50 | * specific block, only when *not* __UNISTD_H_SOURCED__, in which 51 | * to declare the extended API. 52 | */ 53 | #endif /* !defined(__GETOPT_H__) */ 54 | 55 | #if !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) 56 | #define __GETOPT_LONG_H__ 57 | 58 | #ifdef __cplusplus 59 | extern "C" { 60 | #endif 61 | 62 | struct option /* specification for a long form option... */ 63 | { 64 | const char *name; /* option name, without leading hyphens */ 65 | int has_arg; /* does it take an argument? */ 66 | int *flag; /* where to save its status, or NULL */ 67 | int val; /* its associated status value */ 68 | }; 69 | 70 | enum /* permitted values for its `has_arg' field... */ 71 | { 72 | no_argument = 0, /* option never takes an argument */ 73 | required_argument, /* option always requires an argument */ 74 | optional_argument /* option may take an argument */ 75 | }; 76 | 77 | extern int getopt_long(int nargc, char * const *nargv, const char *options, 78 | const struct option *long_options, int *idx); 79 | extern int getopt_long_only(int nargc, char * const *nargv, const char *options, 80 | const struct option *long_options, int *idx); 81 | /* 82 | * Previous MinGW implementation had... 83 | */ 84 | #ifndef HAVE_DECL_GETOPT 85 | /* 86 | * ...for the long form API only; keep this for compatibility. 87 | */ 88 | # define HAVE_DECL_GETOPT 1 89 | #endif 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif /* !defined(__UNISTD_H_SOURCED__) && !defined(__GETOPT_LONG_H__) */ 96 | -------------------------------------------------------------------------------- /common_src/midi_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | */ 9 | 10 | #ifndef __MIDI_DEV_H__ 11 | #define __MIDI_DEV_H__ 12 | 13 | int mididev_init(void); 14 | int mididev_deinit(void); 15 | 16 | int mididev_send_bytes(const unsigned char *buf, int len); 17 | 18 | void mididev_print_usage(void); 19 | 20 | void mididev_add_optstr(char *optstr); 21 | int mididev_parse_arg(int c, const char *optarg); 22 | 23 | #endif /* __MIDI_DEV_H__ */ 24 | -------------------------------------------------------------------------------- /common_src/miniwift.h: -------------------------------------------------------------------------------- 1 | /* 2 | * "miniwi" is a 4x8 pixel bitmap font 3 | * 4 | * Copyright (C) 2015-2016 Josuah Demangeon 5 | * , https://git.z0.is/miniwi/ 6 | * 7 | * 8 | */ 9 | 10 | #ifndef __MINIWIFT_H__ 11 | #define __MINIWIFT_H__ 12 | 13 | /* 14 | * The printable 7-bit ASCII part of miniwi 15 | * as a C array with bitmap data. 2 glyphs 16 | * are packed together per 8 Bytes by using 17 | * the high nibbles for glyph 1 and the low 18 | * nibbles for glyph 2. 19 | * 20 | * Size: 384 Bytes 21 | */ 22 | static unsigned char miniwi_data[48][8] = { 23 | { 0x00, 0x04, 0x04, 0x04, 0x00, 0x04, 0x00, 0x00 }, 24 | { 0x00, 0xA6, 0xAF, 0x06, 0x0F, 0x06, 0x00, 0x00 }, 25 | { 0x00, 0x4A, 0xE2, 0xC4, 0x68, 0xEA, 0x40, 0x00 }, 26 | { 0x00, 0x44, 0xA4, 0x40, 0xA0, 0x60, 0x00, 0x00 }, 27 | { 0x24, 0x42, 0x42, 0x42, 0x42, 0x42, 0x24, 0x00 }, 28 | { 0x00, 0xA0, 0x44, 0xEE, 0x44, 0xA0, 0x00, 0x00 }, 29 | { 0x00, 0x00, 0x00, 0x0E, 0x00, 0x40, 0x80, 0x00 }, 30 | { 0x02, 0x02, 0x04, 0x04, 0x04, 0x48, 0x08, 0x00 }, 31 | { 0x00, 0xE4, 0xEC, 0xA4, 0xE4, 0xEE, 0x00, 0x00 }, 32 | { 0x00, 0xEE, 0x22, 0xEE, 0x82, 0xEE, 0x00, 0x00 }, 33 | { 0x00, 0xAE, 0xA8, 0xEE, 0x22, 0x2E, 0x00, 0x00 }, 34 | { 0x00, 0xEE, 0x82, 0xE2, 0xA2, 0xE2, 0x00, 0x00 }, 35 | { 0x00, 0xEE, 0xAA, 0xEE, 0xA2, 0xEE, 0x00, 0x00 }, 36 | { 0x00, 0x00, 0x44, 0x00, 0x00, 0x44, 0x08, 0x00 }, 37 | { 0x00, 0x20, 0x4E, 0x80, 0x4E, 0x20, 0x00, 0x00 }, 38 | { 0x00, 0x8E, 0x42, 0x24, 0x40, 0x84, 0x00, 0x00 }, 39 | { 0x00, 0xCE, 0x2A, 0xAA, 0xAE, 0x4A, 0x00, 0x00 }, 40 | { 0x00, 0xCE, 0xA8, 0xC8, 0xA8, 0xCE, 0x00, 0x00 }, 41 | { 0x00, 0xCE, 0xA8, 0xAE, 0xA8, 0xCE, 0x00, 0x00 }, 42 | { 0x00, 0xEE, 0x88, 0xE8, 0x8A, 0x8E, 0x00, 0x00 }, 43 | { 0x00, 0xAE, 0xA4, 0xE4, 0xA4, 0xAE, 0x00, 0x00 }, 44 | { 0x00, 0x2A, 0x2A, 0x2C, 0xAA, 0xEA, 0x00, 0x00 }, 45 | { 0x00, 0x8E, 0x8E, 0x8E, 0x8A, 0xEA, 0x00, 0x00 }, 46 | { 0x00, 0xAE, 0xEA, 0xEA, 0xEA, 0xAE, 0x00, 0x00 }, 47 | { 0x00, 0xEE, 0xAA, 0xEA, 0x8E, 0x8E, 0x02, 0x00 }, 48 | { 0x00, 0xEE, 0xA8, 0xC4, 0xA2, 0xAE, 0x00, 0x00 }, 49 | { 0x00, 0xEA, 0x4A, 0x4A, 0x4A, 0x4E, 0x00, 0x00 }, 50 | { 0x00, 0xAA, 0xAA, 0xAE, 0xAE, 0x4E, 0x00, 0x00 }, 51 | { 0x00, 0xAA, 0xAA, 0x44, 0xA4, 0xA4, 0x00, 0x00 }, 52 | { 0x06, 0xE4, 0x24, 0x44, 0x84, 0xE4, 0x06, 0x00 }, 53 | { 0x86, 0x82, 0x42, 0x42, 0x42, 0x22, 0x26, 0x00 }, 54 | { 0x40, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F }, 55 | { 0x80, 0x40, 0x2E, 0x02, 0x0E, 0x0E, 0x00, 0x00 }, 56 | { 0x80, 0x80, 0xEE, 0xA8, 0xA8, 0xEE, 0x00, 0x00 }, 57 | { 0x20, 0x20, 0xEE, 0xAE, 0xA8, 0xEE, 0x00, 0x00 }, 58 | { 0x60, 0x40, 0xEE, 0x4A, 0x4A, 0x4E, 0x02, 0x0E }, 59 | { 0x84, 0x80, 0xE4, 0xA4, 0xA4, 0xA4, 0x00, 0x00 }, 60 | { 0x48, 0x08, 0x4A, 0x4C, 0x4C, 0x4A, 0x40, 0xC0 }, 61 | { 0x40, 0x40, 0x4E, 0x4E, 0x4E, 0x6A, 0x00, 0x00 }, 62 | { 0x00, 0x00, 0xEE, 0xAA, 0xAA, 0xAE, 0x00, 0x00 }, 63 | { 0x00, 0x00, 0xEE, 0xAA, 0xAA, 0xEE, 0x82, 0x82 }, 64 | { 0x00, 0x00, 0xEE, 0x88, 0x82, 0x8E, 0x00, 0x00 }, 65 | { 0x00, 0x40, 0xEA, 0x4A, 0x4A, 0x6E, 0x00, 0x00 }, 66 | { 0x00, 0x00, 0xAA, 0xAE, 0xAE, 0x4E, 0x00, 0x00 }, 67 | { 0x00, 0x00, 0xAA, 0x4A, 0x4A, 0xAE, 0x02, 0x0E }, 68 | { 0x06, 0x04, 0xE4, 0x28, 0x84, 0xE4, 0x06, 0x00 }, 69 | { 0x4C, 0x44, 0x44, 0x42, 0x44, 0x44, 0x4C, 0x00 }, 70 | { 0x0E, 0x0A, 0x0A, 0xCA, 0x6A, 0x0A, 0x0E, 0x00 } 71 | }; 72 | 73 | #endif /* __MINIWIFT_H__ */ 74 | 75 | -------------------------------------------------------------------------------- /common_src/mt32-pi.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi control program for DOS PCs 3 | * 4 | * Copyright (C) 2021 Andreas Zdziarstek 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are met: 8 | * 9 | * 1. Redistributions of source code must retain the above copyright notice, 10 | * this list of conditions and the following disclaimer. 11 | * 12 | * 2. Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 | * POSSIBILITY OF SUCH DAMAGE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include "midi_dev.h" 35 | #include "getopt.h" 36 | #include "delay.h" 37 | #include "miniwift.h" 38 | 39 | #ifndef PROGRAM_NAME 40 | #define PROGRAM_NAME "MT32-PI.EXE" 41 | #endif 42 | 43 | typedef enum { 44 | MODE_UNCHANGED, 45 | MODE_MUNT, 46 | MODE_FLUID 47 | } mt32pi_mode_t; 48 | 49 | typedef enum { 50 | ROM_UNCHANGED, 51 | ROM_OLD, 52 | ROM_NEW, 53 | ROM_CM32L 54 | } mt32pi_romset_t; 55 | 56 | static void str_to_sysex_disp_mt32(unsigned char *sysexbuf, const char *str); 57 | static int str_to_sysex_disp_sc55(unsigned char *sysexbuf, const char *str); 58 | static void bmp_to_sysex_disp_sc55(unsigned char *se_array, const char *fname, int negative); 59 | static void strpic_to_sysex_disp_sc55(unsigned char *sysexbuf, const char *str, int negative); 60 | 61 | static unsigned char roland_checksum(const unsigned char *buf, unsigned short len); 62 | 63 | static void print_usage(void); 64 | 65 | static unsigned char cmd_sysex[5] = {0xF0, 0x7D, 0x00, 0x00, 0xF7}; 66 | static unsigned char reboot_sysex[4] = {0xF0, 0x7D, 0x00, 0xF7}; 67 | 68 | static unsigned char gm_reset[6] = {0xF0, 0x7E, 0x7F, 0x09, 0x01, 0xF7}; 69 | static unsigned char gs_reset[11] = {0xF0, 0x41, 0x10, 0x42, 0x12, 0x40, 0x00, 0x7F, 0x00, 0x41, 0xF7}; 70 | static unsigned char mt32_reset[8] = {0xF0, 0x41, 0x10, 0x16, 0x12, 0x7F, 0x01, 0xF7}; 71 | 72 | static unsigned char custom_midi_buf[64]; 73 | static int custom_midi_len = 0; 74 | 75 | static char sc55_bmp_fname[64] = {'\0'}; 76 | static char syx_fname[64] = {'\0'}; 77 | 78 | static unsigned char sysexbuf[74]; 79 | static char mt32_text[21] = {'\0'}; 80 | static char sc55_text[33] = {'\0'}; 81 | static char sc55_pictxt[9] = {'\0'}; 82 | 83 | 84 | int main(int argc, char *argv[]) { 85 | // static linkage because of C89 and struct 86 | // initialization for long_options[] 87 | static int reboot_flag = 0, 88 | mt32_rst_flag = 0, 89 | gs_rst_flag = 0, 90 | gm_rst_flag = 0, 91 | pic_negative_flag = 0, 92 | verbose = 0, 93 | soundfont = -1, 94 | mt32_rstereo = -1; 95 | static mt32pi_mode_t mode = MODE_UNCHANGED; 96 | static mt32pi_romset_t romset = ROM_UNCHANGED; 97 | #ifdef ARGV_NO_BASENAME 98 | argv[0] = PROGRAM_NAME; 99 | #endif 100 | 101 | int c; 102 | static struct option long_options[] = 103 | { 104 | {"verbose", no_argument, &verbose, 1}, 105 | {"reboot", no_argument, &reboot_flag, 1}, 106 | {"help", no_argument, 0, 'h'}, 107 | {"mt32", no_argument, 0, 'm'}, 108 | {"mt32-rstereo", required_argument, 0, 'S'}, 109 | {"fluidsynth", no_argument, 0, 'g'}, 110 | {"soundfont", required_argument, 0, 's'}, 111 | {"mt32-reset", no_argument, &mt32_rst_flag, 1}, 112 | {"gm-reset", no_argument, &gm_rst_flag, 1}, 113 | {"gs-reset", no_argument, &gs_rst_flag, 1}, 114 | {"mt32-txt", required_argument, 0, 't'}, 115 | {"sc55-txt", required_argument, 0, 'T'}, 116 | {"sc55-bmp", required_argument, 0, 'P'}, 117 | {"sc55-btxt", required_argument, 0, 'X'}, 118 | {"negative", required_argument, 0, 'N'}, 119 | {"romset", required_argument, 0, 'b'}, 120 | {"midi", required_argument, 0, 'M'}, 121 | {"syx", required_argument, 0, 'Y'}, 122 | {0, 0, 0, 0} 123 | }; 124 | char optstr[32] = "hs:S:t:T:b:mgrvM:P:NY:X:"; 125 | 126 | // The MIDI backend may add some more short options 127 | // Be sure that strlen(optstr) remains <32 128 | // Could also generate new string of arbitrary 129 | // but don't really wanna mess with malloc/free 130 | // for this bit 131 | mididev_add_optstr(optstr); 132 | 133 | opterr = 1; 134 | if(argc == 1) { 135 | print_usage(); 136 | #ifndef NO_ERROR_ON_HELP 137 | return EXIT_FAILURE; 138 | #else 139 | return EXIT_SUCCESS; 140 | #endif 141 | } 142 | 143 | while(1) { 144 | 145 | c = getopt_long(argc, argv, optstr, long_options, NULL); 146 | 147 | // End of options 148 | if(c == -1) 149 | break; 150 | 151 | switch(c) { 152 | case 0: 153 | break; 154 | case 's': 155 | soundfont = (int)strtol(optarg, NULL, 10); 156 | break; 157 | case 'S': 158 | mt32_rstereo = (int)strtol(optarg, NULL, 10); 159 | break; 160 | case 't': 161 | strncpy(mt32_text, optarg, 20); 162 | break; 163 | case 'T': 164 | strncpy(sc55_text, optarg, 32); 165 | break; 166 | case 'b': 167 | if(strcmp(optarg, "old")==0) { 168 | romset = ROM_OLD; 169 | } else if(strcmp(optarg, "new")==0) { 170 | romset = ROM_NEW; 171 | } else if(strcmp(optarg, "cm32l")==0) { 172 | romset = ROM_CM32L; 173 | } else { 174 | fprintf(stderr, "%s is not a recognized romset.\n", optarg); 175 | return EXIT_FAILURE; 176 | } 177 | break; 178 | case 'm': 179 | mode = MODE_MUNT; 180 | break; 181 | case 'g': 182 | mode = MODE_FLUID; 183 | break; 184 | case 'r': 185 | reboot_flag = 1; 186 | break; 187 | case 'M': 188 | { 189 | unsigned char *mptr = custom_midi_buf; 190 | char *end; 191 | while(1) { 192 | errno = 0; 193 | *mptr = (unsigned char)strtoul(optarg, &end, 16); 194 | if(errno || end==optarg) 195 | break; 196 | custom_midi_len++; 197 | mptr++; 198 | if(mptr == custom_midi_buf+64 || *end == '\0') 199 | break; 200 | optarg = end; 201 | } 202 | } 203 | break; 204 | case 'P': 205 | if(strlen(optarg) > 63) { 206 | fprintf(stderr, "Sorry, the filename is too long!\n"); 207 | return EXIT_FAILURE; 208 | } 209 | strncpy(sc55_bmp_fname, optarg, 64); 210 | break; 211 | case 'X': 212 | strncpy(sc55_pictxt, optarg, 8); 213 | break; 214 | case 'N': 215 | pic_negative_flag = 1; 216 | break; 217 | case 'Y': 218 | if(strlen(optarg) > 63) { 219 | fprintf(stderr, "Sorry, the filename is too long!\n"); 220 | return EXIT_FAILURE; 221 | } 222 | strncpy(syx_fname, optarg, 64); 223 | break; 224 | case 'v': 225 | verbose = 1; 226 | break; 227 | case ':': 228 | case '?': 229 | case 'h': 230 | print_usage(); 231 | #ifndef NO_ERROR_ON_HELP 232 | return EXIT_FAILURE; 233 | #else 234 | return EXIT_SUCCESS; 235 | #endif 236 | default: 237 | if(mididev_parse_arg(c, optarg)<0) { 238 | fprintf(stderr, "Error parsing argument \"%s\"\n", optarg); 239 | return EXIT_FAILURE; 240 | } 241 | } 242 | 243 | } 244 | 245 | // Reset/init the MIDI interface 246 | if(mididev_init() == -1) { 247 | fprintf(stderr, "Error initializing midi interface.\n"); 248 | return EXIT_FAILURE; 249 | } 250 | 251 | // -r/--reboot 252 | if(reboot_flag) { 253 | if(verbose) 254 | fprintf(stderr, "Rebooting MT32-PI and waiting 5 seconds.\n"); 255 | mididev_send_bytes(reboot_sysex, 4); 256 | delay_ms(5000); 257 | } 258 | 259 | // -m/--mt32 and -g/--fluidsynth 260 | if(mode==MODE_MUNT) { 261 | if(verbose) 262 | fprintf(stderr, "Switching to MT-32 mode.\n"); 263 | cmd_sysex[2] = 0x03; 264 | cmd_sysex[3] = 0x00; 265 | mididev_send_bytes(cmd_sysex, 5); 266 | } else if(mode==MODE_FLUID) { 267 | if(verbose) 268 | fprintf(stderr, "Switching to FluidSynth mode.\n"); 269 | cmd_sysex[2] = 0x03; 270 | cmd_sysex[3] = 0x01; 271 | mididev_send_bytes(cmd_sysex, 5); 272 | } 273 | 274 | // -b/--romset 275 | if(romset==ROM_OLD) { 276 | if(verbose) 277 | fprintf(stderr, "Switching to old MT-32 romset.\n"); 278 | cmd_sysex[2] = 0x01; 279 | cmd_sysex[3] = 0x00; 280 | mididev_send_bytes(cmd_sysex, 5); 281 | } else if(romset==ROM_NEW) { 282 | if(verbose) 283 | fprintf(stderr, "Switching to new MT-32 romset.\n"); 284 | cmd_sysex[2] = 0x01; 285 | cmd_sysex[3] = 0x01; 286 | mididev_send_bytes(cmd_sysex, 5); 287 | } else if(romset==ROM_CM32L) { 288 | if(verbose) 289 | fprintf(stderr, "Switching to CM-32L romset.\n"); 290 | cmd_sysex[2] = 0x01; 291 | cmd_sysex[3] = 0x02; 292 | mididev_send_bytes(cmd_sysex, 5); 293 | } 294 | 295 | // -s/--soundfont 296 | if(soundfont > -1) { 297 | if(verbose) 298 | fprintf(stderr, "Switching SoundFont to #%d.\n", soundfont); 299 | cmd_sysex[2] = 0x02; 300 | cmd_sysex[3] = soundfont; 301 | mididev_send_bytes(cmd_sysex, 5); 302 | } 303 | 304 | // -S/--mt32-rstereo 305 | if(mt32_rstereo > -1) { 306 | if(verbose) 307 | fprintf(stderr, "%sabling MT-32 reversed stereo mode.\n", mt32_rstereo ? "En" : "Dis"); 308 | cmd_sysex[2] = 0x04; 309 | cmd_sysex[3] = mt32_rstereo; 310 | mididev_send_bytes(cmd_sysex, 5); 311 | } 312 | 313 | // --mt32-reset 314 | if(mt32_rst_flag) { 315 | if(verbose) 316 | fprintf(stderr, "Sending MT-32 reset and waiting 100ms.\n"); 317 | mididev_send_bytes(mt32_reset, 8); 318 | delay_ms(100); 319 | } 320 | 321 | // --gm-reset 322 | if(gm_rst_flag) { 323 | if(verbose) 324 | fprintf(stderr, "Sending GM reset and waiting 100ms.\n"); 325 | mididev_send_bytes(gm_reset, 6); 326 | delay_ms(100); 327 | } 328 | 329 | // --gs-reset 330 | if(gs_rst_flag) { 331 | if(verbose) 332 | fprintf(stderr, "Sending GS reset and waiting 100ms.\n"); 333 | mididev_send_bytes(gs_reset, 11); 334 | delay_ms(100); 335 | } 336 | 337 | // -t/--mt32-txt 338 | if(mt32_text[0] != '\0') { 339 | if(verbose) 340 | fprintf(stderr, "Displaying \"%s\" (MT-32 mode).\n", mt32_text); 341 | str_to_sysex_disp_mt32(sysexbuf, mt32_text); 342 | mididev_send_bytes(sysexbuf, 30); 343 | } 344 | 345 | // -T/--sc55-txt 346 | if(sc55_text[0] != '\0') { 347 | int se_len; 348 | if(verbose) 349 | fprintf(stderr, "Displaying \"%s\" (SC-55 mode).\n", sc55_text); 350 | se_len = str_to_sysex_disp_sc55(sysexbuf, sc55_text); 351 | mididev_send_bytes(sysexbuf, se_len); 352 | } 353 | 354 | // -M/--midi "C0 01 C0 DE" 355 | if(custom_midi_len > 0) { 356 | if(verbose) 357 | fprintf(stderr, "Sending %d custom MIDI bytes.\n", custom_midi_len); 358 | mididev_send_bytes(custom_midi_buf, custom_midi_len); 359 | } 360 | 361 | // -P/--sc55-bmp FILE.BMP and -N 362 | if(sc55_bmp_fname[0] != '\0') { 363 | if(verbose) 364 | fprintf(stderr, "Displaying %s.\n", sc55_bmp_fname); 365 | bmp_to_sysex_disp_sc55(sysexbuf, sc55_bmp_fname, pic_negative_flag); 366 | mididev_send_bytes(sysexbuf, 74); 367 | } 368 | 369 | // -X/--sc55-btxt 370 | if(sc55_pictxt[0] != '\0') { 371 | if(verbose) 372 | fprintf(stderr, "Displaying \"%s\" using bitmap mode.\n", sc55_pictxt); 373 | strpic_to_sysex_disp_sc55(sysexbuf, sc55_pictxt, pic_negative_flag); 374 | mididev_send_bytes(sysexbuf, 74); 375 | } 376 | 377 | // -Y/--syx FILE.SYX 378 | if(syx_fname[0] != '\0') { 379 | unsigned char fbuf[512]; 380 | FILE *fh; 381 | if(verbose) 382 | fprintf(stderr, "Sending %s.\n", syx_fname); 383 | 384 | 385 | fh = fopen(syx_fname, "rb"); 386 | if(fh == NULL) { 387 | fprintf(stderr, "ERROR: Can't open %s.\n", syx_fname); 388 | mididev_deinit(); 389 | return EXIT_FAILURE; 390 | } 391 | 392 | // go through file 393 | while(1) { 394 | int i, found_start, found_end, start_index, end_index; 395 | int n; 396 | long int read_start = ftell(fh); 397 | n = fread(fbuf, 1, 512, fh); 398 | 399 | found_start = 0; 400 | found_end = 0; 401 | // search for next sysex in buffer 402 | start_index = 0; 403 | end_index = 0; 404 | for(i=0; i>3) & 0x1F; 545 | sysexrow[16] = ((bmprow[0]&0x07)<<2) | ((bmprow[1]>>6)&0x03); 546 | sysexrow[32] = (bmprow[1]>>1)&0x1F; 547 | sysexrow[48] = (bmprow[1] & 0x01)<<4; 548 | if(negative) { 549 | sysexrow[0] = (~sysexrow[0])&0x1F; 550 | sysexrow[16] = (~sysexrow[16])&0x1F; 551 | sysexrow[32] = (~sysexrow[32])&0x1F; 552 | sysexrow[48] = (~sysexrow[48])&0x10; 553 | } 554 | } 555 | sysexbuf[72] = roland_checksum(sysexbuf+5, 67); 556 | sysexbuf[73] = 0xF7; 557 | } 558 | 559 | static void strpic_to_sysex_disp_sc55(unsigned char *sysexbuf, const char *str, int negative) { 560 | const unsigned char prefix[7] = { 0xF0, 0x41, 0x10, 0x45, 0x12, 0x10, 0x01 }; 561 | int i, slen; 562 | unsigned char picbuf[32]; 563 | memset(sysexbuf, '\0', 74); 564 | memset(picbuf, 0, sizeof(picbuf)); 565 | /* Copy SysEx start and display cmd into msg */ 566 | memcpy(sysexbuf, prefix, 7); 567 | slen = strlen(str); 568 | if(slen > 8) { 569 | slen = 8; 570 | } 571 | 572 | for(i=0; i 127) { 576 | continue; 577 | } 578 | c = str[i] - 32; 579 | src_rshift = (c & 1) ? 0 : 4; 580 | dest_lshift = (i & 1) ? 0 : 4; 581 | destptr = (i<4) ? &picbuf[i/2] : &picbuf[(i-4)/2 + 16]; 582 | for(j=0; j<8; j++) { 583 | destptr[j*2] |= ( (miniwi_data[c/2][j]>>src_rshift ) & 0x0F ) <>3) & 0x1F; 594 | sysexrow[16] = ((bmprow[0]&0x07)<<2) | ((bmprow[1]>>6)&0x03); 595 | sysexrow[32] = (bmprow[1]>>1)&0x1F; 596 | sysexrow[48] = (bmprow[1] & 0x01)<<4; 597 | if(negative) { 598 | sysexrow[0] = (~sysexrow[0])&0x1F; 599 | sysexrow[16] = (~sysexrow[16])&0x1F; 600 | sysexrow[32] = (~sysexrow[32])&0x1F; 601 | sysexrow[48] = (~sysexrow[48])&0x10; 602 | } 603 | } 604 | sysexbuf[72] = roland_checksum(sysexbuf+5, 67); 605 | sysexbuf[73] = 0xF7; 606 | } 607 | 608 | static unsigned char roland_checksum(const unsigned char *buf, unsigned short len) { 609 | const unsigned char *ptr; 610 | unsigned short ck_acc = 0; 611 | for(ptr=buf; ptr (miniwi font), 46 | Copyright (C) 2014-2018 Mateusz Viste (MPU401 lib), 47 | Copyright (c) 2002 Todd C. Miller (getopt). 48 | and Copyright (c) 2000 The NetBSD Foundation, Inc. (getopt) 49 | 50 | See the individual source files for details. 51 | 52 | Redistribution and use in source and binary forms, with or without 53 | modification, are permitted provided that the following conditions are met: 54 | 55 | 1. Redistributions of source code must retain the above copyright notice, 56 | this list of conditions and the following disclaimer. 57 | 58 | 2. Redistributions in binary form must reproduce the above copyright 59 | notice, this list of conditions and the following disclaimer in the 60 | documentation and/or other materials provided with the distribution. 61 | 62 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 63 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 64 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 65 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 66 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 67 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 68 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 69 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 70 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 71 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 72 | POSSIBILITY OF SUCH DAMAGE. 73 | 74 | -------------------------------------------------------------------------------- /dos_bin/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/dos_bin/mt32pi.bmp -------------------------------------------------------------------------------- /dos_src/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Copyright (C) 2021 Andreas Zdziarstek 3 | with parts 4 | Copyright (C) 2014-2018 Mateusz Viste (MPU401 lib), 5 | Copyright (c) 2002 Todd C. Miller (getopt). 6 | and Copyright (c) 2000 The NetBSD Foundation, Inc. (getopt) 7 | 8 | See the individual source files for details. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | 1. Redistributions of source code must retain the above copyright notice, 14 | this list of conditions and the following disclaimer. 15 | 16 | 2. Redistributions in binary form must reproduce the above copyright 17 | notice, this list of conditions and the following disclaimer in the 18 | documentation and/or other materials provided with the distribution. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 24 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 | POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /dos_src/MAKEFILE: -------------------------------------------------------------------------------- 1 | # 2 | # MT32-PI.EXE Makefile for OpenWatcom 3 | # Copyright (C) 2020 Andreas Zdziarstek 4 | # 5 | # adapted from the DOSMid Makefile for OpenWatcom 6 | # Copyright (C) 2014-2018 Mateusz Viste 7 | # 8 | 9 | # memory segmentation mode (s = small ; c = compact ; m = medium ; l = large) 10 | # code | data 11 | # small 64K | 64K 12 | # compact 64K | 64K+ 13 | # medium 64K+ | 64K 14 | # large 64K+ | 64K+ 15 | MODE = s 16 | 17 | all: mt32-pi.exe 18 | 19 | mt32-pi.exe: ..\COMMON~1\mt32-pi.c mpu401.c midi_dev.c ..\COMMON~1\getopt.c 20 | *wcl -i=..\COMMON~1\ -zp2 -lr -we -d0 -y -0 -s -m$(MODE) -wx -dUSE_CUSTOM_DELAY -dREPLACE_GETOPT -fe=mt32-pi.exe -fm=mt32-pi.map *.c ..\COMMON~1\*.c 21 | 22 | dist: mt32-pi.exe .symbolic 23 | UPXCOMP.BAT 24 | copy /Y MT32-PI.EXE ..\dos_bin\ 25 | 26 | clean: .symbolic 27 | del *.obj 28 | del *.map 29 | del mt32-pi.exe 30 | 31 | -------------------------------------------------------------------------------- /dos_src/MPU401.C: -------------------------------------------------------------------------------- 1 | /* 2 | * Library to access MPU-401 hardware 3 | * 4 | * Copyright (C) 2014-2018 Mateusz Viste 5 | * All rights reserved. 6 | * 7 | * With modifications removing custom timer dependency 8 | * and adding mpu401_send_bytes(), mpu401_rst_nowait() 9 | * Copyright (C) 2021 Andreas Zdziarstek 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * 1. Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include /* inp() and outp() */ 35 | #include 36 | #include 37 | 38 | #include "mpu401.h" /* include self for control */ 39 | 40 | 41 | #define MPU_DATA mpuport 42 | #define MPU_STAT mpuport+1 43 | 44 | 45 | /* flush everything from the MPU port (if anything) */ 46 | void mpu401_flush(int mpuport) { 47 | while (mpu401_poll(mpuport) != 0) inp(MPU_DATA); 48 | } 49 | 50 | 51 | /* wait until it's okay for us to write to the MPU */ 52 | void mpu401_waitwrite(int mpuport) { 53 | int buff; 54 | for (;;) { 55 | mpu401_flush(mpuport); /* make sure to flush first, in case the MPU */ 56 | buff = inp(MPU_STAT); /* tries to talk to us */ 57 | if ((buff & 0x40) == 0) break; 58 | } 59 | } 60 | 61 | 62 | /* wait until it's okay for us to write to the MPU - but no longer than 63 | * timeout us. returns 0 on success, non-zero otherwise. */ 64 | static int mpu401_waitwrite_timeout(int mpuport, long timeout) { 65 | int buff; 66 | clock_t curtime, notafter; 67 | notafter = clock(); 68 | notafter += timeout; 69 | for (;;) { 70 | buff = inp(MPU_STAT); 71 | if ((buff & 0x40) == 0) return(0); 72 | curtime = clock(); 73 | if (curtime >= notafter) return(-1); 74 | } 75 | } 76 | 77 | 78 | /* polls the midi interface - returns 0 if nothing is available to be read, non-zero otherwise. 79 | note that this should be checked as often as possible - whenever UART have some bytes for you, you MUST read them out */ 80 | int mpu401_poll(int mpuport) { 81 | int buff; 82 | buff = inp(MPU_STAT); 83 | if ((buff & 0x80) == 0) return(1); 84 | return(0); 85 | } 86 | 87 | 88 | void mpu401_waitread(int mpuport) { 89 | while (mpu401_poll(mpuport) == 0); 90 | } 91 | 92 | 93 | /* resets the MPU-401. returns 0 on success, non-zero otherwise. */ 94 | int mpu401_rst(int mpuport) { 95 | clock_t curtime, timeout; 96 | if (mpu401_waitwrite_timeout(mpuport, CLOCKS_PER_SEC/2) != 0) return(-1); /* wait for the MPU to accept bytes from us */ 97 | outp(MPU_STAT, 0xFF); /* Send MPU-401 RESET Command */ 98 | /* note that some cards do not ACK on 0xFF ! that's why I should wait for a timeout here, and skip waiting if no answer after 1 or 2s */ 99 | timeout = clock(); 100 | timeout += CLOCKS_PER_SEC/2; /* timeout is 0.5s */ 101 | for (;;) { 102 | /* wait for the MPU to hand a byte to us (we are waiting for an ACK) */ 103 | if (mpu401_poll(mpuport) != 0) { 104 | if (inp(MPU_DATA) == 0xFE) break; /* if we got the ACK, continue */ 105 | } 106 | curtime = clock(); 107 | if (curtime >= timeout) break; 108 | } 109 | mpu401_flush(mpuport); 110 | return(0); 111 | } 112 | 113 | /* reset the MPU-401 without waiting for an ACK */ 114 | int mpu401_rst_nowait(int mpuport) { 115 | if (mpu401_waitwrite_timeout(mpuport, CLOCKS_PER_SEC/2) != 0) return(-1); /* wait for the MPU to accept bytes from us */ 116 | outp(MPU_STAT, 0xFF); /* Send MPU-401 RESET Command */ 117 | return(0); 118 | } 119 | 120 | void mpu401_uart(int mpuport) { 121 | mpu401_waitwrite(mpuport); /* Wait for port ready */ 122 | outp(MPU_STAT, 0x3F); /* Set MPU-401 "Dumb UART" Mode */ 123 | /* I don't read anything back here, because MPU is not supposed to acknowledge the UART command */ 124 | } 125 | 126 | void mpu401_send_bytes(int mpuport, const unsigned char *buf, int len) { 127 | clock_t start; 128 | int x; 129 | for (x = 0; x < len; x++) { 130 | mpu401_waitwrite(mpuport); /* Wait for port ready */ 131 | outp(mpuport, buf[x]); /* Send sysex data byte */ 132 | } 133 | start = clock(); 134 | //Wait a bit to give the MPU401 time to shift out our message 135 | //Otherwise, a MPU401 reset command may interrupt the midi msg 136 | //25ms should be fine even if the hardware has a large buffer 137 | while(clock()-start < 25); 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /dos_src/MPU401.H: -------------------------------------------------------------------------------- 1 | /* 2 | * Library to access MPU-401 hardware 3 | * 4 | * Copyright (C) 2014-2018 Mateusz Viste 5 | * All rights reserved. 6 | * 7 | * With modifications removing custom timer dependency 8 | * and adding mpu401_send_bytes(), mpu401_rst_nowait() 9 | * Copyright (C) 2021 Andreas Zdziarstek 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions are met: 13 | * 14 | * 1. Redistributions of source code must retain the above copyright notice, 15 | * this list of conditions and the following disclaimer. 16 | * 17 | * 2. Redistributions in binary form must reproduce the above copyright 18 | * notice, this list of conditions and the following disclaimer in the 19 | * documentation and/or other materials provided with the distribution. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 25 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 26 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 27 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 28 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 29 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 | * POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef mpu401_h_sentinel 35 | #define mpu401_h_sentinel 36 | 37 | /* wait until it's okay for us to write to the MPU */ 38 | void mpu401_waitwrite(int mpuport); 39 | 40 | /* polls the midi interface - returns 0 if nothing is available to be read, non-zero otherwise. note that this should be checked as often as possible - whenever UART have some bytes for you, you MUST read them out */ 41 | int mpu401_poll(int mpuport); 42 | 43 | void mpu401_waitread(int mpuport); 44 | 45 | /* flush everything from the MPU port (if anything) */ 46 | void mpu401_flush(int mpuport); 47 | 48 | /* resets the MPU-401. returns 0 on success, non-zero otherwise. */ 49 | int mpu401_rst(int mpuport); 50 | 51 | /* reset the MPU-401 without waiting for an ACK */ 52 | int mpu401_rst_nowait(int mpuport); 53 | 54 | /* switches the MPU-401 into 'dumb UART' mode */ 55 | void mpu401_uart(int mpuport); 56 | 57 | /* Send data out */ 58 | void mpu401_send_bytes(int mpuport, const unsigned char *buf, int len); 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /dos_src/UPXCOMP.BAT: -------------------------------------------------------------------------------- 1 | upx --8086 -9 mt32-pi.exe 2 | -------------------------------------------------------------------------------- /dos_src/delay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs 5 | * and Amiga computers 6 | * 7 | * dos_src/delay.c - non-stdlib delay function 8 | * using the 8254 timer in DOS PCs 9 | * 10 | * Copyright (C) 2021 Andreas Zdziarstek 11 | */ 12 | 13 | #include 14 | #include 15 | 16 | #include "delay.h" 17 | 18 | static void interrupt (*orig_isr)(void); 19 | static volatile unsigned int ticks = 0, doscall_counter = 0; 20 | 21 | /* interrupt handler for Timer 0 */ 22 | static void interrupt tim_0_isr(void) { 23 | ticks++; 24 | doscall_counter++; 25 | 26 | /* Call original DOS handler about every 55ms (approx 18.2Hz) */ 27 | if (doscall_counter >= 55) { 28 | doscall_counter = 0; 29 | _chain_intr(orig_isr); 30 | } else { 31 | /* if not, signal EOI to the PIC */ 32 | outp(0x20, 0x20); 33 | } 34 | } 35 | 36 | /* Hardware timer delay funtion, busy-waits for 37 | * delay milliseconds using the 8254 timer 0 38 | */ 39 | void delay_ms(unsigned int delay) { 40 | /* Save the address of the original 18.2Hz DOS 41 | * Timer ISR 42 | */ 43 | orig_isr = _dos_getvect(0x08); 44 | 45 | //reset ticks and doscall counter 46 | ticks = 0; 47 | doscall_counter = 0; 48 | 49 | /* Disable interrupts */ 50 | _disable(); 51 | 52 | /* Install our new timer ISR */ 53 | _dos_setvect(0x08, tim_0_isr); 54 | 55 | /* Set Timer 0 prescaler and start counting*/ 56 | outp(0x43, 0x36); 57 | outp(0x40, 0xA8); // Prescaler 0x04A8 == 1192 58 | outp(0x40, 0x04); // 1192/1193180Hz is approx 1ms 59 | // We could also do 1193 but 1192 gives a better 60 | // approximation to the 18.2Hz DOS interrupt. 61 | // Don't really know if that's important though. 62 | 63 | /* Enable interrupts */ 64 | _enable(); 65 | 66 | // Busy-wait delay ms 67 | while(ticks < delay); 68 | 69 | // Disable interrupts 70 | _disable(); 71 | // Restore the original timer 0 ISR 72 | _dos_setvect(0x08, orig_isr); 73 | 74 | // Restore Timer 0 configuration 75 | // 0x0000 is a special case that 76 | // gets interpreted as the highest 77 | // possible prescaler of 65536, yielding 78 | // the good old 18.2 Hz interrupt 79 | outp(0x43, 0x36); 80 | outp(0x40, 0x00); 81 | outp(0x40, 0x00); 82 | 83 | // Enable interrupts again and we're done, 84 | // hopefully without breaking anything :) 85 | _enable(); 86 | } 87 | 88 | -------------------------------------------------------------------------------- /dos_src/midi_dev.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "midi_dev.h" 6 | #include "getopt.h" 7 | #include "mpu401.h" 8 | #include "delay.h" 9 | 10 | static int mpubase = 0x330; 11 | 12 | static char *dev_optstr = "p:"; 13 | 14 | int mididev_init(void) { 15 | if(mpu401_rst(mpubase) == -1) { 16 | fprintf(stderr, "ERROR: MPU401 not answering at port 0x%X.\n", mpubase); 17 | return -1; 18 | } 19 | mpu401_uart(mpubase); 20 | 21 | /* 22 | * Wait for 100ms, some intelligent MPU-401s seem to need this. 23 | * (should fix github.com/gmcn42/mt32-pi-control/issues/2) 24 | */ 25 | delay_ms(100); 26 | return 0; 27 | } 28 | 29 | int mididev_deinit(void) { 30 | mpu401_rst_nowait(mpubase); 31 | return 0; 32 | } 33 | 34 | int mididev_send_bytes(const unsigned char *buf, int len) { 35 | mpu401_send_bytes(mpubase, buf, len); 36 | return 0; 37 | } 38 | 39 | void mididev_print_usage(void) { 40 | printf("-p [ADDR]: Set the port address of the MPU401 interface. Default: 330.\n"); 41 | } 42 | 43 | void mididev_add_optstr(char *optstr) { 44 | int main_len = strlen(optstr); 45 | strcpy(optstr+main_len, dev_optstr); 46 | } 47 | 48 | int mididev_parse_arg(int c, const char *optarg) { 49 | if(c=='p') { 50 | mpubase = (int)strtol(optarg, NULL, 16); 51 | if(!mpubase) 52 | return -1; 53 | return 0; 54 | } 55 | return -1; 56 | } 57 | 58 | -------------------------------------------------------------------------------- /dos_src/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/dos_src/mt32pi.bmp -------------------------------------------------------------------------------- /images/mt32pictl_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/images/mt32pictl_1.jpg -------------------------------------------------------------------------------- /images/mt32pictl_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/images/mt32pictl_2.jpg -------------------------------------------------------------------------------- /linux_src/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME := mt32-pi-ctl 2 | 3 | CXX = g++ 4 | CC = gcc 5 | AR = ar 6 | AS = as 7 | 8 | VPATH = .:../common_src 9 | INCLUDE = -I../common_src 10 | DEFINE = -DPROGRAM_NAME='"$(PROGRAM_NAME)"' -DUSE_CUSTOM_DELAY 11 | 12 | #CFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -mcrt=nix13 -msmall-code -fbaserel -mregparm -Wall -Werror -Wno-pointer-sign -std=gnu99 13 | 14 | CFLAGS = $(INCLUDE) -O2 -ffunction-sections -fdata-sections -Wl,--gc-sections -Wall -Werror -Wno-pointer-sign -std=gnu99 $(DEFINE) 15 | 16 | CXXFLAGS = $(INCLUDE) -O2 -ffunction-sections -fdata-sections -Wl,--gc-sections -Wall -Werror $(DEFINE) 17 | 18 | LIBS = -lasound 19 | 20 | OBJ = mt32-pi.o midi_dev.o delay.o 21 | 22 | %.o : %.c 23 | $(CC) -c -o $@ $< $(CFLAGS) 24 | 25 | %.o : %.C 26 | $(CC) -c -o $@ $< $(CFLAGS) 27 | 28 | 29 | $(PROGRAM_NAME) : $(OBJ) 30 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 31 | 32 | .PHONY: clean 33 | clean : 34 | rm -f $(OBJ) $(PROGRAM_NAME) 35 | 36 | .PHONY: dist 37 | dist: $(PROGRAM_NAME) 38 | cp -v $(PROGRAM_NAME) ../linux_bin/ 39 | cp -v *.bmp ../linux_bin/ 40 | 41 | -------------------------------------------------------------------------------- /linux_src/completion_scripts/mt32-pi-ctl.bash: -------------------------------------------------------------------------------- 1 | # mt32-pi-ctl bash completion script 2 | _mt32-pi-ctl() { 3 | local cur prev opts 4 | compopt +o default 5 | COMPREPLY=() 6 | cur="${COMP_WORDS[COMP_CWORD]}" 7 | prev="${COMP_WORDS[COMP_CWORD-1]}" 8 | opts="--help 9 | --verbose 10 | --reboot 11 | --mt32 12 | --fluidsynth 13 | --romset 14 | --soundfont 15 | --mt32-rstereo 16 | --mt32-reset 17 | --gm-reset 18 | --gs-reset 19 | --mt32-txt 20 | --sc55-txt 21 | --sc55-bmp 22 | --sc55-btxt 23 | --negative 24 | --midi 25 | --syx" 26 | 27 | if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]]; then 28 | COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) 29 | return 0 30 | fi 31 | 32 | case "${prev}" in 33 | -b|--romset) 34 | local romsets="old new cm32l" 35 | COMPREPLY=( $(compgen -W "${romsets}" -- ${cur}) ) 36 | ;; 37 | -Y|--syx|-P|--sc55-bmp) 38 | compopt -o default 39 | ;; 40 | -p) 41 | COMPREPLY=() 42 | if command -v aplaymidi > /dev/null 2>&1; then 43 | local midiports=$(aplaymidi -l | awk 'NR>1 { print $1 }' ORS=' ') 44 | COMPREPLY=( $(compgen -W "${midiports}" -- ${cur}) ) 45 | fi 46 | esac 47 | } 48 | complete -F _mt32-pi-ctl mt32-pi-ctl 49 | -------------------------------------------------------------------------------- /linux_src/completion_scripts/mt32-pi-ctl.fish: -------------------------------------------------------------------------------- 1 | # mt32-pi-ctl fish completion script 2 | 3 | function __fish_list_midi_ports --description 'List available MIDI ports' 4 | if type -q aplaymidi 5 | aplaymidi -l | awk 'NR>1 {gsub(/^[[:space:]]+/, "", $1); print $1, $3}' FS='[[:space:]]{2,}' OFS='\t' 6 | end 7 | end 8 | 9 | complete -c mt32-pi-ctl -f 10 | complete -c mt32-pi-ctl -s h -l help -d "Display help." 11 | complete -c mt32-pi-ctl -s v -l verbose -d "Be verbose about what is going on." 12 | complete -c mt32-pi-ctl -s r -l reboot -d "Reboot the Pi. Will block for a few secs to give it time." 13 | complete -c mt32-pi-ctl -s p -x -a "(__fish_list_midi_ports)" -d "The ALSA MIDI client and port address to output to." 14 | complete -c mt32-pi-ctl -s m -l mt32 -d "Switch mt32-pi to MT-32 mode." 15 | complete -c mt32-pi-ctl -s g -l fluidsynth -d "Switch mt32-pi to FluidSynth mode." 16 | complete -c mt32-pi-ctl -s b -l romset -x -a "(echo -e \"old\tOld MT-32 (ROM v1.xx)\nnew\tNew MT-32 (ROM v2.xx)\ncm32l\tCM-32L\n\")" -d "Switch MT-32 romset." 17 | complete -c mt32-pi-ctl -s s -l soundfont -x -d "Set FluidSynth SoundFont. (number)" 18 | complete -c mt32-pi-ctl -s S -l mt32-rstereo -x -d "Enable/disable MT-32 reversed stereo. (0, 1)" 19 | complete -c mt32-pi-ctl -l mt32-reset -d "Send an MT-32 reset SysEx message." 20 | complete -c mt32-pi-ctl -l gm-reset -d "Send a GM reset SysEx message." 21 | complete -c mt32-pi-ctl -l gs-reset -d "Send a GS reset SysEx message." 22 | complete -c mt32-pi-ctl -s t -l mt32-txt -x -d "Send an MT-32 text display SysEx." 23 | complete -c mt32-pi-ctl -s T -l sc55-txt -x -d "Send an SC-55 text display SysEx." 24 | complete -c mt32-pi-ctl -s P -l sc55-bmp -r -d "Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx)" 25 | complete -c mt32-pi-ctl -s X -l sc55-btxt -x -d "Display a string on the screen as a Bitmap. (SC-55)" 26 | complete -c mt32-pi-ctl -s N -l negative -n "__fish_contains_opt -s P sc55-bmp -s X sc55-btxt" -d "Reverse color. Use with '-P/--sc55-bmp' or '-X/--sc55-btxt'." 27 | complete -c mt32-pi-ctl -s M -l midi -x -d "Send a list of custom MIDI bytes." 28 | complete -c mt32-pi-ctl -s Y -l syx -r -d "Send the contents of a SYX-file." 29 | -------------------------------------------------------------------------------- /linux_src/completion_scripts/mt32-pi-ctl.zsh: -------------------------------------------------------------------------------- 1 | #compdef mt32-pi-ctl 2 | _mt32-pi-ctl() { 3 | typeset -A opt_args 4 | local context state line 5 | 6 | _arguments -s -S \ 7 | "--help[Display help.]" \ 8 | "-v[Be verbose about what is going on.]" \ 9 | "--verbose[Be verbose about what is going on.]" \ 10 | "-b+[Switch MT-32 romset.]:romset:->romsets" \ 11 | "--romset[Switch MT-32 romset.]:romset:->romsets" \ 12 | "-p+[The ALSA MIDI client and port address to output to.]:midi-output:->midiports" \ 13 | "-r[Reboot the Pi. Will block for a few secs to give it time.]" \ 14 | "--reboot[Reboot the Pi. Will block for a few secs to give it time.]" \ 15 | "-m[Switch mt32-pi to MT-32 mode.]" \ 16 | "--mt32[Switch mt32-pi to MT-32 mode.]" \ 17 | "-g[Switch mt32-pi to FluidSynth mode.]" \ 18 | "--fluidsynth[Switch mt32-pi to FluidSynth mode.]" \ 19 | "-s+[Set FluidSynth SoundFont. (number)]:SoundFont number (0-255):" \ 20 | "--soundfont[Set FluidSynth SoundFont. (number)]:SoundFont number (0-255):" \ 21 | "-S+[Enable/disable MT-32 reversed stereo. (0, 1)]:1 or 0:->rstereo" \ 22 | "--mt32-rstereo[Enable/disable MT-32 reversed stereo. (0, 1)]:1 or 0:->rstereo" \ 23 | "--mt32-reset[Send an MT-32 reset SysEx message]" \ 24 | "--gm-reset[Send a GM reset SysEx message]" \ 25 | "--gs-reset[Send a GS reset SysEx message]" \ 26 | "--mt32-txt[Send an MT-32 text display SysEx]:some text:" \ 27 | "-t+[Send an MT-32 text display SysEx]:some text:" \ 28 | "--sc55-txt[Send an SC-55 text display SysEx]:some text:" \ 29 | "-T+[Send an SC-55 text display SysEx]:some text:" \ 30 | "--sc55-btxt[Display a string on the screen as a Bitmap. (SC-55)]:8 characters:" \ 31 | "-X+[Display a string on the screen as a Bitmap. (SC-55)]:8 characters:" \ 32 | "--sc55-bmp[Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx)]:file:_files" \ 33 | "-P+[Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx)]:file:_files" \ 34 | "-N[Reverse color. Use with '-P/--sc55-bmp' or '-X/--sc55-btxt'.]" \ 35 | "--negative[Reverse color. Use with '-P/--sc55-bmp' or '-X/--sc55-btxt'.]" \ 36 | "-M[Send a list of custom MIDI bytes (in hexadecimal).]:Hexadecimal Bytes:" \ 37 | "--midi[Send a list of custom MIDI bytes (in hexadecimal).]:Hexadecimal Bytes:" \ 38 | "-Y[Send the contents of a SYX-file.]:file:_files" \ 39 | "--syx[Send the contents of a SYX-file.]:file:_files" \ 40 | && return 0 41 | 42 | case $state in 43 | (midiports) 44 | if type aplaymidi > /dev/null 2>&1; then 45 | local midiports=$(aplaymidi -l | awk -F '[[:space:]][[:space:]]+' 'NR>1 { gsub(/^[ \t]+/,""); gsub(/:/,"\\:"); print $1":"$3 }') 46 | midiports=(${(ps:\n:)${midiports}}) 47 | _describe 'Available MIDI outputs' midiports && return 0 48 | else 49 | return 0 50 | fi 51 | ;; 52 | (romsets) 53 | local romlist=('old:Old MT-32 (ROM v1.xx)' 'new:New MT-32 (ROM v2.xx)' 'cm32l:CM-32L') 54 | _describe 'romset' romlist && return 0 55 | ;; 56 | (rstereo) 57 | local desclist=('0:Disable reversed stereo' '1:Enable reversed stereo') 58 | _describe 'reversed stereo' desclist && return 0 59 | ;; 60 | esac 61 | 62 | return 1 63 | } 64 | 65 | _mt32-pi-ctl 66 | -------------------------------------------------------------------------------- /linux_src/delay.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #include "delay.h" 15 | 16 | void delay_ms(unsigned int delay) { 17 | struct timespec ts, tr; 18 | ts.tv_sec = (time_t)(delay/1000); 19 | ts.tv_nsec = (long) ((delay%1000)*1000000); 20 | while((clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, &tr))==EINTR) { 21 | ts = tr; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /linux_src/midi_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "midi_dev.h" 17 | 18 | typedef enum { 19 | MIDI_OP_NOTEOFF = 0x80, 20 | MIDI_OP_NOTEON = 0x90, 21 | MIDI_OP_KEYPRESS = 0xA0, 22 | MIDI_OP_CTRL = 0xB0, 23 | MIDI_OP_PC = 0xC0, 24 | MIDI_OP_CC = 0xD0, 25 | MIDI_OP_PB = 0xE0, 26 | MIDI_OP_SYX = 0xF0 27 | } midi_op_t; 28 | 29 | static snd_seq_t *seq = NULL; 30 | static int src_port = -1, dst_port = -1, dst_client = -1; 31 | static char *alsa_client = NULL; 32 | 33 | static char *dev_optstr = "p:"; 34 | 35 | int mididev_init(void) { 36 | if(snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0) < 0) { 37 | fprintf(stderr, "ERROR: Could not open ALSA sequencer.\n"); 38 | return -1; 39 | } 40 | 41 | if(alsa_client != NULL && *alsa_client != 0) { 42 | snd_seq_addr_t addr; 43 | if(snd_seq_parse_address(seq, &addr, alsa_client) >= 0) { 44 | dst_client = addr.client; 45 | dst_port = addr.port; 46 | } 47 | } 48 | 49 | if(dst_port == -1 || dst_client == -1) { 50 | fprintf(stderr, "ERROR: You must specify ALSA client and port using -p [CLIENT]:[PORT]\n"); 51 | snd_seq_close(seq); 52 | return -1; 53 | } 54 | 55 | if( (src_port = snd_seq_create_simple_port( 56 | seq, 57 | "mt32-pi-ctl", 58 | SND_SEQ_PORT_CAP_READ, 59 | SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { 60 | fprintf(stderr, "ERROR: Could not create MIDI output.\n"); 61 | snd_seq_close(seq); 62 | return -1; 63 | } 64 | 65 | if(snd_seq_connect_to(seq, src_port, dst_client, dst_port) < 0) { 66 | fprintf(stderr, "ERROR: Could not connect to MIDI port %d:%d.\n", dst_client, dst_port); 67 | mididev_deinit(); 68 | return -1; 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | int mididev_deinit(void) { 75 | snd_seq_delete_simple_port(seq, src_port); 76 | snd_seq_close(seq); 77 | return 0; 78 | } 79 | 80 | int mididev_send_bytes(const unsigned char *buf, int len) { 81 | if(len==0) return 0; 82 | 83 | snd_seq_event_t ev; 84 | snd_seq_ev_clear(&ev); 85 | snd_seq_ev_set_direct(&ev); 86 | snd_seq_ev_set_source(&ev, src_port); 87 | snd_seq_ev_set_dest(&ev, dst_client, dst_port); 88 | 89 | midi_op_t o = (midi_op_t)(buf[0] & 0xF0); 90 | 91 | unsigned char *tempbuf = NULL; 92 | 93 | switch(o) { 94 | case MIDI_OP_NOTEOFF: 95 | if(len != 3) goto handle_error; 96 | snd_seq_ev_set_noteoff(&ev, buf[0]&0x0F, buf[1], buf[2]); 97 | break; 98 | case MIDI_OP_NOTEON: 99 | if(len != 3) goto handle_error; 100 | snd_seq_ev_set_noteon(&ev, buf[0]&0x0F, buf[1], buf[2]); 101 | break; 102 | case MIDI_OP_KEYPRESS: 103 | if(len != 3) goto handle_error; 104 | snd_seq_ev_set_keypress(&ev, buf[0]&0x0F, buf[1], buf[2]); 105 | break; 106 | case MIDI_OP_CTRL: 107 | if(len != 3) goto handle_error; 108 | snd_seq_ev_set_controller(&ev, buf[0]&0x0F, buf[1], buf[2]); 109 | break; 110 | case MIDI_OP_PC: 111 | if(len != 2) goto handle_error; 112 | snd_seq_ev_set_pgmchange(&ev, buf[0]&0x0F, buf[1]); 113 | break; 114 | case MIDI_OP_CC: 115 | if(len != 2) goto handle_error; 116 | snd_seq_ev_set_chanpress(&ev, buf[0]&0x0F, buf[1]); 117 | break; 118 | case MIDI_OP_PB: 119 | if(len != 3) goto handle_error; 120 | unsigned char par = (buf[1] & 0x7F) + ((buf[2] & 0x7F)<<7); 121 | snd_seq_ev_set_pitchbend(&ev, buf[0]&0x0F, par); 122 | break; 123 | case MIDI_OP_SYX: 124 | if(buf[len-1] != 0xF7) goto handle_error; 125 | { 126 | tempbuf = malloc(len); 127 | if(tempbuf == NULL) { 128 | fprintf(stderr, "ERROR: Memory allocation error.\n"); 129 | return -1; 130 | } 131 | memcpy(tempbuf, buf, len); 132 | snd_seq_ev_set_sysex(&ev, len, tempbuf); 133 | } 134 | break; 135 | default: 136 | handle_error: 137 | fprintf(stderr, "ERROR: Unknown MIDI message.\n"); 138 | return -1; 139 | break; 140 | } 141 | 142 | snd_seq_event_output(seq, &ev); 143 | snd_seq_drain_output(seq); 144 | 145 | if(tempbuf != NULL) free(tempbuf); 146 | return 0; 147 | } 148 | 149 | void mididev_print_usage(void) { 150 | printf("-p \"[CLIENT]:[PORT]\" : The ALSA MIDI client and port address to output to (*MANDATORY*).\n"); 151 | } 152 | 153 | void mididev_add_optstr(char *optstr) { 154 | int main_len = strlen(optstr); 155 | strcpy(optstr+main_len, dev_optstr); 156 | } 157 | 158 | int mididev_parse_arg(int c, const char *optarg) { 159 | if(c != 'p') { 160 | fprintf(stderr, "ERROR: Unknown option \'%c\'.\n", c); 161 | return -1; 162 | } 163 | if(alsa_client != NULL) { 164 | free(alsa_client); 165 | alsa_client = NULL; 166 | } 167 | size_t len = strlen(optarg); 168 | alsa_client = (char*) malloc(len+1); 169 | if(alsa_client == NULL) { 170 | fprintf(stderr, "Error allocating memory.\n"); 171 | return -1; 172 | } 173 | memcpy(alsa_client, optarg, len+1); 174 | return 0; 175 | } 176 | 177 | -------------------------------------------------------------------------------- /linux_src/mt32-pi-ctl.readme: -------------------------------------------------------------------------------- 1 | #### mt32-pi-ctl 1.0.1a Linux ReadMe #### 2 | 3 | mt32-pi-ctl is a control program for the mt32-pi MIDI synthesizer. 4 | 5 | Program source, build instructions and ports to different modern and 6 | retro platforms available at https://github.com/gmcn42/mt32-pi-control 7 | 8 | The mt32-pi synthesizer lives at https://github.com/dwhinham/mt32-pi 9 | 10 | 11 | #### Usage Summary #### 12 | 13 | mt32-pi-ctl accepts the following parameters: 14 | 15 | USAGE: mt32-pi-ctl [OPTIONS] 16 | OPTIONS: 17 | -h/--help: Print this info. 18 | -v/--verbose: Be verbose about what is going on. 19 | -r/--reboot: Reboot the Pi. Will block for a few secs to give it time. 20 | -p "[CLIENT]:[PORT]" : The ALSA MIDI client and port address to output to (*MANDATORY*). 21 | -m/--mt32: Switch mt32-pi to MT-32 mode. 22 | -g/--fluidsynth: Switch mt32-pi to FluidSynth mode. 23 | -b/--romset [old, new, cm32l]: Switch MT-32 romset. 24 | -s/--soundfont [NUMBER]: Set FluidSynth SoundFont. 25 | -S/--mt32-rstereo [0, 1]: Enable/disable MT-32 reversed stereo. 26 | --mt32-reset: Send an MT-32 reset SysEx message. 27 | --gm-reset: Send a GM reset SysEx message. 28 | --gs-reset: Send a GS reset SysEx message. 29 | -t/--mt32-txt "Some text": Send an MT-32 text display SysEx. 30 | -T/--sc55-txt "Some text": Send an SC-55 text display SysEx. 31 | -P/--sc55-bmp FILE.BMP: Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx) 32 | -X/--sc55-btxt "SomeText": Display a string on the screen as a Bitmap. (SC-55) 33 | -N/--negative: Reverse image color. Use with '-P/--sc55-bmp'. 34 | -M/--midi "C0 01 C0 DE": Send a list of custom MIDI bytes. 35 | -Y/--syx file.syx : Send the contents of a SYX-file. 36 | 37 | You may specify multiple options, i.e. mt32-pi-ctl -m -t "Hello, World!" will 38 | first send the MT-32 mode command and then the screen text. 39 | 40 | ### smart option completion #### 41 | 42 | Since version 1.0.1a, the Linux version of mt32-pi-ctl also comes with command completion 43 | scripts for bash, fish, and zsh that will smartly autocomplete (long) options, romsets, filenames 44 | and, if aplaymidi is in your PATH, even available MIDI ports. 45 | 46 | Examples for bash: 47 | 48 | $ mt32-pi-ctl -p 49 | 14:0 28:0 50 | 51 | $ mt32-pi-ctl -p 28:0 --mt 52 | --mt32 --mt32-reset --mt32-rstereo --mt32-txt 53 | 54 | $ mt32-pi-ctl -p 28:0 --romset 55 | cm32l new old 56 | 57 | For this to work mt32-pi-ctl must be in your PATH, e.g. in /usr/local/bin and you need to copy the 58 | completion scripts in linux_src/completion_scripts/ to some special folders so your shell can find 59 | them automatically. The folders may be distro-specific but the ones below should work for 60 | Debian/Ubuntu at the very least. If in doubt, consult your distro's documentation. 61 | 62 | Note that you need aplaymidi installed if you want autocompletion to show you available MIDI output 63 | ports for the -p option. On most distros---including Debian, Ubuntu, and Arch---the package you need 64 | is called alsa-utils. 65 | 66 | ## bash completion script ## 67 | 68 | On modern Debian/Ubuntu systems, install the script using 69 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.bash /usr/share/bash-completion/completions/mt32-pi-ctl 70 | 71 | On other or older distros, the bash completion script may need to go to /etc/bash_completion.d/mt32-pi-ctl 72 | instead. 73 | 74 | Alternatively you can add the line 75 | source /path/to/mt32-pi-ctl.bash 76 | to your .bashrc. 77 | 78 | After installing, you may need to relogin or reboot. 79 | 80 | ## fish completion script ### 81 | 82 | Install the script using 83 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.fish /usr/share/fish/vendor_completions.d/mt32-pi-ctl.fish 84 | 85 | The fish completion script may be located in your home directory under ~/.config/fish/completions 86 | instead, if you prefer. 87 | Once copied, completions should be available in fish immediately. 88 | 89 | ## zsh completion script ## 90 | 91 | Install the script using 92 | sudo cp linux_src/completion_scripts/mt32-pi-ctl.zsh /usr/local/share/zsh/site-functions/_mt32-pi-ctl 93 | 94 | Alternatively you may use any of the directories reported by echo $fpath but note that the script has to 95 | be renamed to _mt32-pi-ctl or zsh won't associate it with the program. 96 | After installing, you may need to relogin or reboot. 97 | 98 | #### BSD-License #### 99 | 100 | Copyright (C) 2021 Andreas Zdziarstek 101 | with parts 102 | Copyright (C) 2015-2016 Josuah Demangeon (miniwi font), 103 | Copyright (C) 2002 Todd C. Miller (getopt). 104 | and Copyright (C) 2000 The NetBSD Foundation, Inc. (getopt) 105 | 106 | See the individual source files for details. 107 | 108 | Redistribution and use in source and binary forms, with or without 109 | modification, are permitted provided that the following conditions are met: 110 | 111 | 1. Redistributions of source code must retain the above copyright notice, 112 | this list of conditions and the following disclaimer. 113 | 114 | 2. Redistributions in binary form must reproduce the above copyright 115 | notice, this list of conditions and the following disclaimer in the 116 | documentation and/or other materials provided with the distribution. 117 | 118 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 119 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 120 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 121 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 122 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 123 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 124 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 125 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 126 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 127 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 128 | POSSIBILITY OF SUCH DAMAGE. 129 | -------------------------------------------------------------------------------- /linux_src/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/linux_src/mt32pi.bmp -------------------------------------------------------------------------------- /win32_bin/mt32-pi-ctl.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/win32_bin/mt32-pi-ctl.exe -------------------------------------------------------------------------------- /win32_bin/mt32-pi-ctl.txt: -------------------------------------------------------------------------------- 1 | #### mt32-pi-ctl.exe 1.0.1 Windows ReadMe #### 2 | 3 | MT32-PI.EXE is a Win32 CLI control program for the mt32-pi MIDI synthesizer. 4 | 5 | Program source and ports to different modern and retro platforms 6 | available at https://github.com/gmcn42/mt32-pi-control 7 | 8 | The mt32-pi synthesizer lives at https://github.com/dwhinham/mt32-pi 9 | 10 | 11 | #### Usage Summary #### 12 | 13 | mt32-pi-ctl.exe accepts the following UNIX-style parameters: 14 | 15 | USAGE: MT32-PI.EXE [OPTIONS] 16 | OPTIONS: 17 | -h/--help: Print this info. 18 | -v/--verbose: Be verbose about what is going on. 19 | -r/--reboot: Reboot the Pi. Will block for a few secs to give it time. 20 | -p PORT : Set MIDI output port number (*MANDATORY*). 21 | -l : List available MIDI output ports and exit. 22 | -m/--mt32: Switch mt32-pi to MT-32 mode. 23 | -g/--fluidsynth: Switch mt32-pi to FluidSynth mode. 24 | -b/--romset [old, new, cm32l]: Switch MT-32 romset. 25 | -s/--soundfont [NUMBER]: Set FluidSynth SoundFont. 26 | -S/--mt32-rstereo [0, 1]: Enable/disable MT-32 reversed stereo. 27 | --mt32-reset: Send an MT-32 reset SysEx message. 28 | --gm-reset: Send a GM reset SysEx message. 29 | --gs-reset: Send a GS reset SysEx message. 30 | -t/--mt32-txt "Some text": Send an MT-32 text display SysEx. 31 | -T/--sc55-txt "Some text": Send an SC-55 text display SysEx. 32 | -P/--sc55-bmp FILE.BMP: Display a 16x16 1bpp BMP on the screen. (SC-55 SysEx) 33 | -X/--sc55-btxt "SomeText": Display a string on the screen as a Bitmap. (SC-55) 34 | -N/--negative: Reverse image color. Use with '-P/--sc55-bmp'. 35 | -M/--midi "C0 01 C0 DE": Send a list of custom MIDI bytes. 36 | -Y/--syx file.syx : Send the contents of a SYX-file 37 | 38 | You may specify multiple options, i.e. mt32-pi-ctl.exe -p1 -m -t "Hello, World!" will 39 | first send the MT-32 mode command and then the screen text out to MIDI port 1. 40 | 41 | 42 | #### BSD-License #### 43 | 44 | Copyright (C) 2021 Andreas Zdziarstek 45 | with parts 46 | Copyright (C) 2015-2016 Josuah Demangeon (miniwi font), 47 | Copyright (c) 2002 Todd C. Miller (getopt). 48 | and Copyright (c) 2000 The NetBSD Foundation, Inc. (getopt) 49 | 50 | See the individual source files for details. 51 | 52 | Redistribution and use in source and binary forms, with or without 53 | modification, are permitted provided that the following conditions are met: 54 | 55 | 1. Redistributions of source code must retain the above copyright notice, 56 | this list of conditions and the following disclaimer. 57 | 58 | 2. Redistributions in binary form must reproduce the above copyright 59 | notice, this list of conditions and the following disclaimer in the 60 | documentation and/or other materials provided with the distribution. 61 | 62 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 63 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 64 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 65 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 66 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 67 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 68 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 69 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 70 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 71 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 72 | POSSIBILITY OF SUCH DAMAGE. 73 | 74 | -------------------------------------------------------------------------------- /win32_bin/mt32pi.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gmcn42/mt32-pi-control/7d31ac3bb86794c72d7a78bb53c2ee02d0e7b2f0/win32_bin/mt32pi.bmp -------------------------------------------------------------------------------- /win32_src/Makefile: -------------------------------------------------------------------------------- 1 | PROGRAM_NAME := mt32-pi-ctl.exe 2 | 3 | CXX = i686-w64-mingw32-g++ 4 | CC = i686-w64-mingw32-gcc 5 | AR = i686-w64-mingw32-ar 6 | AS = i686-w64-mingw32-as 7 | 8 | VPATH = .:../common_src 9 | INCLUDE = -I../common_src 10 | DEFINE = -DPROGRAM_NAME='"$(PROGRAM_NAME)"' -DUSE_CUSTOM_DELAY 11 | 12 | #CFLAGS = $(INCLUDE) -Os -s -m68000 -fomit-frame-pointer -mcrt=nix13 -msmall-code -fbaserel -mregparm -Wall -Werror -Wno-pointer-sign -std=gnu99 13 | 14 | CFLAGS = $(INCLUDE) -O2 -ffunction-sections -fdata-sections -static-libgcc -static-libstdc++ -Wl,--gc-sections -Wall -Werror -Wno-pointer-sign -std=gnu99 $(DEFINE) 15 | 16 | CXXFLAGS = $(INCLUDE) -O2 -ffunction-sections -fdata-sections -static-libgcc -static-libstdc++ -Wl,--gc-sections -Wall -Werror $(DEFINE) 17 | 18 | LIBS = -lwinmm 19 | 20 | OBJ = mt32-pi.o midi_dev.o delay.o 21 | 22 | %.o : %.c 23 | $(CC) -c -o $@ $< $(CFLAGS) 24 | 25 | %.o : %.C 26 | $(CC) -c -o $@ $< $(CFLAGS) 27 | 28 | 29 | $(PROGRAM_NAME) : $(OBJ) 30 | $(CC) -o $@ $^ $(CFLAGS) $(LIBS) 31 | 32 | .PHONY: clean 33 | clean : 34 | rm -f $(OBJ) $(PROGRAM_NAME) 35 | 36 | .PHONY: dist 37 | dist: $(PROGRAM_NAME) 38 | cp -v $(PROGRAM_NAME) ../win32_bin/ 39 | cp -v *.bmp ../win32_bin/ 40 | 41 | -------------------------------------------------------------------------------- /win32_src/delay.c: -------------------------------------------------------------------------------- 1 | #include "windows.h" 2 | #include "delay.h" 3 | 4 | void delay_ms(unsigned int delay) { 5 | Sleep(delay); 6 | } 7 | 8 | -------------------------------------------------------------------------------- /win32_src/midi_dev.c: -------------------------------------------------------------------------------- 1 | /* 2 | * mt32-pi-control 3 | * 4 | * An mt32-pi control program for DOS PCs, Atari ST, 5 | * and Amiga computers 6 | * 7 | * Copyright (C) 2021 Andreas Zdziarstek 8 | * 9 | */ 10 | #include 11 | #include 12 | #include 13 | 14 | #include "windows.h" 15 | 16 | #include "midi_dev.h" 17 | 18 | int output_port = -1; 19 | HMIDIOUT midi_handle = NULL; 20 | static char *dev_optstr = "lp:"; 21 | 22 | static void list_midi_outputs(void); 23 | 24 | int mididev_init(void) { 25 | if(output_port == -1) { 26 | fprintf(stderr, "ERROR: You must specify the MIDI output port number with \"-p PORT\". You can list available ports with \"-l\".\n"); 27 | return -1; 28 | } 29 | 30 | if(midiOutOpen(&midi_handle, output_port, 0, 0, CALLBACK_NULL) != MMSYSERR_NOERROR) { 31 | fprintf(stderr, "ERROR: Could not open MIDI output port %d.\n", output_port); 32 | return -1; 33 | } 34 | return 0; 35 | } 36 | 37 | int mididev_deinit(void) { 38 | midiOutClose(midi_handle); 39 | return 0; 40 | } 41 | 42 | int mididev_send_bytes(const unsigned char *buf, int len) { 43 | if(len==0) return 0; 44 | 45 | 46 | if(buf[0] == 0xF0 && buf[len-1] == 0xF7) { 47 | MIDIHDR mheader; 48 | char *tempbuf = (char *) malloc(len); 49 | if(tempbuf == NULL) { 50 | fprintf(stderr, "Error allocating memory for MIDI output buffer.\n"); 51 | return -1; 52 | } 53 | memcpy(tempbuf, buf, len); 54 | mheader.lpData = tempbuf; 55 | mheader.dwBufferLength = len; 56 | mheader.dwFlags = 0; 57 | mheader.dwBytesRecorded = len; 58 | if(midiOutPrepareHeader(midi_handle, &mheader, sizeof(mheader)) != MMSYSERR_NOERROR) { 59 | free(tempbuf); 60 | fprintf(stderr, "ERROR: Failed to prepare MIDI header.\n"); 61 | return -1; 62 | } 63 | 64 | if(midiOutLongMsg(midi_handle, &mheader, sizeof(mheader)) != MMSYSERR_NOERROR) { 65 | midiOutUnprepareHeader(midi_handle, &mheader, sizeof(mheader)); 66 | free(tempbuf); 67 | fprintf(stderr, "ERROR: Failed to send SysEx message.\n"); 68 | return -1; 69 | } 70 | 71 | // Wait for message to be fully sent. 72 | while(MIDIERR_STILLPLAYING == midiOutUnprepareHeader(midi_handle, &mheader, sizeof(mheader))) { 73 | Sleep(10); 74 | } 75 | 76 | free(tempbuf); 77 | return 0; 78 | } 79 | 80 | if(len==2) { 81 | DWORD out = ((DWORD)buf[1])<<8 | (DWORD)buf[0]; 82 | if(midiOutShortMsg(midi_handle, out) != MMSYSERR_NOERROR) { 83 | fprintf(stderr, "Error sending MIDI message: %02X %02X\n", buf[0], buf[1]); 84 | return -1; 85 | } 86 | return 0; 87 | } 88 | 89 | if(len==3) { 90 | DWORD out = ((DWORD)buf[2])<<16 | ((DWORD)buf[1])<<8 | (DWORD)buf[0]; 91 | if(midiOutShortMsg(midi_handle, out) != MMSYSERR_NOERROR) { 92 | fprintf(stderr, "Error sending MIDI message: %02X %02X %02X\n", buf[0], buf[1], buf[2]); 93 | return -1; 94 | } 95 | return 0; 96 | } 97 | 98 | fprintf(stderr, "ERROR: Invalid MIDI message.\n"); 99 | return -1; 100 | } 101 | 102 | void mididev_print_usage(void) { 103 | printf("-p PORT : Set MIDI output port number (*MANDATORY*).\n"); 104 | printf("-l : List available MIDI output ports and exit.\n"); 105 | } 106 | 107 | void mididev_add_optstr(char *optstr) { 108 | int main_len = strlen(optstr); 109 | strcpy(optstr+main_len, dev_optstr); 110 | } 111 | 112 | int mididev_parse_arg(int c, const char *optarg) { 113 | if(c=='l') { 114 | list_midi_outputs(); 115 | exit(EXIT_SUCCESS); 116 | } 117 | 118 | if(c=='p') { 119 | output_port = (int) strtoul(optarg, NULL, 10); 120 | return 0; 121 | } 122 | 123 | fprintf(stderr, "ERROR: Unknown option \'%c\'.\n", c); 124 | return -1; 125 | } 126 | 127 | static void list_midi_outputs(void) { 128 | UINT n_outs = 0; 129 | MIDIOUTCAPS c; 130 | 131 | n_outs = midiOutGetNumDevs(); 132 | if (!n_outs) { 133 | fprintf(stderr, "ERROR: No MIDI output ports found.\n"); 134 | return; 135 | } 136 | 137 | printf("MIDI output ports:\n"); 138 | for(UINT i=0; i < n_outs; i++) { 139 | midiOutGetDevCaps(i, &c, sizeof(MIDIOUTCAPS)); 140 | fprintf(stderr, "%d: %s\n", i, c.szPname); 141 | } 142 | } 143 | 144 | --------------------------------------------------------------------------------