33

I'm looking for a most simple command line app (or an on-board tool on Mavericks I am not aware of) to play a midi file from the terminal. As plain as possible, something like play myfile.mid.

Context: I'm playing around with midi in Python and I need something I can launch from a script. No GUI, no windows, just playback. It must be callable from the Python script to be accepted, but anything that works in terminal should be.

bmike
  • 235,889
DCS
  • 353

7 Answers7

42

This worked for me:

$ brew install timidity
$ timidity file.midi
lyderic
  • 421
  • 1
    Worked for me too - and it's a LOT less trouble than the answer involving fluidsynth. – glenra Apr 08 '16 at 01:43
  • 3
    Best answer here. The fluidsynth answer is really clever, but it's liable to break at some point (if it hasn't already) because it's so much more complicated than this one. – Westcroft_to_Apse Apr 20 '16 at 08:31
  • This didn't work for me. After running the first command above, I ran the second, and received this message: file.midi: No such file or directory – user65526 Jan 11 '19 at 11:20
  • I tried this but it didn't play anything. Instead it saved output to a .ogg file, as if I'd specified a "-o" option but I didn't. – sh37211 Feb 17 '24 at 21:07
39

This turned out to be a more complicated problem than I originally expected.

QuickTime X cannot play MIDI files, although QuickTime 7 could.

As far as I can tell that means that there is no "built-in" solution to playing MIDI files on Mac OS X (for example, afplay does not work). Therefore I believe that any solution will involve downloading and installing some other program.

Option #1: Download and install QuickTime 7 which still works fine on Mavericks, and then you can play midi files by:

open -a QuickTime\ Player\ 7 /path/to/your/file.mid 

however that will only autoplay if the user has enabled that preference, which I believe is off by default.

Option #2: Use FluidSynth

To install it, you have to be using either Fink, MacPorts, or (my recommendation) Homebrew. Once Homebrew is installed, type this in Terminal:

brew install fluidsynth

(MacPorts' command would be sudo port install fluidsynth and Fink's would be fink install fluidsynth.)

However, downloading fluidsynth only gets you part-way there. Then you need a "SoundFont" file, which I had never heard of before. There is information about them here

I downloaded one from S. Christian Collins called "GeneralUser" which is free. The current version (as of 2013-11-27) is FluidSynth version 1.44. {If that direct link breaks in the future, use the previous link which will take you to the regular web page for GeneralUser.}

Once you have downloaded and unzipped that, you will have a series of files including "GeneralUser GS FluidSynth v1.44.sf2" (obviously the name may change in the future). I renamed that file and moved it to /usr/local/share/fluidsynth/generaluser.v.1.44.sf2.

Once the SoundFont file is place and fluidsynth is installed, you can play a midi by using this command:

fluidsynth -i /usr/local/share/fluidsynth/generaluser.v.1.44.sf2 ~/Music/example.mid 

n.b. There are some (seemingly harmless) error messages which get displayed when you do that. If you want to suppress them use:

(fluidsynth -i /usr/local/share/fluidsynth/generaluser.v.1.44.sf2 ~/Music/example.mid 2>&1) >/dev/null

instead.

Obviously I'm never going to remember all of that, so I made a zsh function called playmidi

function playmidi {

    SOUNDFONT='/usr/local/share/fluidsynth/generaluser.v.1.44.sf2'

    if [ -e "$SOUNDFONT" ]
    then 

        for i in "$@"
        do 
            if [ -e "$i" ]
            then
                (fluidsynth -i "$SOUNDFONT" "$i"  2>&1) >/dev/null
            else
                echo "[playmidi]: cannot find file at $i"   
                return 1
            fi  
        done 
    else
            echo "[playmidi]: SOUNDFONT file not found at $SOUNDFONT"
            return 1
    fi  
}

(That should work for bash too I believe.)

Now all I have to do is type:

playmidi example.mid 

and example.mid will play.

