2004-08-23 00:15:46 +00:00
/*
Copyright ( C ) 1996 - 1997 Id Software , Inc .
This program is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation ; either version 2
of the License , or ( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
2011-05-19 13:34:07 +00:00
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
2004-08-23 00:15:46 +00:00
See the included ( GNU . txt ) GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*/
// Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
// rights reserved.
# include "quakedef.h"
# include "winquake.h"
2017-02-21 20:22:07 +00:00
# ifndef HAVE_CDPLAYER
//nothing
# elif defined(WINRT)
2014-03-30 08:55:06 +00:00
# include "cd_null.c"
# else
2011-06-04 12:56:45 +00:00
# if defined(_MSC_VER) && (_MSC_VER < 1300)
2006-04-02 03:47:06 +00:00
# define DWORD_PTR DWORD
# endif
2004-08-23 00:15:46 +00:00
extern HWND mainwindow ;
extern cvar_t bgmvolume ;
2013-10-08 14:28:11 +00:00
static qboolean initialized ;
static qboolean initializefailed ;
static DWORD resumeend ;
static qboolean pollneeded ; //workaround for windows vista/7 bug, where notification simply does not work for end of tracks.
2004-08-23 00:15:46 +00:00
2012-11-29 13:37:48 +00:00
static UINT wDeviceID ;
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
int CDAudio_GetAudioDiskInfo ( void )
2004-08-23 00:15:46 +00:00
{
DWORD dwReturn ;
2013-10-08 14:28:11 +00:00
static MCI_STATUS_PARMS mciStatusParms ;
if ( ! CDAudio_Startup ( ) )
return - 1 ;
2004-08-23 00:15:46 +00:00
2007-06-20 00:02:54 +00:00
if ( ! initialized )
return - 1 ;
2004-08-23 00:15:46 +00:00
mciStatusParms . dwItem = MCI_STATUS_READY ;
2013-10-08 14:28:11 +00:00
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2017-02-21 20:22:07 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_STATUS_ITEM | MCI_WAIT , ( DWORD_PTR ) ( LPVOID ) & mciStatusParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
Con_DPrintf ( " CDAudio: drive ready test - get status failed \n " ) ;
return - 1 ;
}
if ( ! mciStatusParms . dwReturn )
{
Con_DPrintf ( " CDAudio: drive not ready \n " ) ;
return - 1 ;
}
mciStatusParms . dwItem = MCI_STATUS_NUMBER_OF_TRACKS ;
2013-10-08 14:28:11 +00:00
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2017-02-21 20:22:07 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_STATUS_ITEM | MCI_WAIT , ( DWORD_PTR ) ( LPVOID ) & mciStatusParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
Con_DPrintf ( " CDAudio: get tracks - status failed \n " ) ;
return - 1 ;
}
if ( mciStatusParms . dwReturn < 1 )
{
Con_DPrintf ( " CDAudio: no music tracks \n " ) ;
return - 1 ;
}
2013-10-08 14:28:11 +00:00
return mciStatusParms . dwReturn ;
2004-08-23 00:15:46 +00:00
}
2013-10-08 14:28:11 +00:00
qboolean CDAudio_Startup ( void )
2012-11-29 13:37:48 +00:00
{
DWORD dwReturn ;
2013-11-21 23:02:28 +00:00
static MCI_OPEN_PARMSA mciOpenParms ;
2017-02-21 20:22:07 +00:00
static MCI_SET_PARMS mciSetParms ;
2012-11-29 13:37:48 +00:00
if ( initializefailed )
return false ;
if ( initialized )
return true ;
mciOpenParms . lpstrDeviceType = " cdaudio " ;
2013-10-08 14:28:11 +00:00
mciOpenParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2012-11-29 13:37:48 +00:00
dwReturn = mciSendCommand ( 0 , MCI_OPEN , MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE , ( DWORD_PTR ) ( LPVOID ) & mciOpenParms ) ;
if ( dwReturn )
{
Con_Printf ( " CDAudio_Init: MCI_OPEN failed (%i) \n " , ( int ) dwReturn ) ;
initializefailed = true ;
return 0 ;
}
wDeviceID = mciOpenParms . wDeviceID ;
2017-02-21 20:22:07 +00:00
// Set the time format to frames. vista+ simply cannot come with converting to/from seconds, or something (notifies don't work, status stays playing, position stops updating at about 3 frames from the end of the track).
mciSetParms . dwTimeFormat = MCI_FORMAT_MSF ;
2013-10-08 14:28:11 +00:00
mciSetParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2012-11-29 13:37:48 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_SET , MCI_SET_TIME_FORMAT , ( DWORD_PTR ) ( LPVOID ) & mciSetParms ) ;
2017-02-21 20:22:07 +00:00
if ( dwReturn )
{
2012-11-29 13:37:48 +00:00
Con_Printf ( " MCI_SET_TIME_FORMAT failed (%i) \n " , ( int ) dwReturn ) ;
2017-02-21 20:22:07 +00:00
mciSendCommand ( wDeviceID , MCI_CLOSE , 0 , ( DWORD_PTR ) NULL ) ;
2012-11-29 13:37:48 +00:00
initializefailed = true ;
return 0 ;
2017-02-21 20:22:07 +00:00
}
2012-11-29 13:37:48 +00:00
initialized = true ;
2013-10-08 14:28:11 +00:00
if ( CDAudio_GetAudioDiskInfo ( ) < = 0 )
2012-11-29 13:37:48 +00:00
{
Con_Printf ( " CDAudio_Init: No CD in player. \n " ) ;
}
return true ;
}
void CDAudio_Shutdown ( void )
{
if ( initialized )
{
CDAudio_Stop ( ) ;
if ( mciSendCommand ( wDeviceID , MCI_CLOSE , MCI_WAIT , ( DWORD_PTR ) NULL ) )
Con_DPrintf ( " CDAudio_Shutdown: MCI_CLOSE failed \n " ) ;
}
initialized = false ;
}
2013-10-08 14:28:11 +00:00
void CDAudio_Eject ( void )
2012-11-29 13:37:48 +00:00
{
DWORD dwReturn ;
dwReturn = mciSendCommand ( wDeviceID , MCI_SET , MCI_SET_DOOR_OPEN , ( DWORD_PTR ) NULL ) ;
2017-02-21 20:22:07 +00:00
if ( dwReturn )
2012-11-29 13:37:48 +00:00
Con_DPrintf ( " MCI_SET_DOOR_OPEN failed (%i) \n " , ( int ) dwReturn ) ;
}
2013-10-08 14:28:11 +00:00
void CDAudio_CloseDoor ( void )
2012-11-29 13:37:48 +00:00
{
DWORD dwReturn ;
dwReturn = mciSendCommand ( wDeviceID , MCI_SET , MCI_SET_DOOR_CLOSED , ( DWORD_PTR ) NULL ) ;
2017-02-21 20:22:07 +00:00
if ( dwReturn )
2012-11-29 13:37:48 +00:00
Con_DPrintf ( " MCI_SET_DOOR_CLOSED failed (%i) \n " , ( int ) dwReturn ) ;
}
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
//try to add time values sensibly because:
//a) microsoft api SUCKS and does not directly support frames.
//b) microsoft buggily stops 3 frames short of the end of the track if we use tmsf...
//c) frames added together will break things
//d) we can subtract an offset so we can actually detect when its reached the end of a track
//e) we need to do frames so we don't break if some track is exactly a multiple of a second long
DWORD MSFToFrames ( DWORD base )
2004-08-23 00:15:46 +00:00
{
2013-10-08 14:28:11 +00:00
int m = MCI_MSF_MINUTE ( base ) ;
int s = MCI_MSF_SECOND ( base ) ;
int f = MCI_MSF_FRAME ( base ) ;
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
s + = m * 60 ;
f + = 75 * s ; //75 frames per second.
return f ;
}
DWORD FramesToMSF ( DWORD in )
{
DWORD m , s , f ;
f = in % 75 ;
in / = 75 ;
s = in % 60 ;
in / = 60 ;
m = in ;
return MCI_MAKE_MSF ( m , s , f ) ;
}
2007-06-20 00:02:54 +00:00
2013-10-08 14:28:11 +00:00
void CDAudio_Play ( int track )
{
DWORD dwReturn ;
2017-02-21 20:22:07 +00:00
static MCI_PLAY_PARMS mciPlayParms ;
2013-10-08 14:28:11 +00:00
static MCI_STATUS_PARMS mciStatusParms ;
DWORD trackstartposition ;
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
if ( track < 1 )
2004-08-23 00:15:46 +00:00
{
Con_DPrintf ( " CDAudio: Bad track number %u. \n " , track ) ;
return ;
}
// don't try to play a non-audio track
mciStatusParms . dwItem = MCI_CDA_STATUS_TYPE_TRACK ;
mciStatusParms . dwTrack = track ;
2013-10-08 14:28:11 +00:00
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2017-02-21 20:22:07 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT , ( DWORD_PTR ) ( LPVOID ) & mciStatusParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
2011-05-19 13:34:07 +00:00
Con_DPrintf ( " MCI_STATUS failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
return ;
}
if ( mciStatusParms . dwReturn ! = MCI_CDA_TRACK_AUDIO )
{
Con_Printf ( " CDAudio: track %i is not audio \n " , track ) ;
return ;
}
2013-10-08 14:28:11 +00:00
// get the start of the track to be played
mciStatusParms . dwItem = MCI_STATUS_POSITION ;
2004-08-23 00:15:46 +00:00
mciStatusParms . dwTrack = track ;
2013-10-08 14:28:11 +00:00
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2017-02-21 20:22:07 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT , ( DWORD_PTR ) ( LPVOID ) & mciStatusParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
2011-05-19 13:34:07 +00:00
Con_DPrintf ( " MCI_STATUS failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2013-10-08 14:28:11 +00:00
trackstartposition = mciStatusParms . dwReturn ;
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
// get the length of the track to be played
mciStatusParms . dwItem = MCI_STATUS_LENGTH ;
mciStatusParms . dwTrack = track ;
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
2017-02-21 20:22:07 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_STATUS_ITEM | MCI_TRACK | MCI_WAIT , ( DWORD_PTR ) ( LPVOID ) & mciStatusParms ) ;
2013-10-08 14:28:11 +00:00
if ( dwReturn )
2004-08-23 00:15:46 +00:00
{
2013-10-08 14:28:11 +00:00
Con_DPrintf ( " MCI_STATUS failed (%i) \n " , ( int ) dwReturn ) ;
return ;
2004-08-23 00:15:46 +00:00
}
2013-10-08 14:28:11 +00:00
//set up to play from start to start+length
2017-02-21 20:22:07 +00:00
mciPlayParms . dwFrom = trackstartposition ;
2013-10-08 14:28:11 +00:00
mciPlayParms . dwTo = resumeend = FramesToMSF ( MSFToFrames ( trackstartposition ) + MSFToFrames ( mciStatusParms . dwReturn ) - 8 ) ; //-8 to avoid microsoft's potential fuck ups
2017-02-21 20:22:07 +00:00
mciPlayParms . dwCallback = ( DWORD_PTR ) mainwindow ;
dwReturn = mciSendCommand ( wDeviceID , MCI_PLAY , MCI_NOTIFY | MCI_FROM | MCI_TO , ( DWORD_PTR ) ( LPVOID ) & mciPlayParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
2011-05-19 13:34:07 +00:00
Con_DPrintf ( " CDAudio: MCI_PLAY failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2013-10-08 14:28:11 +00:00
pollneeded = true ;
2004-08-23 00:15:46 +00:00
2006-05-07 20:57:30 +00:00
if ( ! bgmvolume . value )
2004-08-23 00:15:46 +00:00
CDAudio_Pause ( ) ;
return ;
}
void CDAudio_Stop ( void )
{
DWORD dwReturn ;
2013-10-08 14:28:11 +00:00
pollneeded = false ;
2004-08-23 00:15:46 +00:00
2006-04-02 03:47:06 +00:00
dwReturn = mciSendCommand ( wDeviceID , MCI_STOP , 0 , ( DWORD_PTR ) NULL ) ;
2017-02-21 20:22:07 +00:00
if ( dwReturn )
2015-06-20 14:22:06 +00:00
Con_DPrintf ( " MCI_STOP failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
}
void CDAudio_Pause ( void )
{
DWORD dwReturn ;
2013-10-08 14:28:11 +00:00
static MCI_GENERIC_PARMS mciGenericParms ;
2004-08-23 00:15:46 +00:00
2013-11-21 23:02:28 +00:00
if ( ! pollneeded )
return ;
2006-04-02 03:47:06 +00:00
mciGenericParms . dwCallback = ( DWORD_PTR ) mainwindow ;
dwReturn = mciSendCommand ( wDeviceID , MCI_PAUSE , 0 , ( DWORD_PTR ) ( LPVOID ) & mciGenericParms ) ;
2017-02-21 20:22:07 +00:00
if ( dwReturn )
2015-06-20 14:22:06 +00:00
Con_DPrintf ( " MCI_PAUSE failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
2013-10-08 14:28:11 +00:00
pollneeded = false ;
2004-08-23 00:15:46 +00:00
}
void CDAudio_Resume ( void )
{
DWORD dwReturn ;
2017-02-21 20:22:07 +00:00
static MCI_PLAY_PARMS mciPlayParms ;
2005-04-16 16:21:27 +00:00
2006-05-07 20:57:30 +00:00
if ( ! bgmvolume . value )
2005-04-16 16:21:27 +00:00
return ;
2011-05-19 13:34:07 +00:00
2017-02-21 20:22:07 +00:00
mciPlayParms . dwFrom = resumeend ;
mciPlayParms . dwTo = resumeend ;
mciPlayParms . dwCallback = ( DWORD_PTR ) mainwindow ;
dwReturn = mciSendCommand ( wDeviceID , MCI_PLAY , MCI_TO | MCI_NOTIFY , ( DWORD_PTR ) ( LPVOID ) & mciPlayParms ) ;
2004-08-23 00:15:46 +00:00
if ( dwReturn )
{
2011-05-19 13:34:07 +00:00
Con_DPrintf ( " CDAudio: MCI_PLAY failed (%i) \n " , ( int ) dwReturn ) ;
2004-08-23 00:15:46 +00:00
return ;
}
2007-06-20 00:02:54 +00:00
2013-10-08 14:28:11 +00:00
pollneeded = true ;
2004-08-23 00:15:46 +00:00
}
LONG CDAudio_MessageHandler ( HWND hWnd , UINT uMsg , WPARAM wParam , LPARAM lParam )
{
if ( lParam ! = wDeviceID )
return 1 ;
switch ( wParam )
{
case MCI_NOTIFY_SUCCESSFUL :
2013-10-08 14:28:11 +00:00
pollneeded = false ;
Media_EndedTrack ( ) ;
2004-08-23 00:15:46 +00:00
break ;
case MCI_NOTIFY_ABORTED :
case MCI_NOTIFY_SUPERSEDED :
break ;
case MCI_NOTIFY_FAILURE :
Con_DPrintf ( " MCI_NOTIFY_FAILURE \n " ) ;
CDAudio_Stop ( ) ;
2013-10-08 14:28:11 +00:00
CDAudio_Shutdown ( ) ;
// Media_EndedTrack();
2004-08-23 00:15:46 +00:00
break ;
default :
2011-06-04 12:56:45 +00:00
Con_DPrintf ( " Unexpected MM_MCINOTIFY type (%i) \n " , ( int ) wParam ) ;
2004-08-23 00:15:46 +00:00
return 1 ;
}
return 0 ;
}
2006-05-07 20:57:30 +00:00
void CDAudio_Update ( void )
{
2013-10-08 14:28:11 +00:00
//workaround for vista bug where MCI_NOTIFY does not work to signal the end of the track.
if ( pollneeded )
{
MCI_STATUS_PARMS mciStatusParms ;
mciStatusParms . dwCallback = ( DWORD_PTR ) mainwindow ;
mciStatusParms . dwItem = MCI_STATUS_POSITION ;
mciStatusParms . dwReturn = resumeend ;
mciStatusParms . dwTrack = 0 ;
if ( 0 = = mciSendCommand ( wDeviceID , MCI_STATUS , MCI_WAIT | MCI_STATUS_ITEM , ( DWORD_PTR ) & mciStatusParms ) )
{
unsigned int c , f ;
int cm = MCI_MSF_MINUTE ( mciStatusParms . dwReturn ) ;
int cs = MCI_MSF_SECOND ( mciStatusParms . dwReturn ) ;
int cf = MCI_MSF_FRAME ( mciStatusParms . dwReturn ) ;
int fm = MCI_MSF_MINUTE ( resumeend ) ;
int fs = MCI_MSF_SECOND ( resumeend ) ;
int ff = MCI_MSF_FRAME ( resumeend ) ;
c = cf | ( cs < < 8 ) | ( cm < < 16 ) ;
f = ff | ( fs < < 8 ) | ( fm < < 16 ) ;
if ( c > = f )
{
pollneeded = false ;
Media_EndedTrack ( ) ;
}
}
}
2004-08-23 00:15:46 +00:00
}
2013-10-08 14:28:11 +00:00
void CDAudio_Init ( void )
2004-08-23 00:15:46 +00:00
{
}
2014-03-30 08:55:06 +00:00
# endif