Play sound files via mciSendString() commands.

banders7 -3 Tallied Votes 3K Views Share

This program was developed and tested in the XP environment, so the requisite platform is Windows. It demonstrates some basic sound file manipulation techniques using the Multimedia Control Interface (MCI).

jephthah commented: it was a worthy attempt, nonetheless. and as it stands, will be valuable even if only to our sizeable Borland/Turbo-using international students ;) +6
Ancient Dragon commented: At the very least you need to get a clean compile with the code you want to post here. -5
#include <stdlib.h>
#include <stdio.h>
#include <dir.h>
#include <string.h>
#include <conio.h>
#include <io.h>
#include <windows.h>
// While MCI can play video files as well as sound files,
// this program confines itself primarily to sound files.
// This program will run videos, but I make no effort to manage them
// CD tracks (.cda) can also be played, but like videos, require
// more management than I wanted to build into this demonstrator
int main(int argc, char *argv[])
{
int i,j,k;
char filename[MAXDIR]=""; // Name of file to play
char data[1025];
int dsz=sizeof(data)-1;
char mcidata[129]; // Data is returned by some MCI requests
int  mcidatalen=sizeof(mcidata)-1;
char mcimsg[129];  // Message returned by mciGetErrorString
int  mcimsglen=sizeof(mcidata)-1;
char mcicmd[129];
int rc;
char playtime[32];
int seconds,played,remaining;
int x,y;
char ext[MAXEXT],fname[MAXFILE];
char types[128]=".wav .mid .wma .mp3 .cda .wmv"; // .wmv was an afterthought

int done;
struct ffblk file;
char srch[MAXDIR];
int att=FA_NORMAL;

char millisecs[5];
int min,sec;
char minc[4]="000",secc[4]="000";
int filesfound=0;
int remainingsecs;
int sleeptime;

argc--;
if (argc == 0)
   {
   printf("Enter the name of a .mp3, .wma, .mid, .wav, .cda or .wmv file to play\n");
   return 1;
   }

// Get file name. Allow for long file names.
for (i=1; i<argc; i++)
   {
   strcat(filename,argv[i]);
   strcat(filename," ");
   }
strcat(filename,argv[i]);

// Check to see if a file extension has been included
fnsplit(filename,NULL,NULL,NULL,ext);
// No ext. Try to find a matching file name in current/supplied directory
// If more than 1 found, call it "Abiguous"
if (strlen(ext) == 0)
   {
   sprintf(srch,"%s.*",filename);
   for (done=findfirst(srch,&file,att); done==0; done=findnext(&file))
      {
      fnsplit(file.ff_name,NULL,NULL,fname,ext);
      strlwr(file.ff_name); // Shift lower to match "types" case (strstr)
      if (stricmp(fname,mcidata) == 0 && strstr(types,ext) > 0)
         filesfound++;
      }
   if (filesfound > 0)
      {
      printf("%d matching file names found with different types\n",filesfound);
      printf("Please supply a file type as well to rectify the ambiguity\n");
      return 1;
      }
   strcat(filename,ext);
   }
// Does specified file exist?
if (access(filename,0) != 0)
   {
   printf("File '%s' not found. Check spelling and retry.\n",filename);
   return 1;
   }

clrscr();
printf("Playing file: %s\n",filename);
// Put quotes around the file name if it contains blanks
if (strstr(filename," ") > 0)    
   {
   strcpy(data,"\"");
   strcat(data,filename);
   strcat(data,"\"");
   strcpy(filename,data);
   }


// Issue open. Set alias to MEDIAFILE (alias name is completely arbitrary)
// MCI ignores caps ... I use them here to make the file alias stand out
sprintf(mcicmd,"open %s alias MEDIAFILE",filename);
rc = mciSendString(mcicmd,mcidata,mcidatalen,NULL);
if (rc != 0)
   {
   mciGetErrorString(rc,mcimsg,mcimsglen);
   printf("MCI Error: %s\n",mcimsg);
   return rc;
   }

mciSendString("status MEDIAFILE time format",mcidata,mcidatalen,NULL);
//printf("Time format: %s\n",mcidata);
if (stricmp(mcidata,"song pointer") == 0) // Returned by .mid files
   {
//   printf("     Converting time format to milliseconds\n");
   rc = mciSendString("set MEDIAFILE time format ms",mcidata,mcidatalen,NULL);
   if (rc != 0)
      {
      mciGetErrorString(rc,mcimsg,mcimsglen);
      printf("MCI Error: %s\n",mcimsg);
      return rc;
      }
   }

mciSendString("status MEDIAFILE length",mcidata,mcidatalen,NULL);
strcpy(playtime,mcidata); // could have used playtime in above call ...
// time format is SSSmmm (seconds, milliseconds);
// 0 Pad play time to fixed length of 6
strrev(playtime);
while(strlen(playtime) < 6)
   strcat(playtime,"0");
strrev(playtime);
strncpy(minc,playtime,3);
sec = atoi(minc);
i = sec; // save full value for play time display
strcpy(millisecs,playtime+3);
if (strcmp(millisecs,"499") > 0)
   sec++;
min = sec/60;
sec = sec%60;
printf("\nPlay time: %d.%s seconds (%d:%02d)\n",i,millisecs,min,sec);
printf("\n<Press any key to terminate play>\n");

seconds = atoi(playtime);
// mciSendString("setaudio MEDIAFILE volume to nnn",mcidata,mcidatalen,NULL)
// 0 <= nnn <= 1000. Does not work for all types. (good for mp3s & cds for sure)
// for some file types right/left volume can be set independently
// setaudio MEDIAFILE left/right volume to nnn

rc = mciSendString("play MEDIAFILE",NULL,0,NULL);
if (rc != 0)
   {
   mciGetErrorString(rc,data,mcidatalen);
   printf("MCI Error: %s\n",data);
   return rc;
   }

printf("\nRemaining: ");
y = wherey();
x = wherex();

// Run a wait for key press/sleep loop while song plays asynchronously
// Abort play on key press.
while(!kbhit())
   {
   // Get current SSSmmm position in the file
   mciSendString("status MEDIAFILE position",mcidata,mcidatalen,NULL);
   played = atoi(mcidata);
   // We have reached the end of the file ... close communication & exit
   if (played == seconds)
      {
      gotoxy(x,y);
      clreol();
      printf("0\n");
      // Play has stopped on its own ... the stop is not technically required
      mciSendString("stop MEDIAFILE",NULL,0,NULL);
      mciSendString("close MEDIAFILE",NULL,0,NULL);
      return 0;
      }
   remaining = seconds-played;
   remainingsecs = remaining/1000; // take just secs, ignore rounding up
   if (stricmp(ext,".wav") == 0) // wavs are short ... fast refresh
      {
      // Show remaining time as SSS.mmm
      sprintf(data,"%d",remaining);
      strrev(data);
      while(strlen(data) < 6)
         strcat(data,"0");
      strrev(data);
      strncpy(minc,data,3); // "Borrow" minc/secc fields
      strncpy(secc,data+3,3);
      gotoxy(x,y);
      clreol();
      sprintf(data,"%d.%d",atoi(minc),atoi(secc));
      sleeptime = 100; // Refresh every 1/10th second for wavs
      }
   else
      {
      // Show time remaining as MM:SS
      min = remainingsecs/60;
      sec = remainingsecs%60;
      sprintf(data,"%d:%02d",min,sec);
      sleeptime = 1000; // Refresh every 1 second for other file types
      }
   gotoxy(x,y);
   clreol();
   printf("%s\n",data);
   Sleep(sleeptime);
   }
// Play interrupted by keyboard activity.
// Let's get rid of key pressed
if (k=getch() == 0) // handle normal + extended key interrupts
   getch();
gotoxy(x,y);
clreol();
printf("0\n");
printf("\n<Play terminated due to keyboard interruption>\n");
// Stop play before closing communication
// If stop is not issued for a playing CD before close, CD continues to play
// For wav,mid & mp3 files, stop seems to be auto issued if close issued.
mciSendString("stop MEDIAFILE",NULL,0,NULL);
mciSendString("close MEDIAFILE",NULL,0,NULL);
return 0;
}
// Points of interest on CDs
// To open a CD drive rather than specific tracks .... 2 methods
// open cdaudio alias CD         (gets first or only drive)
// open X: type cdaudio alias CD (open a specific drive. X = a drive letter)
// To seek tracks on a CD, time format must be set to tmsf. dflt=msf.
// set CD time format tmsf
// seek CD to 7
// play CD
// status CD number of tracks - value returned in mcidata (using my var name)
// status CD current track    - ditto.
// See MCI documentation for more details
jephthah 1,888 Posting Maven