TJ Luoma
  • 20,659
  • StackExchange at its best! While the fluidsynth option is way more hacking than I was willing to accept (not everyone has brew installed...) it works really well and launches quicker in the script than VLC did. So I'll wait a day or two if some ridiculously simple alternative solution turns up, but if not the bounty is yours. BTW, for me fluid-synth turns up in /usr/local/Cellar/fluid-synth/1.1.6. (I don't like the old version of Quicktime solution, you never know how long it is going to work). – DCS Nov 27 '13 at 13:58
  • Yeah, I agree that "use Homebrew|MacPorts|Fink" isn't an ideal, but a) anyone who spends any amount of time using the command line is going to find themselves wanting more than what Apple can/does provide and b) lacking a built-in solution, it seemed a reasonable one here. As for QuickTime7, I agree, it’s survived longer than I figured it would, but OTOH I can't believe "QuickTime X" still can't do things QuickTime 7 could. (Makes me fearful of iWork's future, but that's another discussion for another thread on another day :-) – TJ Luoma Dec 01 '13 at 06:35
  • The recent release of iWorks actually has been crippled compared to the last version... I'm only waiting for Terminal to disappear. But enough, or we will be bashed for chatter. – DCS Dec 01 '13 at 13:42
  • @DCS We won't bash informed complaints. At worst, we'll ask they spawn to a chat room or open a new thread if warranted. Also, I edited some details out of your post. Please pop that info as an answer or edit one of the answers if that info fits with them. – bmike Dec 01 '13 at 19:06
  • I tried this on Yosemite and could not get it to work. I even uninstalled and reinstalled mac ports. Error: fluidsynth: warning: No preset found on channel 9 [bank=128 prog=56] could not handle external client request. jack main caught signal 31. – Robert Wasmann Mar 17 '16 at 18:56
  • Oh it also said this which is probably the main issue: Parameter '/usr/local/share/fluidsynth/generaluser.v.1.44.sf2' not a SoundFont or MIDI file or error occurred identifying it. – Robert Wasmann Mar 17 '16 at 20:12
  • Even if I run it right from the GeneralMidi folder after unzipping it fails. >fluidsynth -i GeneralUser\ GS\ FluidSynth\ v1.44.sf2 GUTest.mid FluidSynth version 1.1.6 Copyright (C) 2000-2012 Peter Hanappe and others. Distributed under the LGPL license. SoundFont(R) is a registered trademark of E-mu Systems, Inc.

    connect(2) call to /tmp//jack-501/default/jack_0 failed (err=No such file or directory)

    – Robert Wasmann Mar 17 '16 at 20:19
  • Thanks! This is awesome and worked for me! Here's a little piece I was able to compose as a result of this excellent advice: https://gist.github.com/jonathanconway/485c6e4db0aa79a48666b7bcd256f15a – jonathanconway Oct 16 '16 at 08:06
5

You can use VLC with a non-interactive CLI interface with -I dummy:

/Applications/VLC.app/Contents/MacOS/VLC -I dummy file.mid vlc://quit

To enable midi support, go to Preferences > Show All > Input / Codecs > Audio codecs > Fluid Synth and set the soundfont to a file like the FluidSynth soundfont from http://www.schristiancollins.com/generaluser.php.

Edit: midi support was removed from recent version of VLC. You can still use fluidsynth from the shell as described by TJ Luoma:

brew install fluidsynth
wget http://www.schristiancollins.com/soundfonts/GeneralUser_GS_1.44-FluidSynth.zip
unzip GeneralUser_GS_1.44-FluidSynth.zip
mkdir -p /usr/local/share/fluidsynth
mv GeneralUser\ GS\ 1.44\ FluidSynth/GeneralUser\ GS\ FluidSynth\ v1.44.sf2 /usr/local/share/fluidsynth
fluidsynth -i /usr/local/share/fluidsynth/GeneralUser\ GS\ FluidSynth\ v1.44.sf2 file.mid
laurent
  • 1,928
