Skip to content

Instantly share code, notes, and snippets.

@RobertAudi
Last active September 30, 2023 19:54
Show Gist options
  • Save RobertAudi/6045338 to your computer and use it in GitHub Desktop.
Save RobertAudi/6045338 to your computer and use it in GitHub Desktop.
This is a walkthrough on how to install the MOC command-line music player on OS X. The procedure was tested in Mountain Lion.

MOC on OS X

I waited for years for a Homebrew formula for MOC. I finally found one today, but it didn't work for me. So I decided to try to compile it from source.

Requirements

Here is a list of requirements, taken directly from the MOC README:

  • UNIX system with POSIX threads (e.g., Linux or FreeBSD)
  • ncurses (probably already installed in your system)
  • C and C++ compilers (MOC is written in C, but libtool and some decoder plugins require a C++ compiler)
  • libdb version 4 or later (unless configured with --disable-cache)

To support audio formats you need:

For network streams:

For resampling (playing files with sample rate not supported by your hardware):

For JACK (low-latency audio server):

For librcc (fixes encoding in broken mp3 tags):

So first you need to install Xcode and Homebrew if you don't already have them installed; I'll let you Google on how to do that. You also need to install a dependency that is not listed above: BerkeleyDB. Luckily there is a Homebrew package for it:

% brew install berkeley-db

Edit: The BerkeleyDB dependency is actually listed above, as libdb. Thanks to John Fitzgerald, MOC's Maintainer, for the note.

NOTE: % is my prompt, don't copy it, copy just the brew command!

Then you need to install JACK, because it's the audio server that will be used by MOC:

% brew install jack

Finally you need to install the required library to play MP3s, and other audio formats. All my music is in MP3 so I just had to install two libraries:

% brew install libmad
% brew install libid3tag

Compiling MOC from source

First you need to download MOC from here. I downloaded the Development release (2.5.0-beta1). Next you need to go extract the archive you just downloaded:

% cd ~/Downloads
% tar xvjf moc-2.5.0-beta1.tar.bz2
% cd moc-2.5.0-beta1

Next you need to configure, compile and install MOC:

% ./configure --prefix=/usr/local/Cellar/moc/2.5.0-beta1 --with-jack --enable-debug --without-ffmpeg
% make
% make install

NOTE: I haven't compiled MOC with the --without-ffmpeg option myself, but it should work. If it doesn't, uninstall ffmpeg using brew uninstall ffmpeg. A big Thank you to jcf for the tip!

Configuring MOC

Now that you installed MOC, you need to configure it. The first thing I needed to do, is add the bin directory to my $PATH:

  • If you are using ZSH, add this line to your .zshrc:
path=( /usr/local/Cellar/moc/2.5.0-beta1/bin $path )
  • If you're using Bash, add this line to your .bashrc:
PATH=/usr/local/Cellar/moc/2.5.0-beta1/bin:$PATH

Next, you need to create a directory for MOC, in your home directory:

% mkdir ~/.moc

Let's skip the config file step for a minute and let's create a custom theme. First, create the themes directory:

% mkdir ~/.moc/themes

I use a theme I found on the CrunchBang forums. All you need to do is to create a new file for the theme with the following content:

% vim ~/.moc/themes/rhowaldt_theme
background           = default    default
frame                = default    default
window_title         = default    default
directory            = blue       default
selected_directory   = blue       default    reverse
playlist             = default    default
selected_playlist    = default    default    reverse
file                 = default    default
selected_file        = default    default    reverse
marked_file          = blue       default    bold
marked_selected_file = blue       default    reverse
info                 = default    default
selected_info        = default    default
marked_info          = blue       default    bold
marked_selected_info = blue       default    bold
status               = default    default
title                = blue       default    bold
state                = default    default
current_time         = default    default
time_left            = default    default
total_time           = default    default
time_total_frames    = default    default
sound_parameters     = default    default
legend               = default    default
disabled             = default    default
enabled              = blue       default    bold
empty_mixer_bar      = default    default
filled_mixer_bar     = default    default    reverse
empty_time_bar       = default    default
filled_time_bar      = default    default    reverse
entry                = default    default
entry_title          = default    default
error                = default    default    bold
message              = default    default    bold
plist_time           = default    default

