You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdemultimedia/kscd/kcompactdisc.cpp

487 lines
13 KiB

/*
* KCompactDisc - A CD drive interface for the KDE Project.
*
* Copyright (c) 2005 Shaheedur R. Haque <srhaque@iee.org>
*
* 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, 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <dcopclient.h>
#include <dcopref.h>
#include <qfile.h>
#include <kdebug.h>
#include <klocale.h>
#include <kprotocolmanager.h>
#include <krun.h>
#include "kcompactdisc.h"
#include <netwm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <sys/utsname.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
/* this is for glibc 2.x which the ust structure in ustat.h not stat.h */
#ifdef __GLIBC__
#include <sys/ustat.h>
#endif
#ifdef __FreeBSD__
#include <sys/param.h>
#include <sys/ucred.h>
#include <sys/mount.h>
#endif
#ifdef __linux__
#include <mntent.h>
#define KSCDMAGIC 0
#endif
#include <kprocess.h>
#include <config.h>
extern "C"
{
// We don't have libWorkMan installed already, so get everything
// from within our own directory
#include "libwm/include/wm_cddb.h"
#include "libwm/include/wm_cdrom.h"
#include "libwm/include/wm_cdtext.h"
#include "libwm/include/wm_config.h"
#include "libwm/include/wm_cdinfo.h"
#include "libwm/include/wm_helpers.h"
// Sun, Ultrix etc. have a canonical CD device specified in the
// respective plat_xxx.c file. On those platforms you need not
// specify the CD device and DEFAULT_CD_DEVICE is not defined
// in config.h.
#ifndef DEFAULT_CD_DEVICE
#define DEFAULT_CD_DEVICE "/dev/cdrom"
#endif
}
#include <qtextcodec.h>
#include <fixx11h.h>
// Our internal definition of when we have no disc. Used to guard some
// internal arrays.
#define NO_DISC ((m_discId == missingDisc) && (m_previousDiscId == 0))
#define FRAMES_TO_MS(frames) \
((frames) * 1000 / 75)
#define TRACK_VALID(track) \
((track) && (track <= m_tracks))
const QString KCompactDisc::defaultDevice = DEFAULT_CD_DEVICE;
const unsigned KCompactDisc::missingDisc = (unsigned)-1;
KCompactDisc::KCompactDisc(InformationMode infoMode) :
m_device(QString::null),
m_status(0),
m_previousStatus(123456),
m_discId(missingDisc),
m_previousDiscId(0),
m_artist(QString::null),
m_title(QString::null),
m_track(0),
m_previousTrack(99999999),
m_infoMode(infoMode)
{
// Debug.
// wm_cd_set_verbosity(WM_MSG_LEVEL_DEBUG | WM_MSG_CLASS_ALL );
m_trackArtists.clear();
m_trackTitles.clear();
m_trackStartFrames.clear();
connect(&timer, SIGNAL(timeout()), SLOT(timerExpired()));
}
KCompactDisc::~KCompactDisc()
{
// Ensure nothing else starts happening.
timer.stop();
wm_cd_stop();
wm_cd_set_verbosity(0x0);
wm_cd_destroy();
}
const QString &KCompactDisc::device() const
{
return m_device;
}
unsigned KCompactDisc::discLength() const
{
if (NO_DISC || !m_tracks)
return 0;
return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]);
}
unsigned KCompactDisc::discPosition() const
{
return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]);
}
QString KCompactDisc::discStatus(int status)
{
QString message;
switch (status)
{
case WM_CDM_TRACK_DONE: // == WM_CDM_BACK
message = i18n("Back/Track Done");
break;
case WM_CDM_PLAYING:
message = i18n("Playing");
break;
case WM_CDM_FORWARD:
message = i18n("Forward");
break;
case WM_CDM_PAUSED:
message = i18n("Paused");
break;
case WM_CDM_STOPPED:
message = i18n("Stopped");
break;
case WM_CDM_EJECTED:
message = i18n("Ejected");
break;
case WM_CDM_NO_DISC:
message = i18n("No Disc");
break;
case WM_CDM_UNKNOWN:
message = i18n("Unknown");
break;
case WM_CDM_CDDAERROR:
message = i18n("CDDA Error");
break;
case WM_CDM_CDDAACK:
message = i18n("CDDA Ack");
break;
default:
if (status <= 0)
message = strerror(-status);
else
message = QString::number(status);
break;
}
return message;
}
/**
* Do everything needed if the user requested to eject the disc.
*/
void KCompactDisc::eject()
{
if (m_status == WM_CDM_EJECTED)
{
emit trayClosing();
wm_cd_closetray();
}
else
{
wm_cd_stop();
wm_cd_eject();
}
}
unsigned KCompactDisc::track() const
{
return m_track;
}
bool KCompactDisc::isPaused() const
{
return (m_status == WM_CDM_PAUSED);
}
bool KCompactDisc::isPlaying() const
{
return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE);
}
void KCompactDisc::pause()
{
// wm_cd_pause "does the right thing" by flipping between pause and resume.
wm_cd_pause();
}
void KCompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack)
{
wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK );
}
QString KCompactDisc::urlToDevice(const QString& device)
{
KURL deviceUrl(device);
if (deviceUrl.protocol() == "media" || deviceUrl.protocol() == "system")
{
kdDebug() << "Asking mediamanager for " << deviceUrl.fileName() << endl;
DCOPRef mediamanager("kded", "mediamanager");
DCOPReply reply = mediamanager.call("properties(QString)", deviceUrl.fileName());
QStringList properties = reply;
if (!reply.isValid() || properties.count() < 6)
{
kdError() << "Invalid reply from mediamanager" << endl;
return defaultDevice;
}
else
{
kdDebug() << "Reply from mediamanager " << properties[5] << endl;
return properties[5];
}
}
return device;
}
bool KCompactDisc::setDevice(
const QString &device_,
unsigned volume,
bool digitalPlayback,
const QString &audioSystem,
const QString &audioDevice)
{
timer.stop();
QString device = urlToDevice(device_);
#if !defined(BUILD_CDDA)
digitalPlayback = false;
#endif
int status = wm_cd_init(
digitalPlayback ? WM_CDDA : WM_CDIN,
QFile::encodeName(device),
digitalPlayback ? audioSystem.ascii() : 0,
digitalPlayback ? audioDevice.ascii() : 0,
0);
m_device = wm_drive_device();
kdDebug() << "Device change: "
<< (digitalPlayback ? "WM_CDDA, " : "WM_CDIN, ")
<< m_device << ", "
<< (digitalPlayback ? audioSystem : QString::null) << ", "
<< (digitalPlayback ? audioDevice : QString::null) << ", status: "
<< discStatus(status) << endl;
if (status < 0)
{
// Severe (OS-level) error.
m_device = QString::null;
}
else
{
// Init CD-ROM and display.
setVolume(volume);
}
m_previousStatus = m_status = wm_cd_status();
if (m_infoMode == Asynchronous)
timerExpired();
else
timer.start(1000, true);
return m_device != QString::null;
}
void KCompactDisc::setVolume(unsigned volume)
{
int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED);
kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl;
}
void KCompactDisc::stop()
{
wm_cd_stop();
}
const QString &KCompactDisc::trackArtist() const
{
return trackArtist(m_track);
}
const QString &KCompactDisc::trackArtist(unsigned track) const
{
if (NO_DISC || !TRACK_VALID(track))
return QString::null;
return m_trackArtists[track - 1];
}
unsigned KCompactDisc::trackLength() const
{
return trackLength(m_track);
}
unsigned KCompactDisc::trackLength(unsigned track) const
{
if (NO_DISC || !TRACK_VALID(track))
return 0;
return cd->trk[track - 1].length * 1000;
}
unsigned KCompactDisc::trackPosition() const
{
return cur_pos_rel * 1000;
}
unsigned KCompactDisc::tracks() const
{
return m_tracks;
}
const QString &KCompactDisc::trackTitle() const
{
return trackTitle(m_track);
}
const QString &KCompactDisc::trackTitle(unsigned track) const
{
if (NO_DISC || !TRACK_VALID(track))
return QString::null;
return m_trackTitles[track - 1];
}
bool KCompactDisc::isAudio(unsigned track) const
{
if (NO_DISC || !TRACK_VALID(track))
return 0;
return !(cd->trk[track - 1].data);
}
/*
* timerExpired
*
* - Data discs not recognized as data discs.
*
*/
void KCompactDisc::timerExpired()
{
m_status = wm_cd_status();
if (WM_CDS_NO_DISC(m_status) || (m_device == QString::null))
{
if (m_previousStatus != m_status)
{
m_previousStatus = m_status;
m_discId = missingDisc;
m_previousDiscId = 0;
m_trackArtists.clear();
m_trackTitles.clear();
m_trackStartFrames.clear();
m_tracks = 0;
m_track = 0;
emit discChanged(m_discId);
}
}
else
{
m_discId = cddb_discid();
if (m_previousDiscId != m_discId)
{
m_previousDiscId = m_discId;
kdDebug() << "New discId=" << m_discId << endl;
// Initialise the album and its signature from the CD.
struct cdtext_info *info = wm_cd_get_cdtext();
if (info && info->valid)
{
m_artist = reinterpret_cast<char*>(info->blocks[0]->performer[0]);
m_title = reinterpret_cast<char*>(info->blocks[0]->name[0]);
}
else
{
m_artist = i18n("Unknown Artist");
m_title = i18n("Unknown Title");
}
// Read or default CD data.
m_trackArtists.clear();
m_trackTitles.clear();
m_trackStartFrames.clear();
m_tracks = wm_cd_getcountoftracks();
for (unsigned i = 1; i <= m_tracks; i++)
{
if (info && info->valid)
{
m_trackArtists.append(reinterpret_cast<char*>(info->blocks[0]->performer[i]));
m_trackTitles.append(reinterpret_cast<char*>(info->blocks[0]->name[i]));
}
else
{
m_trackArtists.append(i18n("Unknown Artist"));
m_trackTitles.append(i18n("Track %1").arg(QString::number(i).rightJustify(2, '0')));
}
// FIXME: KDE4
// track.length = cd->trk[i - 1].length;
m_trackStartFrames.append(cd->trk[i - 1].start);
}
m_trackStartFrames.append(cd->trk[0].start);
m_trackStartFrames.append(cd->trk[m_tracks].start);
emit discChanged(m_discId);
}
// Per-event processing.
m_track = wm_cd_getcurtrack();
if (m_previousTrack != m_track)
{
m_previousTrack = m_track;
// Update the current track and its length.
emit trackChanged(m_track, trackLength());
}
if (isPlaying())
{
m_previousStatus = m_status;
// Update the current playing position.
emit trackPlaying(m_track, trackPosition());
}
else
if (m_previousStatus != m_status)
{
// If we are not playing, then we are either paused, or stopped.
switch (m_status)
{
case WM_CDM_PAUSED:
emit trackPaused(m_track, trackPosition());
break;
case WM_CDM_EJECTED:
emit trayOpening();
break;
default:
if (m_previousStatus == WM_CDM_PLAYING || m_previousStatus == WM_CDM_PAUSED
&& m_status == WM_CDM_STOPPED)
{
emit discStopped();
}
break;
}
m_previousStatus = m_status;
}
}
// Now that we have incurred any delays caused by the signals, we'll start the timer.
timer.start(1000, true);
}
#include "kcompactdisc.moc"