non-standard libraries. fatal compile errors. Failure.

||=== mci, Debug ===|
C:\sandbox\mci\mci_main.c||In function `main':|
C:\sandbox\mci\mci_main.c|16|error: `MAXDIR' undeclared (first use in this function)|
C:\sandbox\mci\mci_main.c|16|error: (Each undeclared identifier is reported only once|
C:\sandbox\mci\mci_main.c|16|error: for each function it appears in.)|
C:\sandbox\mci\mci_main.c|28|error: `MAXEXT' undeclared (first use in this function)|
C:\sandbox\mci\mci_main.c|28|error: `MAXFILE' undeclared (first use in this function)|
C:\sandbox\mci\mci_main.c|32|error: storage size of 'file' isn't known|
C:\sandbox\mci\mci_main.c|34|error: `FA_NORMAL' undeclared (first use in this function)|
C:\sandbox\mci\mci_main.c|59|warning: implicit declaration of function `fnsplit'|
C:\sandbox\mci\mci_main.c|65|warning: implicit declaration of function `findfirst'|
C:\sandbox\mci\mci_main.c|65|warning: implicit declaration of function `findnext'|
C:\sandbox\mci\mci_main.c|87|warning: implicit declaration of function `clrscr'|
C:\sandbox\mci\mci_main.c|158|warning: implicit declaration of function `wherey'|
C:\sandbox\mci\mci_main.c|159|warning: implicit declaration of function `wherex'|
C:\sandbox\mci\mci_main.c|171|warning: implicit declaration of function `gotoxy'|
C:\sandbox\mci\mci_main.c|172|warning: implicit declaration of function `clreol'|
C:\sandbox\mci\mci_main.c|211|warning: suggest parentheses around assignment used as truth value|
C:\sandbox\mci\mci_main.c|15|warning: unused variable `j'|
C:\sandbox\mci\mci_main.c|16|warning: unused variable `filename'|
C:\sandbox\mci\mci_main.c|18|warning: unused variable `dsz'|
C:\sandbox\mci\mci_main.c|28|warning: unused variable `ext'|
C:\sandbox\mci\mci_main.c|28|warning: unused variable `fname'|
C:\sandbox\mci\mci_main.c|32|warning: unused variable `file'|
C:\sandbox\mci\mci_main.c|33|warning: unused variable `srch'|
||=== Build finished: 7 errors, 16 warnings ===|
Ancient Dragon 5,243 Achieved Level 70 Team Colleague Featured Poster

Yup -- previous errors confirmed when I tried to compile it with Code::Blocks. Will not compile at all with VC++ 2008 Express because it does not support dir.h

banders7 1 Newbie Poster

My sincere apologies.

It looks to be a compiler specific issue. I should have mentioned this was written using a Borland C++ compiler. Any further postings I make will explicitly note the Borland dependency.

Short term, based on the errors your compiler reported, you can, if you're really interested in pursuing this:

Replace MAXEXT,MAXFILE,MAXDIR with 256.
Comment out/delete lines 31 through 34.
Comment out/delete lines 58 through 79. They are not germane to the MCI demonstration. Their only function is to permit supplying a file name without specifying a file type ... a courtesy, not a necessity. That also takes care of the FA_NORMAL problem.

Further, comment out/delete the statements with references to gotoxy, clreol, clrscr, wherex, wherey. Commenting out the (repeating) print statements within the kbhit() loop would probably be a good idea as well. The majority of the screen display is not critical to the testing of MCI.

I will respond to any further problems or concerns with the code that you might have.

jephthah 1,888 Posting Maven

why would you ever do something that depends on Borland C++? that's got to be one of the worst compilers out there.

please consider using MSVC, GCC or MinGW compilers. there are plenty of free development environments with full featured editors and debuggers that use modern libraries instead of the obscure non-standard ones peculiar to Borland and Turbo C.