Lri
  • 105,117
  • 1
    Works! However, VLC, being the full-blown super player it is, has some very noticeable startup delay before playback starts, which my Windows solution to the problem, a super-small app named playsmf.exe did not have. That does not kill VLC for my purpose, but having something smaller would still be nice. I'll wait a few days to see if another answer pops up; if not, I'll accept yours. BTW: vlc://quit must be added to the arguments in order to quit VLC and allow my script to resume. – DCS Oct 29 '13 at 15:17
  • See my update above - damn, this used to work well! – DCS Nov 26 '13 at 21:35
  • is it possible to use an old version of vlc? – wrossmck Nov 26 '13 at 22:23
  • @RossMcKinley: While technically possible this is certainly not what I want to do. Software is updated for a reason, and I want a heavy-weight program like VLC to be up to date on my system. This would be different if it was just a 100kb mini tool, but VLC is not like that. – DCS Nov 27 '13 at 06:35
1

According to this post on VLC's own forums Felix Paul Kuehne, the site admin, said

Hello, apparently, FluidSynth got lost in the compilation process for the last update. Be assured that it will be back in 2.1.2 very soon!

So for now either downgrade to vlc 2.0.9, or wait for vlc 2.1.2. This way, your existing solution will work as it used to.

wrossmck
  • 2,406
1

Not exactly the answer but there is a small app that can send midi data on the Mac OS X command line. It can also control basic GarageBand functions as well: http://www.bibiko.de/music/MIDImyAPP/

user72160
  • 11
  • 2
0

To make things more convenient, you can wrap up TJ Luoma's answer in an AppleScript app:

on open inputFile
    tell application "Terminal"
        do script "fluidsynth -i /usr/local/share/fluidsynth/generaluser.v.1.44.sf2 " & (quoted form of POSIX path of inputFile as string)
        activate
    end tell
end open

You can then select Get Info on a random .mid file, change "Open with:" to the AppleScript app, and press "Change All..." to make it the default way of opening .mid files. Now when you double-click on a MIDI file, it'll open up the Terminal window and play it automatically.

0

The following python3 script will use macOS's CoreMIDI to play any MIDI files supplied as arguments on the command line.

#!/usr/bin/env python3

from AVFoundation import AVMIDIPlayer from Foundation import NSURL import time import sys

def myCompletionHandler(): return

def playMIDIFile(filepath): midiFile = NSURL.fileURLWithPath_(filepath) midiPlayer, error = AVMIDIPlayer.alloc().initWithContentsOfURL_soundBankURL_error_(midiFile, None, None) if error: print (error) sys.exit(1) MIDItime = midiPlayer.duration() midiPlayer.prepareToPlay() midiPlayer.play_(myCompletionHandler) if not midiPlayer.isPlaying: midiPlayer.stop() else: time.sleep(MIDItime) return

if name == "main": for filename in sys.argv[1:]: playMIDIFile(filename)

Here's a Swift script which will also play MIDI files given as arguments. You can run the uncompiled script (though it's slower than python!), or compile it to make a command line tool.

#!/usr/bin/swift

import AVFoundation

func playMIDIFile(filepath: String) {

let midiFile = NSURL.fileURL(withPath: filepath) var midiPlayer: AVMIDIPlayer?

do { try midiPlayer = AVMIDIPlayer.init(contentsOf: midiFile as URL, soundBankURL: nil)

if let time = midiPlayer?.duration {
    let hours = Int(floor(time / 3600))
    let minutes = Int(floor(time / 60))
    let seconds = Int((time)) % 60
    let timeString = String(format: "%02d:%02d:%02d", hours, minutes, seconds)
    print("\(filepath) : \(timeString)") 
    }
midiPlayer?.prepareToPlay()
midiPlayer?.play({return})

while midiPlayer!.isPlaying {
    usleep(10000)
}

} catch { print("could not create MIDI player") }

}

// "main" if CommandLine.argc > 1 { for (index, args) in CommandLine.arguments.enumerated() { if index > 0 { // Check that args is MIDI file here. let fileType = (args as NSString).pathExtension if (fileType == "mid" || fileType == "midi" || fileType == "smf") { playMIDIFile(filepath: args) } } }

} else { // For CLI use: print("MIDI files required as arguments") }

benwiggy
  • 35,635