NOTE: I removed the documentation comments from the theme, but you can find the whole theme file here

Now you need a config file. Let's copy the example config file that came with MOC:

% cp /usr/local/Cellar/moc/2.5.0-beta1/share/doc/moc/config.example ~/.moc/config

Next, edit it with the following config:

% vim ~/.moc/config

NOTE: This configuration assumes that you are using iTerm2. Feel free to fiddle with it.

ReadTags = yes
MusicDir = /Users/aziz/Music
StartInMusicDir = yes
SoundDriver = JACK
XTerms = xterm-256color
Theme = rhowaldt_theme
MOCDir = ~/.moc
UseRCC = no

And...you're done configuring MOC!

Running MOC

Before you run MOC, you need to run JACK, otherwise it won't work! You can run it manually using this command:

% jackd -d coreaudio

But that's tedious to do! So let's create a launch agent:

% vim ~/Library/LaunchAgents/org.jackaudio.jackd.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>org.jackaudio.jackd</string>
    <key>WorkingDirectory</key>
    <string>/Users/aziz/</string>
    <key>ProgramArguments</key>
    <array>
      <string>/usr/local/Cellar/jack/1.9.7/bin/jackd</string>
      <string>-d</string>
      <string>coreaudio</string>
    </array>
    <key>EnableGlobbing</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
  </dict>
</plist>

And load that launch agent:

% launchctl load ~/Library/LaunchAgents/org.jackaudio.jackd.plist

Now you are finally ready to use MOC!!!

% mocp

To see the list of available commands, type ? once in MOC. The cool thing about this is that you can run MOC in the background: just type q (lowercase not upercast).

One more thing

I found a Gist that I created a while ago. It's a bash script to enable Growl notifications for MOC! I doubt I wrote it myself, but I can't find its origins so I can't really give credit where credit is due; if you wrote this little script, post a comment and I'll update the Gist. Here it is:

#!/bin/bash

while out=$(/usr/local/Cellar/moc/2.5.0-beta1/bin/mocp -i); do

  # Parse mocp output.
  while IFS=: read -r field value; do
    case $field in
      Artist) artist=$value;;
      Album) album=$value;;
      SongTitle) title=$value;;
    esac
  done <<< "$out"

  # Don't do anything if we're still on the same song.
  [[ "$artist-$album-$title" = "$current" ]] && { sleep 1; continue; }

  # Growl notify this information
  if [[ $album && $artist && $title ]]; then
    /usr/local/bin/growlnotify -t "moc: $title" -n "mocp" -m "by $artist"$'\n'"(album: $album)"
  fi

  # Remember the current song.
  current="$artist-$album-$title"

done

Put that anywhere you want, it really doesn't matter. I've put it the bin directory in my Home directory. Make sure you make the script executable with chmod a+x moc-growl.sh. Again, you can run the script manually, but it's tedious, so I created a launch agent:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>Label</key>
    <string>me.azizlight.mocgrowl</string>
    <key>WorkingDirectory</key>
    <string>/Users/aziz/</string>
    <key>ProgramArguments</key>
    <array>
      <string>/bin/sh</string>
      <string>/Users/aziz/bin/moc-growl.sh</string>
    </array>
    <key>EnableGlobbing</key>
    <true/>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
  </dict>
</plist>

And don't forget to load the launch agent:

% launchctl load ~/Library/LaunchAgents/me.azizlight.mocgrowl.plist

The cherry on the cake

Yes there is YET ONE MORE THING! I managed to map the function keys F7, F8 and F9 to moc actions using applescript! Here is how to do it:

  • Open Automator
  • Create a new Service
  • Choose "Run AppleScript" from the list of actions
  • Insert the script below in the mini AppleScript editor
  • Save
  • Go in the Keyboard Shortcuts System Preferences
  • Locate your new service
  • Assign it a shortcut

