Page 2 of 4
Posted: Thu Nov 19, 2009 12:47 pm
by Baker
Spike wrote: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.
Super annoying. End of map, a second of lag and sometimes you didn't have time to give your parting "comments" to the other players. ("gg", "that guy sucks", "I entered the game late!")
quakeworld engines default to CD audio disabled.

Hehe, ProQuake 4 you can turn the CD music off or no. No, not the volume ... like actually turn the CD off or from off to on. Real-time -nocdaudio command line param.
Not hard, but it was in the interests of eliminating the need for command line parameters which I view as archaic.
Posted: Thu Nov 19, 2009 3:42 pm
by mh
Baker wrote:eliminating the need for command line parameters which I view as archaic.
I'll drink to that!
From reading the QuakeOne forums, I'd guess that maybe 95% of the problems people have with Quake are command-line related. I mean real people who actually just play the game, as opposed to modders, mappers and coders (we can all be guilty of just seeing things from our own perspectives sometimes).
Posted: Thu Nov 19, 2009 4:18 pm
by Spirit
Quoth's "*_command" works well with "cd play x". If you have an engine that supports "cd" for vorbis tracks, it is great fun.
Posted: Thu Nov 19, 2009 5:17 pm
by Baker
mh wrote:From reading QuakeOne forums, I'd guess that maybe 95% of the problems people have with Quake are command-line related.
Hmmm ... neither ProQuake 4 or Qrack need any command line parameters generally speaking. DarkPlaces hasn't needed command line parameters in quite a while (for the most part). FitzQuake barely does. On the Quakeworld side of things, ezQuake doesn't and FTEQW for a long time hasn't needed them.
Within the last year or 2, Rook and I added video mode switching and the ability to switch dinput on or off. Both have gamedir changing, although your engine takes it a step further with being able to toggle the "-hipnotic" type stuff.
In fact, there is quite the general lack of ProQuake questions because it just runs and, in my opinion, has a well designed menu.
QuakeOne has a ton of legacy stuff from 2006 when command line parameters were a severe headache.
Today, if someone asks a command line question, generally they are Steam-purchased GLQuake using player or want to use, say, ProQuake 3.50 because they have always used it or want to use, say, a software renderer but want as many fps as possible so they need the -directdraw command line (or whatever it is).
Posted: Thu Nov 19, 2009 5:27 pm
by mh
Improvement #1: this will modify the code to support
both /music and /sound/cdtracks. The fallback is fully automatic; if the track isn't found in one location it will just try the other.
Replace your TouchMP3 function with this one:
Code: Select all
char *TouchMP3 (char *MP3Name, int verbose)
{
static char MP3File[1024];
char *musicdirs[] =
{
"music",
"sound/cdtracks",
NULL
};
int i;
// slap on a ".mp3" extension if it doesn't have one
COM_DefaultExtension (MP3Name, ".mp3", sizeof(MP3Name));
for (i = 0; ; i++)
{
// didn't find it at all
if (!musicdirs[i])
{
if (verbose > 0)
{
Con_Printf ("couldn't find %s!!!\n", MP3Name);
}
return NULL;
}
// try the current game directory first
va_snprintf ("TouchMP3", MP3File, sizeof(MP3File), "%s/%s/%s", com_gamedir, musicdirs[i], MP3Name);
FILE *f = fopen (MP3File, "rb");
if (!f)
{
// no music in the current game directory so lets try ID1
va_snprintf ("TouchMP3", MP3File, sizeof(MP3File), "id1/%s/%s", musicdirs[i], MP3Name);
f = fopen (MP3File, "rb");
// no music in ID1 either
if (!f) continue;
}
// found it
fclose (f);
break;
}
if (verbose)
{
Con_Printf ("playing %s\n", MP3Name);
}
return MP3File;
}
All we do here is provide a list of locations we're going to check and loop through that list, checking them all until we either find it or run out of options.
Note that which this provides compatibility with the DarkPlaces
directory convention, it doesn't provide compatibility with the DarkPlaces
name convention. To get that you have a number of options, but probably the easiest is to find the
PlayMP3DShow function and replace this line:
Code: Select all
UserMP3DShow (va ("track%02i", mp3num), -1);
with this one:
Code: Select all
UserMP3DShow (va ("track%03i", mp3num), -1);
Darkplaces provides for up to 3 digits for the track number, which is a bit silly as - as I've previously noted - the Red Book CD audio spec only allows for up to 99 tracks on a CD (i.e. 2 digits).
If you're interested in a more general solution that's capable of checking both directores as well as an optional user-specified directory, that genuinely doesn't
care about what the file is called, and that
doesn't remove CD audio functionality, have a look at the DirectQ source code.
I'll hopefully post a semi-port of the relevant parts of that to the interface used by this tutorial sometime over the next few days, which will also hopefully enable loading from PAK files.
MP3 Converted From C++ To C
Posted: Mon Mar 14, 2011 12:54 pm
by Baker
I don't really like having .cpp files living in my project when the rest of them are .c , so I converted this C.
Note: This code is essentially Reckless + MH authored work, I just translated some of the C++ to C by enabling stuffs in the dshow.h header file with a define and then doing a lot of grunt work.
Code: Select all
/*
Ugh, C++
If anybody thinks I'm gonna write a class here though...
This is a drop-in replacement for the existing CD playing stuff. It's built on DirectShow using the DirectX
8.1 SDK - I don't know if it'll work on older versions of DirectX, but I assume as most people using this
will be gamers anyway, they'll already have upgraded beyond 8.1 a long time ago. If you want to recompile
you may need the SDK...
Please don't compile Realm using the Direct X 9 SDK as you'll lose an awful lot of speed if you do (I
gained 50 FPS just by going back to 8.1)
This MP3 interface uses DirectShow for streaming the MP3 from the hard disk. Better performance may be had
by buffering the entire file in memory and playing from that instead... I'm kinda new to Direct X in general
so I don't know how to yet :(
I'm not a C++ head by any means, so if anything looks stupid in here, it probably is.
This could probably be very easily modified to enable Quake to stream audio off the web!!!
*/
#include "quakedef.h"
#include "version.h"
#ifdef BUILD_MP3_VERSION
#include <windows.h>
#include <stdio.h>
#include <objbase.h>
#define COBJMACROS
#include <dshow.h>
extern char com_gamedirshort[MAX_OSPATH];
// this needs to be defined in gl_vidnt.c as well
#define WM_GRAPHNOTIFY WM_USER + 13
static IGraphBuilder *pGraph = NULL;
static IMediaControl *pControl = NULL;
static IMediaEventEx *pEvent = NULL;
static IBasicAudio *pAudio = NULL;
static IMediaSeeking *pSeek = NULL;
static BOOL MP3Enabled = FALSE;
static BOOL COMSTUFFOK = FALSE;
void WaitForFilterState (OAFilterState DesiredState)
{
OAFilterState MP3FS;
HRESULT hr;
while (1)
{
hr = IMediaControl_GetState(pControl, 1, &MP3FS);
if (FAILED (hr)) continue;
if (MP3FS == DesiredState) return;
}
}
char InitMP3DShow (void)
{
// COM is beautiful, intuitive and really easy in VB. This is just clunky and awful.
HRESULT hr = CoInitialize (NULL);
if (FAILED (hr)) {
Con_Printf ("ERROR - Could not initialize COM library");
return 0;
}
if (FAILED (hr)) {
Con_Printf ("ERROR - Could not create the Filter Graph Manager.");
CoUninitialize (); // kill off COM
return 0;
}
COMSTUFFOK = TRUE;
return 1;
}
void KillMP3DShow (void)
{
if (!COMSTUFFOK) return;
// stop anything that's playing
if (MP3Enabled)
{
IMediaEventEx_SetNotifyWindow(pEvent, (OAHWND) NULL, 0, 0);
IMediaControl_Stop(pControl);
IMediaControl_Release(pControl);
IMediaEventEx_Release(pEvent);
IBasicAudio_Release(pAudio);
IMediaEventEx_Release(pSeek);
IGraphBuilder_Release(pGraph);
}
CoUninitialize ();
}
void StopMP3DShow (void)
{
// don't try anything if we couldn't start COM
if (!COMSTUFFOK) return;
// don;t try to stop if we're not even playing!!!
if (!MP3Enabled) return;
// kill it straight away
IMediaEventEx_SetNotifyWindow(pEvent, (OAHWND) NULL, 0, 0);
IMediaControl_Stop(pControl);
WaitForFilterState (State_Stopped);
IMediaControl_Release(pControl);
IMediaEventEx_Release(pEvent);
IBasicAudio_Release(pAudio);
IMediaSeeking_Release(pSeek);
IGraphBuilder_Release(pGraph);
// nothing playing now
MP3Enabled = FALSE;
}
void PawsMP3DShow (int Paused)
{
// don't try anything if we couldn't start COM
if (!COMSTUFFOK) return;
// don;t try to pause if we're not even playing!!!
if (!MP3Enabled) return;
// don't wait for the filter states here
if (Paused)
{
IMediaControl_Run(pControl);
}
else
{
IMediaControl_Pause(pControl);
}
}
void VolmMP3DShow (int Level)
{
// don't try anything if we couldn't start COM
if (!COMSTUFFOK) return;
// don;t try to change volume if we're not even playing!!!
if (!MP3Enabled) return;
// put_Volume uses an exponential decibel-based scale going from -10000 (no sound) to 0 (full volume)
// each 100 represents 1 db. i could do the maths, but this is faster and more maintainable.
switch (Level)
{
case 0:
IBasicAudio_put_Volume(pAudio, -10000);
break;
case 1:
IBasicAudio_put_Volume(pAudio, -2000);
break;
case 2:
IBasicAudio_put_Volume(pAudio, -1400);
break;
case 3:
IBasicAudio_put_Volume(pAudio, -1040);
break;
case 4:
IBasicAudio_put_Volume(pAudio, -800);
break;
case 5:
// half volume = -6.02 db
// i got these figures from GoldWave 5's volume changer...
IBasicAudio_put_Volume(pAudio, -602);
break;
case 6:
IBasicAudio_put_Volume(pAudio, -440);
break;
case 7:
IBasicAudio_put_Volume(pAudio, -310);
break;
case 8:
IBasicAudio_put_Volume(pAudio, -190);
break;
case 9:
IBasicAudio_put_Volume(pAudio, -90);
break;
case 10:
IBasicAudio_put_Volume(pAudio, 0);
break;
}
}
/*
===================
MesgMP3DShow
MP3 Message handler. The only one we're interested in is a stop message.
Everything else is handled within the engine code.
===================
*/
void MesgMP3DShow (int Looping)
{
LONG evCode;
LONG_PTR evParam1, evParam2;
HRESULT hr = S_OK;
// don't try anything if we couldn't start COM
if (!COMSTUFFOK) return;
// don't try anything if we're not even playing!!!
if (!MP3Enabled) return;
// Process all queued events
while (SUCCEEDED (IMediaEventEx_GetEvent (pEvent, &evCode, &evParam1, &evParam2, 0)))
{
// Free memory associated with callback, since we're not using it
hr = IMediaEventEx_FreeEventParams(pEvent, evCode, evParam1, evParam2);
// If this is the end of the clip, reset to beginning
if (evCode == EC_COMPLETE && Looping)
{
LONGLONG pos = 0;
// Reset to first frame of movie
hr = IMediaSeeking_SetPositions(pSeek, &pos, AM_SEEKING_AbsolutePositioning, NULL, AM_SEEKING_NoPositioning);
}
else if (evCode == EC_COMPLETE)
{
// have to explicitly stop it when it completes otherwise the interfaces will remain open
// when the next MP3 is played, and both will play simultaneously...!
StopMP3DShow ();
}
}
return;
}
/*
==================
TouchMP3
quickly confirm that a file exists without having to route it through labyrinthine COM interfaces
this isn't limited to MP3's only, by the way... specify an extension in your "cd play" or "mp3 play"
command and it'll play the file if you have a codec that works with direct show
==================
*/
char *TouchMP3 (char *MP3Name, int verbose)
{
static char MP3File[1024];
#define snprintf _snprintf
// slap on a ".mp3" extension if it doesn't have one
StringModify_DefaultExtension (MP3Name, ".mp3");
// try the current game directory first
snprintf (MP3File, sizeof(MP3File), "%s/music/%s", com_gamedirshort, MP3Name);
{
FILE *f = fopen (MP3File, "rb");
if (!f)
{
// no music in the current game directory so lets try ID1
snprintf (MP3File, sizeof(MP3File), "id1/music/%s", MP3Name);
f = fopen (MP3File, "rb");
// no music in ID1 either
if (!f)
{
if (verbose > 0)
{
Con_Printf ("couldn't find %s!!!\n", MP3Name);
}
return NULL;
}
}
fclose (f);
if (verbose)
{
Con_DPrintf ("playing %s\n", MP3Name);
}
}
return MP3File;
}
/*
==================
StringToLPCWSTR
fucking stupid MS data types
==================
*/
WCHAR *StringToLPCWSTR (char *instr)
{
static WCHAR outstr[1024];
int i;
if (!instr)
{
return '\0';
}
for (i = 0; ; i++)
{
outstr[i] = instr[i];
if (instr[i] == '\0') break;
}
return outstr;
}
/*
=====================
SetupMP3DShow
Initialize COM interfaces and begin playing the MP3
=====================
*/
void SetupMP3DShow (WCHAR *MP3File)
{
if (!MP3File) return;
if (!COMSTUFFOK) return;
// Create the filter graph manager and query for interfaces.
CoCreateInstance (&CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **) &pGraph);
IGraphBuilder_QueryInterface(pGraph, &IID_IMediaControl, (void **) &pControl);
IGraphBuilder_QueryInterface(pGraph, &IID_IMediaEventEx, (void **) &pEvent);
IGraphBuilder_QueryInterface(pGraph, &IID_IBasicAudio, (void **) &pAudio);
IGraphBuilder_QueryInterface(pGraph, &IID_IMediaSeeking, (void **) &pSeek);
IGraphBuilder_RenderFile(pGraph, MP3File, NULL);
// send events through the standard window event handler
IMediaEventEx_SetNotifyWindow(pEvent, (OAHWND) engine.platform.mainwindow, WM_GRAPHNOTIFY, 0);
// Run the graph.
IMediaControl_Run(pControl);
// tell us globally that we can play OK
MP3Enabled = TRUE;
// wait until it reports playing
WaitForFilterState (State_Running);
// examples in the SDK will wait for the event to complete here, but this is totally inappropriate
// for a game engine.
}
void UserMP3DShow (char *mp3name, int verbose)
{
MP3Enabled = FALSE;
{
WCHAR *MP3File = StringToLPCWSTR (TouchMP3 (mp3name, verbose));
SetupMP3DShow (MP3File);
}
}
void PlayMP3DShow (int mp3num)
{
UserMP3DShow (va ("track%02i", mp3num), -1); // Baker: using -1 to denote we SAY what we are playing, but say nothing if no mp3 track is found
}
#endif
Posted: Mon Apr 11, 2011 2:57 pm
by JasonX
While implementing this, i'm getting a lot of unresolved externals.
Such as:
error LNK2019: unresolved external symbol _CDAudio_Play referenced in function _CL_ParseServerMessage
Any ideas?
Posted: Mon Apr 11, 2011 3:01 pm
by JasonX
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _StopMP3DShow referenced in function _CDAudio_Stop
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _PawsMP3DShow referenced in function _CDAudio_Pause
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _KillMP3DShow referenced in function _CDAudio_Shutdown
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _PlayMP3DShow referenced in function _CDAudio_Play
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _VolmMP3DShow referenced in function _CDAudioSetVolume
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _MesgMP3DShow referenced in function _CDAudio_MessageHandler
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _UserMP3DShow referenced in function _CD_f
1>cd_win_mp3.obj : error LNK2019: unresolved external symbol _InitMP3DShow referenced in function _CDAudio_Init
Posted: Mon Apr 11, 2011 3:10 pm
by mh
You're missing either a header file or
function prototypes for these.
Can I seriously recommend spending some time learning some basic C before you go hacking at Quake? It'll make things so much easier for you, you won't be just doing copy-pasta code, you'll start to build up an understanding of how things work, and you'll feel more confident doing stuff.
Anyway, looking back over that code, I'm particularly awestruck by the cutesy 4-letter names I put on each operation. What on earth could I have been
thinking?
Posted: Mon Apr 11, 2011 3:16 pm
by JasonX
A few more!
Code: Select all
1>qmp3.c
1>E:\Program Files\Microsoft SDKs\Windows\v6.0A\\include\strmif.h(21060) : error C2061: syntax error : identifier 'LPDIRECTDRAWSURFACE7'
1>E:\Program Files\Microsoft SDKs\Windows\v6.0A\\include\strmif.h(21068) : error C2059: syntax error : '}'
1>E:\Program Files\Microsoft SDKs\Windows\v6.0A\\include\strmif.h(21131) : error C2143: syntax error : missing ')' before '*'
1>E:\Program Files\Microsoft SDKs\Windows\v6.0A\\include\strmif.h(21131) : error C2081: 'VMRPRESENTATIONINFO' : name in formal parameter list illegal
1>E:\Program Files\Microsoft SDKs\Windows\v6.0A\\include\strmif.h(21131) : error C2143: syntax error : missing ';' before '*'
Posted: Mon Apr 11, 2011 3:18 pm
by JasonX
I don't think it's a problem about learning basic C per se, but learning about damn dependencies and libs in this horrible horrible Visual Studio.
Posted: Mon Apr 11, 2011 4:32 pm
by mh
You're setting things up wrong and then you call it "horrible" when it doesn't work. :?
This code compiles perfectly in Visual Studio. How do I know this? Because I wrote the original using Visual Studio, that's how. From Baker's involvement I can surmise that the Visual Studio versions it is known-good in are 6, 2003, 2008 and 2010 (the latter two including Express versions). From Reckless' involvement I can guess that it's also known-good with his custom mingw. That's pretty damn robust support; there are many other free software projects/libraries/code-snippets that don't even get a quarter of that.
But you gotta set your environment up correctly first.
Posted: Mon Apr 11, 2011 7:50 pm
by revelator
yap

this one suggest a missing dxsdk id say
actually visual studio 2010's standard sdk seems to lack even basic directx support (earlier versions had that integrated to some extent).
http://msdn.microsoft.com/en-us/directx/aa937788
also includes links to other development sdk's if in doubt get it all (i hope you have tons of free space though some of em are pretty monstrous sizewise).
if in visual studio 2010 setting up external dependencies is a little different than older visual studios, in visual studio 2008 feks. you could do it globally 2010 has a pr project way of doing it though.
a little shortcut instead of writing in the full paths in libraries and includes is doing $(DXSDK)\include and $(DXSDK)\lib\x86
i dont use visual studio anymore since development on codeblocks and mingw is in a fine state allready

(and its free ohoho) which unfortunatly also means im babbling of memory so if i remember wrong in some parts please excuse.
Posted: Tue Apr 12, 2011 1:34 am
by Baker
Must download dxsdk8 and follow instructions in post #1.
Posted: Tue Apr 12, 2011 2:35 am
by JasonX
I'm using DirectX 9 SDK, June 2010.