jephthah 1,888 Posting Maven

okay, i feel bad now. despite being dependent on legacy libraries, this is some nice looking code ... and the particular problem is an interesting one. i guess that's why i was disappointed i couldn't compile it.

But I also see now that the OP is an experienced programmer who used Borland back when it was top of the line in the 80s and early 90's. .

it's just a shame that today Borland focuses more on cornering the burgeoning Second World market than updating their compilers to the 21st Century.

perhaps in the future banders7 will just check that his code is compilable on MSVC or gcc or MinGW compilers as well as Borland.

Lenardo 0 Newbie Poster

Thanks a lot for the post. I didn't try to compile the entire file but the individual concepts helped me immensely. I had a great deal of difficulty finding such clear coding on the total playback duration and time remaining during playback.

Ancient Dragon 5,243 Achieved Level 70 Team Colleague Featured Poster

okay, i feel bad now. despite being dependent on legacy libraries, this is some nice looking code ... and the particular problem is an interesting one. i guess that's why i was disappointed i couldn't compile it.

But I also see now that the OP is an experienced programmer who used Borland back when it was top of the line in the 80s and early 90's. .

it's just a shame that today Borland focuses more on cornering the burgeoning Second World market than updating their compilers to the 21st Century.

perhaps in the future banders7 will just check that his code is compilable on MSVC or gcc or MinGW compilers as well as Borland.

Borland does have modern 32-bit/64-bit compilers, they are not free. The only free Borland compilers are ancient and obsolete. I can't vouch for how good or bad the newest Borland compilers are because I don't use them. But since Borland is still in business I suppose enough people/companies are buying them to keep them alive.

pheininger 5 Light Poster

Borland does have modern 32-bit/64-bit compilers, they are not free. The only free Borland compilers are ancient and obsolete. I can't vouch for how good or bad the newest Borland compilers are because I don't use them. But since Borland is still in business I suppose enough people/companies are buying them to keep them alive.

Borland separated its IDE and compiler tools into a subsidiary during 2006. The unit was given the name CodeGear. Embarcadero acquired CodeGear from Borland during 2008. The Micro Focus acquired the balance of Borland in 2009.

Ancient Dragon 5,243 Achieved Level 70 Team Colleague Featured Poster

That's bad news. We needed Borland as a competitor to Microsoft. I suppose M$ now has about 90% marketshare of the compiler market.

pheininger 5 Light Poster

We needed Borland as a competitor to Microsoft.

The competitor is now Embarcadero and the various free compilers.

banders7 1 Newbie Poster

I described the compiler used for the snippet development as being Borland. It is actually C++ Builder 2010 from the Embarcadero RAD Studio 2010 offering. Despite the name change, I still tend to think of them as Borland.

Azar Mohamed 0 Newbie Poster

#include <iostream>
#include <windows.h>
#include <mmsystem.h>

using namespace std;

int main()
{
mciSendString("open CDAudio", NULL, 0, NULL);
mciSendString("set CDAudio door open", NULL, 0, NULL);
return 0;
}

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.