I chose those function keys because they match the media keys. Do this for every script show below (three times in total):

-- This is the script to Play/Pause
do shell script "/usr/local/Cellar/moc/2.5.0-beta1/bin/mocp -G"
-- This is the script to go to the Next Song
do shell script "/usr/local/Cellar/moc/2.5.0-beta1/bin/mocp -f"
-- This is the script to go to the Previous Song
do shell script "/usr/local/Cellar/moc/2.5.0-beta1/bin/mocp -r"

Alfred Workflow

I also created an Alfred Workflow to show the current playing song and triggering the play/pause/next/previous functionality. The Workflow can be downloaded here. Once downloaded, just double-click on the file and it should install it.

Voila! A full headless command-line alternative to iTunes!

Enjoy!

@toroidal-code
Copy link

I created a Homebrew formula, it's a pull request right now, but it works. (I finished it before I saw this)
I'll reference this gist in the caveats as it seems really useful.
Homebrew/legacy-homebrew#22044

@dminkovsky
Copy link

Very awesome. Thank you for this gist!! Baller.

@dminkovsky
Copy link

@azizLIGHT: have you had any difficulty with MP3 playback? Which version of ffmpeg are you using?

@dpsk
Copy link

dpsk commented Oct 7, 2013

Great stuff, maybe it makes more sense to use apple notifications, instead of Growl?
I added notes about this in my fork https://gist.github.com/dpsk/6868634

@seekerFactory
Copy link

Super *
Worked for me with both moc 2.5.0-beta1 / beta2

@luckydonald
Copy link

@RobertAudi
The link to the theme's original is 404ed, do you still have the documented version to put online?

@ddmytrenko
Copy link

Second plist will not work. Just because the script will terminate all the time. Your script needs mocp to be already running.

@ddmytrenko
Copy link

We can extend your script. And use Open Source terminal-notifier instead of proprietary Growl.

#!/bin/bash

while :; do

    out=$(mocp -i)

    if [[ $out ]]; then

        # Parse mocp output
        while IFS=: read -r field value; do
            case $field in
                Artist) artist=$value;;
                Album) album=$value;;
                SongTitle) title=$value;;
            esac
        done <<< "$out"

        # Ignore if we are still on the same song
        [[ "$artist-$album-$title" = "$current" ]] && { sleep 1; continue; }

        # Show notification using terminal-notifier
        if [[ $album && $artist && $title ]]; then
            terminal-notifier -title "$title" -subtitle "$artist" -message "$album"
        fi

        # Store current song
        current="$artist-$album-$title"

    else
        sleep 1
    fi

done

@PedroLopes
Copy link

First of all, thanks for the how-to, I keep coming here everytime I re-install mocp!

Alfred workflow is 403 on your dropbox link, can you upload? I'm using spark with exactly the same commands you used on your applescript via automator, does the trick too. But got curious of the alfred workflow!

@hankchanocd
Copy link

Just a quick shoutout: If you are downloading MOC directly from Homebrew, the version of Jack that comes with it is 0.125.0_2 (as of 4 Feb, 2018). Don't forget to reflect this change in the xml file for running Jack, as in
<string>/usr/local/Cellar/jack/1.9.7/bin/jackd</string>
should be replaced with
<string>/usr/local/Cellar/jack/0.125.0_2/bin/jackd</string>

or you would get a crash alarm saying "Jack: not a valid server"...

@myelsukov
Copy link

This thread refuses to die :)

Notifications using polling is overkill.
MOC has nice built-in feature for that:

# Run the OnSongChange command when a new song starts playing.
# Specify the full path (i.e. no leading '~') of an executable to run.
# Arguments will be passed, and you can use the following escapes:
#
#     %a artist
#     %r album
#     %f filename
#     %t title
#     %n track
#     %d file duration in XX:YY form
#     %D file duration, number of seconds
#
# No pipes/redirects can be used directly, but writing a shell script
# can do the job.
#
# Example:    OnSongChange = "/home/jack/.moc/myscript %a %r"
#
#OnSongChange =

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment