Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

MP3 Support Goto page 1, 2 Next

| Inside3d Forums Forum Index -> Programming Tutorials

View previous topic :: View next topic
Author | Message
--- | ---
Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Nov 18, 2009 2:42 pm Post subject: MP3 Support |

The following tutorial adds MP3 playback support to an engine under Windows with, presumably, DirectX 8 or later installed. Credit: MH and Reckless Requires: DX8.1 SDK "dx81sdk_full.exe" to compile (NOT needed to run the engine) Sample implementation with binary and source: FitzQuake 0.85 MP3
Limitations:

Quote:
1. No volume control for mp3 playback volume 2. No support for mp3s to be in pak files 3. Music goes in quake\<gamedir>\music or quake\id1\music folder 4. CD tracks are named track01.mp3, track02.mp3, etc. 5. Can directly play MP3s via "mp3 play track03"
6. Doing this tutorial adds MP3 support but removes CD support

Instructions:
1. Download cd_win_mp3.c (file)and add to your project.
2. Download qmp3.cpp (file) and add to your project.
3. Remove cd_win.c from your project.
Step 4.
In sound.h, add below "extern vec_t sound_nominal_clip_dist;":

Quote:

Code:
#ifdef MP3_VERSION extern int sound_started; #endif

Step 5.
Open common.h and above "void Q_memset (void *dest, int fill, int count);" add:

Quote:

Code:
#ifdef MP3_VERSION #undef snprintf #undef vsnprintf #ifdef WIN32 # define snprintf _snprintf
# define vsnprintf _vsnprintf
#endif
int va_snprintf (char *function, char *buffer, size_t buffersize, const char *format, ...);
int va_vsnprintf (char *function, char *buffer, size_t buffersize, const char *format, va_list args);
#endif

Step 6.
Open common.c and add this where you'd like. It can go to the botton of the file if you want but it'd be better to include somewhere in the vicinity of the string functions.

Quote:

Code:
#ifdef MP3_VERSION // fast safe printbuffers courtesy of lord havoc. int va_snprintf (char *function, char *buffer, size_t buffersize, const char *format, ...) { va_list args;
int result;
va_start (args, format);
result = va_vsnprintf (function, buffer, buffersize, format, args);
va_end (args);
return result;
}
int va_vsnprintf (char *function, char *buffer, size_t buffersize, const char *format, va_list args)
{
size_t result;
result = vsnprintf (buffer, buffersize, format, args);
if (result < 0 || result >= buffersize)
{
static qboolean inside;
// Beware recursion here
if (!inside)
{
inside = true;
Con_SafePrintf ("%s: excessive string length, max = %d\n", function, buffersize);
}
inside = false;
buffer[buffersize - 1] = '\0';
return -1;
}
return result;
}
#endif

Step #7.1
The MP3 playback needs to pause if the Quake window loses focus and then resume if it gets it back. At least that's what I think. And I was lazy and didn't make proper functions and just tossed commands on the command buffer.
Mostly because I have no time and would like to get this done.
Open gl_vidnt.c and locate this:

Code:
// enable/disable sound on focus gain/loss if (!ActiveApp && sound_active) { S_BlockSound (); sound_active = false;
}
else if (ActiveApp && !sound_active)
{
S_UnblockSound ();
sound_active = true;
}

And replace it with ...

Quote:

Code:
// enable/disable sound on focus gain/loss if (!ActiveApp && sound_active) { S_BlockSound (); #ifdef MP3_VERSION
// Need to pause CD music here if is playing
if (sound_started) {
Cbuf_InsertText ("mp3 pause\n");
Cbuf_Execute ();
}
#endif
sound_active = false;
}
else if (ActiveApp && !sound_active)
{
S_UnblockSound ();
#ifdef MP3_VERSION
// Need to unpause CD music here if was playing
if (sound_started) {
Cbuf_InsertText ("mp3 resume\n");
Cbuf_Execute ();
}
#endif
sound_active = true;
}

Step #7.2
Still in gl_vidnt.c
Find this:

Code:
/* main window procedure */ LONG WINAPI MainWndProc ...

And add above it:

Quote:

Code:
#ifdef MP3_VERSION #ifndef WM_GRAPHNOTIFY #define WM_GRAPHNOTIFY WM_USER + 13 #endif #endif

Step #7.3
This next part, I could Google up what this is doing, but I'm low on time and don't want to. Sorry.
Done is better than not done in this one instance and I have not so much time and so many things I want to get done.
Find this:

Code:
case MM_MCINOTIFY: lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam); break;

And replace with ...

Quote:

Code:
#ifdef MP3_VERSION case WM_GRAPHNOTIFY: #else case MM_MCINOTIFY: #endif
lRet = CDAudio_MessageHandler (hWnd, uMsg, wParam, lParam);
break;

Step 8.
Now MH wrote this very modularly, so it is easy to implement. In the project, you need to define MP3_VERSION. In MSVC6, do Projects -> Settings. And likewise you need to add references to 2 libraries: dsound.lib strmiids.lib
Then compile!

Back to top |

reckless Joined: 24 Jan 2008 Posts: 390 Location: inside tha debugger |

Posted: Wed Nov 18, 2009 3:07 pm Post subject: |

Code:
/* ============ va does a varargs printf into a temp buffer, so I don't need to have varargs versions of all text functions.
============
*/
char *va(const char *format, ...)
{
// now cycles through 8 buffers to avoid problems in most cases
va_list argptr;
static char string[8][1024], *s;
static int stringindex = 0;
s = string[stringindex];
stringindex = (stringindex + 1) & 7;
va_start (argptr, format);
va_vsnprintf ("va", s, sizeof(string[0]), format, argptr);
va_end (argptr);
return s;
}

might want to add this also in regards to lord havocs print buffers
fixes some problems on win7
else looking good

Back to top |

mh Joined: 12 Jan 2008 Posts: 910 |

Posted: Wed Nov 18, 2009 5:35 pm Post subject: |

Nice one! The WM_GRAPHNOTIFY message, by the way, is a custom one generated by DirectShow whenever stuff relating to the playback state changes. Rather evil that it's not defined in the SDK headers. _________________ DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Spirit Joined: 20 Nov 2004 Posts: 476 |

Posted: Wed Nov 18, 2009 6:07 pm Post subject: |

Yay, Baker's tutorials are back! Everyone implementing this or similar functionality, PLEASE be compatible to how Darkplaces has done this for years for Vorbis and WAVE. "id1/sound/cdtracks/track002.ogg" _________________ Quake Maps

Back to top |

mh Joined: 12 Jan 2008 Posts: 910 |

Posted: Wed Nov 18, 2009 8:32 pm Post subject: |

I'd prefer to be compatible with how every other ID game that came after Quake did it and stick them in "/music", to be honest. The DP method seems a little weird, as it makes no top-level distinction between in-game sounds and music, and secondly they're not really CD tracks. Kinda reminiscent of the way Tenebrae used "/override" to store textures in. It would be great if LH could provide insight into his reasoning for choosing that particular location to use.
The best approach however is to let the music location be user-configurable via cvar, falling back to testing both "/sound/cdtracks" and "/music" if not found. No reason to artifically limit your engine's capability, is there? Especially when a way to be more broadly compatible is so obvious.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Wed Nov 18, 2009 9:05 pm Post subject: |

sure, but if its a choice of one or the other, it is better to be compatible with another quake engine than it is a q2 or q3 engine, as more people are going to expect mods to work using that notation. music is sound, and thus it must go in the sounds directory.
Well... that and the fact that all precached sounds in quake are prefixed with 'sound/' before opening.
And they _are_ cd tracks if they're played with the cd command. They use svc_cdtrack protocols, etc. The DP extension that specifies it is named something containing FAKE_TRACKS or something, so...
Just a small note, this won't work for playing sounds in paks. You would need to implement lots of extra COM objects for this, but it would be possible, somehow.
_________________
What's a signature?

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Nov 18, 2009 10:27 pm Post subject: |

Spirit wrote:
Yay, Baker's tutorials are back! Everyone implementing this or similar functionality, PLEASE be compatible to how Darkplaces has done this for years for Vorbis and WAVE. "id1/sound/cdtracks/track002.ogg"

For about 5 minutes, I was thinking exactly what you were.
And then I had to Google for what I have posted at least 5 times as to the folder name. Was it "sound/cdtrack" or "sounds/cdtracks" or "sounds/cdtracks/cdtrack01.mp3" or what?
And then I remembered that DarkPlaces doesn't play MP3s so this isn't going to interfere with an existing ogg.
I like the gamedir/music folder because I can remember it, it is modder friendly and makes sense.
Why should the cdtracks be in the sound/cdtracks folder?
They aren't game sounds so they shouldn't be in the sound folder.
And cdtracks is wrong because I bet in 3 years there won't be any games with cd sound tracks because CDs are obsolete. That would be like sticking the music in sounds/cassettetracks or sounds/8track folder.

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Nov 18, 2009 11:56 pm Post subject: |

