Jim Lawless' Blog


A Command-Line MP3 Player for Windows

Originally published on: Sun, 02 Aug 2009 03:44:19 +0000

Please note! If you're having difficulties compiling the C source code presented below, please see my post: Compiling C from the Command Line with Pelles C

( See also http://www.mailsend-online.com/blog/play-mp3-files-with-python-on-windows.html ... a newer post that describes how to use the same technique presented below to play MP3's in Python scripts. )

I was thinking about writing a scriptable MP3 player today and realized that I really didn't know how to cause an MP3 to play on a recent version of Windows. I chose to create a command-line MP3 utility named cmdmp3.

I had used the sndPlaySound() API call to play WAV files, but I had assumed that playing an MP3 wouldn't be quite that easy. I found that I needed to use the Media Control Interface API via the mciSendString() function.

I'm no stranger to writing C code to use Windows API calls. I expected that I'd have to fill out a bunch of data structures with a DWORD in the front to determine the given structure's size and assumed that I'd have to learn a lot of new bitwise flags.

I was pleasantly surprised; the mciSendString() function accepts a text command string with parameters. Instead of having to learn a constant to represent a WAIT flag, I simply needed to append the word "Wait" to the end of the string that plays the MP3.

To play an MP3, cmdmp3 issues three MCI commands:

Close All
Open filename Type MPEGVideo Alias theMP3
Play theMP3 Wait

The first command closes active media that's playing. I assume that this is only within the same process, as I was able to launch two concurrent invocations of cmdmp3.exe and heard both MP3's playing simultaneously.

The next command was the Open command. Note that the type is specified as MPEGVideo even though audio only will be played. the Alias option causes the symbol theMP3 to act as a sort of handle variable for use with the Play command.

The filename specified in the open cannot contain spaces as the MCI string parser will assume that the latter parts of such a filename are parameters. In order to accommodate common file paths with spaces in them, cmdmp3 calls the GetShortPathName() API function to obtain an equivalent filename with all of the spaces removed.

The final command that causes the MP3 to be heard is the Play command. I added the "Wait" option at the end of the play string to prevent the API from returning while the MP3 plays. This would have caused the cmdmp3 process to terminate. Terminating cmdmp3 will halt the playing MP3.

After writing cmdmp3, I realized that others might enjoy using it, but might not want a console window to be displayed while the MP3 plays. You'll see an alternate version of the source called cmdmp3win that provides a window-only version of the player.

The program cmdmp3win.exe does not create its own window ... it simply tries to play the MP3, then terminates.

The command-line syntax is spartan; the first parameter of each program is used as the filename. Filenames with spaces in their name or path should be enclosed in double-quotes.

cmdmp3 "c:\my songs\something.mp3" or cmdmp3win "c:\my songs\something.mp3"

cmdmp3.c


// cmdmp3
// A command-line MP3 player for Windows
// (console-mode version)
//
// License: MIT / X11
// Copyright (c) 2009 by James K. Lawless
// jimbo@radiks.net http://www.radiks.net/~jimbo
// http://www.mailsend-online.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#include <windows.h>
#include <stdio.h>
#include <string.h>

#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"kernel32.lib")

void sendCommand(char *);

int main(int argc,char **argv) {
   char shortBuffer[MAX_PATH];
   char cmdBuff[MAX_PATH + 64];
   printf("cmdmp3\n");
   printf("Command-line MP3 player\n");
   printf("by Jim Lawless\n\n");
   
   if(argc<2) {
      fprintf(stderr,"Syntax:\n\tcmdmp3 \"c:\\path to file\\file.mp3\"\n");
      return 1;
   }
      // Get the shortened path because the MCI string interpreter uses spaces
      // as a separator. If spaces appear in the commands, parts of the filename
      // would be interpreted as paramters to the given command.
   GetShortPathName(argv[1],shortBuffer,sizeof(shortBuffer));
   if(!*shortBuffer) {
      fprintf(stderr,"Cannot shorten filename \"%s\"\n",argv[1]);
      return 1;
   }
   sendCommand("Close All");

   sprintf(cmdBuff,"Open %s Type MPEGVideo Alias theMP3",shortBuffer);
   sendCommand(cmdBuff);

   sendCommand("Play theMP3 Wait");
   return 0;
}

   // Send a string to the Media Control Interface
   // If an error occurs, display it and the string
   // that produced the error.
void sendCommand(char *s) {
   int i;
   i=mciSendString(s,NULL,0,0);
   if(i) {
         fprintf(stderr,"Error %d when sending %s\n",i,s);
   }
}

cmdmp3win.c


// cmdmp3win
// A command-line MP3 player for Windows
// (window-mode version)
//
// License: MIT / X11
// Copyright (c) 2009 by James K. Lawless
// jimbo@radiks.net http://www.radiks.net/~jimbo
// http://www.mailsend-online.com
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.



#include <windows.h>
#include <string.h>

#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"kernel32.lib")
#pragma comment(lib, "user32.lib")

char msg[256];

extern int __argc;
extern char ** __argv;


void sendCommand(char *);

int WINAPI WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow ) {

   char shortBuffer[MAX_PATH];
   char cmdBuff[MAX_PATH + 64];
   
   if(__argc<2) {
      sprintf(msg,"Syntax:\n\tcmdmp3win \"c:\\path to file\\file.mp3\"\n");
      MessageBox(NULL,msg,"cmdmp3win",MB_OK);
      return 1;
   }
      // Get the shortened path because the MCI string interpreter uses spaces
      // as a separator. If spaces appear in the commands, parts of the filename
      // would be interpreted as paramters to the given command.
   GetShortPathName(__argv[1],shortBuffer,sizeof(shortBuffer));
   if(!*shortBuffer) {
      sprintf(msg,"Cannot shorten filename \"%s\"\n",__argv[1]);
      MessageBox(NULL,msg,"cmdmp3win",MB_OK);
      return 1;
   }
   sendCommand("Close All");

   sprintf(cmdBuff,"Open %s Type MPEGVideo Alias theMP3",shortBuffer);
   sendCommand(cmdBuff);

   sendCommand("Play theMP3 Wait");
   return 0;
}

   // Send a string to the Media Control Interface
   // If an error occurs, display it and the string
   // that produced the error.
