sigmaplayer/src/cdda.cpp

554 lines
12 KiB
C++

//////////////////////////////////////////////////////////////////////////
/**
* SigmaPlayer source project - CDDA (CD-Audio) player impl.
* \file cdda.cpp
* \author bombur
* \version 0.1
* \date 2.08.2004
*
* 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
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* 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, USA.
*/
//////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <libsp/sp_misc.h>
#include <libsp/sp_msg.h>
#include <libsp/sp_fip.h>
#include <libsp/sp_khwl.h>
#include <libsp/sp_mpeg.h>
#include <libsp/sp_cdrom.h>
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include "script.h"
#include "cdda.h"
#include "media.h"
extern int cdrom_handle;
static Cdda *cdda = NULL;
static bool cdda_msg = true;
#define MSG if (cdda_msg) msg
bool cdda_read_track(CddaTrack *track, int i)
{
struct cdrom_tocentry e;
memset(&e, 0, sizeof(struct cdrom_tocentry));
e.cdte_track = (BYTE)i;
e.cdte_format = CDROM_MSF;
if (ioctl(cdrom_handle, CDROMREADTOCENTRY, &e) >= 0)
{
if ((e.cdte_ctrl & CDROM_DATA_TRACK) == 0)
{
if (track != NULL)
{
track->idx = i;
track->frame_idx = e.cdte_addr.msf.minute * CD_SECS * CD_FRAMES
+ e.cdte_addr.msf.second * CD_FRAMES + e.cdte_addr.msf.frame;
}
return true;
}
}
return false;
}
Cdda *cdda_open()
{
int i;
if (cdda != NULL)
return cdda;
#ifdef WIN32
// only for real device!
if (cdrom_handle == 2)
{
MCI_OPEN_PARMS mciOpen;
MCI_STATUS_PARMS mciParams;
MCI_SET_PARMS mciSet;
mciOpen.lpstrDeviceType = (LPCSTR)MCI_DEVTYPE_CD_AUDIO;
mciSendCommand(0, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)&mciOpen);
mciSet.dwTimeFormat = MCI_FORMAT_MSF;
mciSendCommand(mciOpen.wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)&mciSet);
mciParams.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)&mciParams);
int numtr = mciParams.dwReturn;
cdda = new Cdda();
cdda->tracks.Reserve(numtr);
for (i = 0; i < numtr; i++)
{
mciParams.dwItem = MCI_CDA_STATUS_TYPE_TRACK;
mciParams.dwTrack = i + 1;
mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)&mciParams);
if (mciParams.dwReturn == MCI_CDA_TRACK_AUDIO)
{
CddaTrack track;
mciParams.dwItem = MCI_STATUS_LENGTH;
mciParams.dwTrack = i + 1;
mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)&mciParams);
track.length = MCI_MSF_MINUTE(mciParams.dwReturn) * CD_SECS * CD_FRAMES +
MCI_MSF_SECOND(mciParams.dwReturn) * CD_FRAMES +
MCI_MSF_FRAME(mciParams.dwReturn);
mciParams.dwItem = MCI_STATUS_POSITION;
mciParams.dwTrack = i + 1;
mciSendCommand(mciOpen.wDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)&mciParams);
track.frame_idx = MCI_MSF_MINUTE(mciParams.dwReturn) * CD_SECS * CD_FRAMES +
MCI_MSF_SECOND(mciParams.dwReturn) * CD_FRAMES +
MCI_MSF_FRAME(mciParams.dwReturn);
cdda->tracks.Add(track);
}
}
mciSendCommand(mciOpen.wDeviceID, MCI_CLOSE, 0, 0);
return cdda;
} else
{
return NULL;
}
#else
if (cdrom_handle < 0)
{
msg_error("CDDA: Cannot find opened CD-ROM handle.\n");
return NULL;
}
struct cdrom_tochdr toc;
if (ioctl(cdrom_handle, CDROMREADTOCHDR, &toc) < 0)
{
return NULL;
}
cdda = new Cdda();
cdda->track1 = toc.cdth_trk0;
cdda->track2 = toc.cdth_trk1;
cdda->tracks.Reserve(cdda->track2 - cdda->track1 + 1);
int track_idx = 0;
cdda->total_length = 0;
for (i = cdda->track1; i <= cdda->track2; i++)
{
CddaTrack track;
if (cdda_read_track(&track, i))
{
track_idx = cdda->tracks.Add(track);
if (track_idx > 0)
cdda->tracks[track_idx - 1].length = track.frame_idx - cdda->total_length;
cdda->total_length = track.frame_idx;
}
}
CddaTrack track_leadout;
if (cdda_read_track(&track_leadout, CDROM_LEADOUT))
{
cdda->tracks[track_idx].length = track_leadout.frame_idx - cdda->total_length;
cdda->total_length = track_leadout.frame_idx;
}
return cdda;
#endif
}
BOOL cdda_close()
{
cdda_stop();
SPSafeDelete(cdda);
return TRUE;
}
int cdda_play(char *path)
{
if (cdrom_handle < 0)
return -1;
if (path == NULL)
{
fip_write_special(FIP_SPECIAL_PLAY, 1);
fip_write_special(FIP_SPECIAL_PAUSE, 0);
mpeg_setspeed(MPEG_SPEED_NORMAL);
return 0;
}
if (strncasecmp(path, "/cdrom/", 7) != 0)
return -1;
int ret = media_open(path, MEDIA_TYPE_CDDA);
if (ret < 0 || cdda == NULL)
{
msg_error("CDDA: media open FAILED.\n");
return ret;
}
MSG("CDDA: start...\n");
if (strlen(path) > 7)
{
cdda->cur_trackid = atoi(path + 7) - 1;
} else
cdda->cur_trackid = 0;
if (cdda->cur_trackid < 0 || cdda->cur_trackid >= cdda->tracks.GetN())
return -1;
cdda->cur = cdda->tracks[cdda->cur_trackid].frame_idx;
MSG("CDDA: * Play Track = %d (pos = %d).\n", cdda->cur_trackid+1, cdda->cur);
cdda->feof = false;
mpeg_init(MPEG_1, TRUE, FALSE, FALSE);
mpeg_setbuffer(MPEG_BUFFER_1, BUF_BASE, 8, 30576);
MSG("CDDA: Setting default audio params.\n");
MpegAudioPacketInfo defaudioparams;
defaudioparams.type = eAudioFormat_PCM;
defaudioparams.samplerate = 44100;
defaudioparams.numberofbitspersample = 16;
defaudioparams.numberofchannels = 2;
defaudioparams.fromstream = 0;
mpeg_setaudioparams(&defaudioparams);
// write dvd-specific FIP stuff...
fip_write_special(FIP_SPECIAL_CD, 1);
const char *digits = " 00000";
fip_write_string(digits);
fip_write_special(FIP_SPECIAL_COLON1, 1);
fip_write_special(FIP_SPECIAL_COLON2, 1);
fip_write_special(FIP_SPECIAL_PLAY, 1);
fip_write_special(FIP_SPECIAL_PAUSE, 0);
script_audio_info_callback("");
script_video_info_callback("");
cdda->playing = true;
mpeg_start();
return 0;
}
int cdda_get_length(char *path)
{
if (path == NULL)
return 0;
if (strncasecmp(path, "/cdrom/", 7) != 0)
return 0;
if (cdda_open() == NULL)
return 0;
int trackid = atoi(path + 7) - 1;
if (trackid < 0 || trackid >= cdda->tracks.GetN())
return 0;
return cdda->tracks[trackid].length / CD_FRAMES;
}
int cdda_get_numtracks()
{
if (cdda_open() == NULL)
return 0;
return cdda->tracks.GetN();
}
BOOL cdda_feof()
{
if (cdda == NULL || cdda->feof)
return TRUE;
return FALSE;
}
int cdda_read(BYTE *buf, int numblocks)
{
if (cdrom_handle < 0 || cdda == NULL)
{
return 0;
}
if (cdda->feof)
{
return 0;
}
if (cdda->cur < 0 || cdda->cur_trackid < 0)
{
return 0;
}
int maxbl = cdda->tracks[cdda->cur_trackid].frame_idx + cdda->tracks[cdda->cur_trackid].length - 1;
//MSG("CDDA: %5d / %5d [%d]\n", cdda->cur, maxbl, cdda->feof);
if (cdda->cur + numblocks > maxbl)
{
numblocks = maxbl - cdda->cur;
cdda->feof = true;
}
if (numblocks < 1)
return 0;
cdrom_read_audio rda;
rda.addr.msf.minute = (BYTE)(cdda->cur / CD_FRAMES / CD_SECS);
rda.addr.msf.second = (BYTE)((cdda->cur / CD_FRAMES) % CD_SECS);
rda.addr.msf.frame = (BYTE)(cdda->cur % CD_FRAMES);
rda.addr_format = CDROM_MSF;
rda.nframes = numblocks;
rda.buf = buf;
if (ioctl(cdrom_handle, CDROMREADAUDIO, &rda) < 0)
return 0;
// LPCM requires big-endian byte order
mpeg_PCM_to_LPCM(buf, numblocks * MPEG_PACKET_LENGTH);
cdda->cur += numblocks;
return numblocks;
}
void cdda_update_info()
{
if (cdda == NULL)
return;
static int old_secs = 0;
KHWL_TIME_TYPE displ;
displ.pts = 0;
displ.timeres = 90000;
if (mpeg_is_displayed())
khwl_getproperty(KHWL_TIME_SET, etimSystemTimeClock, sizeof(displ), &displ);
// displ.pts -= pts_base;
if (cdda->old_trackid != cdda->cur_trackid || (LONGLONG)displ.pts != cdda->saved_pts)
{
if (cdda->cur_trackid >= 0 && (LONGLONG)displ.pts >= 0)
{
if (cdda->cur_trackid != cdda->old_trackid)
{
script_totaltime_callback(cdda->tracks[cdda->cur_trackid].length / CD_FRAMES);
script_cdda_track_callback(cdda->cur_trackid + 1);
old_secs = -1;
}
cdda->old_trackid = cdda->cur_trackid;
cdda->saved_pts = displ.pts;
char fip_out[10];
int secs = (int)(displ.pts / 90000);
int ccid = cdda->cur_trackid + 1;
if (ccid < 0)
ccid = 0;
if (ccid > 99)
ccid = 99;
if (secs < 0)
secs = 0;
if (secs >= 10*3600)
secs = 10*3600-1;
if (secs != old_secs)
{
script_time_callback(secs);
fip_out[0] = (char)((ccid / 10) + '0');
fip_out[1] = (char)((ccid % 10) + '0');
fip_out[2] = (char)((secs/3600) + '0');
int secs3600 = secs%3600;
fip_out[3] = (char)(((secs3600/60)/10) + '0');
fip_out[4] = (char)(((secs3600/60)%10) + '0');
fip_out[5] = (char)(((secs3600%60)/10) + '0');
fip_out[6] = (char)(((secs3600%60)%10) + '0');
fip_out[7] = '\0';
fip_write_string(fip_out);
old_secs = secs;
}
}
}
}
int cdda_player_loop()
{
static int info_cnt = 0;
if (cdrom_handle < 0 || cdda == NULL || cdda->cur < 0)
return 1;
BYTE *buf = NULL;
MEDIA_EVENT event = MEDIA_EVENT_OK;
int len = 0;
int ret = media_get_next_block(&buf, &event, &len);
if (ret > 0 && buf == NULL)
{
msg_error("CDDA: Not initialized. STOP!\n");
return 1;
}
if (ret == -1)
{
msg_error("CDDA: Error getting next block!\n");
return 1;
}
else if (ret == 0) // wait...
{
return 0;
}
if (event == MEDIA_EVENT_STOP)
{
MSG("CDDA: STOP Event triggered!\n");
return 1;
}
BYTE *base = buf;
MpegPacket *packet = NULL;
packet = mpeg_feed_getlast();
if (packet == NULL) // well, it won't really help
return 0;
memset((BYTE *)packet + 4, 0, sizeof(MpegPacket) - 4);
packet->pts = 0;//pts_base;
packet->nframeheaders = 0xffff;
packet->firstaccessunitpointer = 0;
packet->type = 1;
packet->pData = base;
packet->size = MPEG_PACKET_LENGTH;
// increase bufidx
mpeg_setbufidx(MPEG_BUFFER_1, packet);
mpeg_feed(MPEG_FEED_AUDIO);
if (info_cnt++ > 32)
{
info_cnt = 0;
cdda_update_info();
}
return 0;
}
BOOL cdda_pause()
{
fip_write_special(FIP_SPECIAL_PLAY, 0);
fip_write_special(FIP_SPECIAL_PAUSE, 1);
mpeg_setspeed(MPEG_SPEED_PAUSE);
return TRUE;
}
BOOL cdda_stop()
{
if (cdda != NULL)
{
if (cdda->playing)
{
mpeg_deinit();
cdda->playing = false;
}
}
return TRUE;
}
void cdda_setdebug(BOOL ison)
{
cdda_msg = ison == TRUE;
}
BOOL cdda_getdebug()
{
return cdda_msg;
}
BOOL cdda_seek(int seconds, int from_frame)
{
if (cdda != NULL)
{
if (cdda->cur_trackid >= 0 && cdda->cur_trackid < cdda->tracks.GetN())
{
// start of the current track
if (from_frame < 0)
from_frame = cdda->tracks[cdda->cur_trackid].frame_idx;
khwl_stop();
cdda->cur = from_frame;
cdda->cur += seconds * CD_FRAMES;
int offs = cdda->cur - cdda->tracks[cdda->cur_trackid].frame_idx;
if (offs < 0)
{
cdda->cur = cdda->tracks[cdda->cur_trackid].frame_idx;
offs = 0;
}
if (offs >= cdda->tracks[cdda->cur_trackid].length)
cdda->cur = cdda->tracks[cdda->cur_trackid].frame_idx + cdda->tracks[cdda->cur_trackid].length - 1;
mpeg_start();
mpeg_setpts(offs * 90000 / CD_FRAMES);
return TRUE;
}
}
script_error_callback(SCRIPT_ERROR_INVALID);
return FALSE;
}
BOOL cdda_seek_track(int track)
{
int trck = track - 1;
if (cdda == NULL || trck < 0 || trck >= cdda->tracks.GetN())
{
script_error_callback(SCRIPT_ERROR_INVALID);
return FALSE;
}
khwl_stop();
cdda->cur_trackid = trck;
cdda->cur = cdda->tracks[trck].frame_idx;
MSG("CDDA: * Playpos = %d.\n", cdda->cur);
mpeg_start();
mpeg_setpts(0);
return TRUE;
}
void cdda_get_cur(int *track)
{
if (track != NULL && cdda != NULL)
{
*track = cdda->cur_trackid + 1;
}
}
void cdda_forward()
{
if (cdda != NULL)
cdda_seek(20, cdda->cur);
}
void cdda_rewind()
{
if (cdda != NULL)
cdda_seek(-20, cdda->cur);
}