Random thoughts related to the discussion: 1. I'd like to see the music indicated in worldspawn be like "mysong" instead of a silly number. I think it would be cool for modders to be able to explicitly name the song playing instead of some number. Even worse that this number needs x-1, which is confusing.
2. I haven't looked at the protocol to see if #1 interferes with it or is even indicated (aside from the DP_ extension), but I know any half-way serious player isn't going to use a cd track in multiplayer because it interferes with hearing and consumes resources. But coop maybe.
Case in point, with Travail (download Travail mp3 pack I made that works with the Fitz 0.85 in post #1), why should the names of the sounds be renamed from, say, "In Memoria" or what not to the bland "track03.mp3".
It's like doing the scrubs all the humanity out of authorship of music. Much better for a map author to put "memoria" in worldspawn and have it play that mp3. I mean, if you are going to rethink whether a standard is good, rethink it all.
(No idea what would happen to an existing engine if it encountered a non-numeric "cd track" name. Maybe use different worldspawn "name" like "music".)

Back to top |

mh Joined: 12 Jan 2008 Posts: 910 |

Posted: Thu Nov 19, 2009 12:31 am Post subject: |

Good points. I addressed this in DirectQ by allowing the MP3s to retain their original names and instead using the number as an index in the file listing, but your idea of being able to specify a name in worldspawn is something I never really thought of. In theory it could be OK, as the worldspawn key can be read and reacted to separately at load time. In practice the messy part is that the Quake architecture specifies a CD tracknumber which is only accessed by the server and sent to the client as an svc_cdtrack message. This would also mean that the server (including QC) would have the ability to specify an arbitrary change of CD track at any point during gameplay.
Now, the interesting thing is that the Red Book spec only allows up to 99 tracks on a CD, and the message is sent as a byte. It's hacky, but given that this is a clear case where a certain type of message that would otherwise never be used exists, one could interpret a value above 100 as indicating that the message is to be followed by a string; possibly just a track name but more usefully a command that could play, pause, stop, change volume, etc.
I wouldn't be comfortable implementing this without a protocol or QC extension, but that's where we're getting into other territories (and where my proposed extensible protocol would be a Good Thing, rather than having to invent complete new protocols for every additional thing, no matter how minor).
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

mh Joined: 12 Jan 2008 Posts: 910 |

Posted: Thu Nov 19, 2009 1:03 am Post subject: |

Spike wrote:
sure, but if its a choice of one or the other, it is better to be compatible with another quake engine than it is a q2 or q3 engine, as more people are going to expect mods to work using that notation. music is sound, and thus it must go in the sounds directory.
Well... that and the fact that all precached sounds in quake are prefixed with 'sound/' before opening.
And they _are_ cd tracks if they're played with the cd command. They use svc_cdtrack protocols, etc. The DP extension that specifies it is named something containing FAKE_TRACKS or something, so...

It depends really. I'm going by the notion that people who are used to playing Q3A and the like, and coming to Q1 from there, would be more inclined to use a "music" directory; but there are arguments on both sides.
Fortunately it's stupidly easy to support both. Just check in one and if it doesn't exist check in the other. Debate over and everybody happy.

Spike wrote:
Just a small note, this won't work for playing sounds in paks. You would need to implement lots of extra COM objects for this, but it would be possible, somehow.

You could extract to a temp file; there are Windows API calls (and since we're using DirectShow we're already committed to Windows so portability concerns don't apply) that can create a temp file (optionally holding it in system memory but making it accessible via the file I/O routines) and then delete it when no processes are left holding it open.
I've 99% of what's required to support this for MP3s implemented in DirectQ already (I use it for loading from PK3 or Zip) so I could probably get the rest up and running in half an hour or so. Making it more generally usable in the context of this tutorial would take only slightly longer.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Nov 19, 2009 2:28 am Post subject: |

Baker wrote:
And then I remembered that DarkPlaces doesn't play MP3s so this isn't going to interfere with an existing ogg.

However, this extension can play wav, and potentially ogg, even if DP cannot play mp3s.
Oo oo, what happens if you point it to a video instead of a sound file?
mh: doesn't need a protocol extension in the regular way. just use stuffcmds. music tracks don't really change often enough that it warrents incompatibilities. imho. doesn't need qc extensions then either. just some stuffcmd calls (interesting note: prefix extension stuffcmds with // and you don't get nasty messages in clients that don't support it, but you do have to explicitly recognise it then).
Also, what does track -1 mean? (>100 might be bad)
_________________
What's a signature?

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Thu Nov 19, 2009 2:31 am Post subject: |

Spike wrote:
Also, what does track -1 mean? (>100 might be bad)

I haven't used a CD with Quake in years but isn't indicating track 01 in the map asking it to play track00?
That's what I mean by -1.
Someone correct me if I am wrong.

Back to top |

mh Joined: 12 Jan 2008 Posts: 910 |

Posted: Thu Nov 19, 2009 10:14 am Post subject: |

Tracks < 1 or > the number of tracks on the CD are just ignored; Quake will do a Con_DPrintf saying "bad track number" and not even bother playing. As the track is read as an unsigned char, -1 will equate to 255. Anyway, Spike is right about a stuffcmd being all that's needed. It's a shame that mods don't do it, as it would be great to see some imaginative use of dramatic music at different points during the game.
_________________
DirectQ Engine - New release 1.8.666a, 9th August 2010
MHQuake Blog (General)
Direct3D 8 Quake Engines

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Thu Nov 19, 2009 12:10 pm Post subject: |

mh wrote:
It's a shame that mods don't do it, as it would be great to see some imaginative use of dramatic music at different points during the game.

That would be. Like the final battle when the door closes behind you.
Great point.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Nov 19, 2009 12:33 pm Post subject: |

the down side of actual CD tracks is that quake's sound system is single threaded, that is, start playing a CD track and your rendering thread stalls for a second or two while the CD drive spins up and starts the relevent track. quakeworld engines default to CD audio disabled. :)
fake tracks don't have the same issue, assuming they are streamed and not decoded in one lump, at least. And using window's COM stuff will generally put it in a different thread anyway.
_________________
What's a signature?

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Programming Tutorials | All times are GMT Goto page 1, 2 Next
Page 1 of 2

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group