void sendCommand(char *s) {
   int i;
   i=mciSendString(s,NULL,0,0);
   if(i) {
         sprintf(msg,"Error %d when sending %s\n",i,s);
         MessageBox(NULL,msg,"cmdmp3win",MB_OK);
   }
}

The source and EXE files for cmdmp3 and cmdmp3win can be found here.

http://www.mailsend-online.com/wp/cmdmp3.zip

Unless otherwise noted, all code and text entries are Copyright 2009 by James K. Lawless



Views expressed in this blog are those of the author and do not necessary reflect those of the author's employer. Views expressed in the comments are those of the responding individual.

stumbleupon Save to StumbleUpon
digg Digg it
reddit Save to Reddit
facebook Share on Facebook
twitter Share on Twitter
aolfav More bookmarks


Previous post: Checking Shift States with DEBUG
Next post:A Quine in Forth


About Jim ...


Follow me on Twitter

http://twitter.com/lawlessGuy


My GitHub Repository

https://github.com/jimlawless


Recent Posts

Compiling C from the Command Line with Pelles C

A Forthcoming Marvel Movie Villain

Uninstalling Problematic Windows Software

Don't be Hatin'

A JavaScript REPL for Android Devices

MailSend is Free

My Blog Engine

The October 10th Bug


Random Posts

Mad Schemes : Learning Lisp via SICP

Throwaway Software: HangUp

Extending SpiderMonkey JavaScript on Windows

A DSL in JavaScript

E-mail cleansing

Along Came AWK

Invoking the Default Windows Screen-Saver

A Data Manipulation Library for TAP

BBS Fun in the Eighties

A Review of Kevin Mitnick's Book Ghost in the Wires


Full List of Posts

http://www.mailsend-online.com/bloglist.htm


Recent Posts from my Other Blog / GitHub

Using Standard C Libraries with Yosemite JavaScript

JavaScript Libraries in Yosemite

Command Line JavaScript in Yosemite

Text to Speech, Yosemite, and JavaScript

A Simple (but useful) TCP Client Written in Go

Yet Another Seven Languages in Seven Weeks

Happy 50th Birthday, BASIC

Extending Commodore 64 BASIC

Hide the HTTP-Referer using HTML and JS

RSS Feed Processing in Python

A Chromecast Slideshow using Python

A Simple Perl REPL

Linux Mint on a Toshiba Netbook

Find and View All Images on a HD with Perl

Pretty-Printing an s-expression in Go

My Personal Text to HTML Utility

1985 Computing : Atari and Commodore

My Mac has Mono

Yet Another Config File Reader for Go (Golang)

Filling a Slice Using Command-line Flags in Go (Golang)

An RPN Interpreter in Go (Golang)

Simulating Try-Catch in Go (Golang)

Sending GMail with Go (Golang)

Variant Types in Golang

My First C64 Interrupt Program

The Triangles Puzzle

Happy 25th, Perl !

My Favorite BASIC One-Liner

Playing with OS/X Text to Speech

The Villain at the end of Marvel's Avengers Move is...

Chicken! Fight like a Robot!

Processing GMail items with Ruby

The Squares Puzzle

Happy 30th Birthday, Commodore 64

Scripting Safari

MailWrench CSharp Command Line Mailer for Windows is now Free Software

Welcome Back, M.U.L.E. !

Rainy Day Fun with the HTML DOM

Building a World War II Foxhole Radio

Prototerp Unleashing a JavaScript REPL via the Mobile Browser

Steal This Bookmarklet

Happy Birthday, Miles