/* * TDECompactDisc - A CD drive interface for the KDE Project. * * Copyright (c) 2005 Shaheedur R. Haque * * 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 #include #include #include #include #include #include #include "kcompactdisc.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #include #include #endif #ifdef __linux__ #include #define KSCDMAGIC 0 #endif #include #include 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 #include // 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 TQString TDECompactDisc::defaultDevice = DEFAULT_CD_DEVICE; const unsigned TDECompactDisc::missingDisc = (unsigned)-1; TDECompactDisc::TDECompactDisc(InformationMode infoMode) : m_device(TQString()), m_status(0), m_previousStatus(123456), m_discId(missingDisc), m_previousDiscId(0), m_artist(TQString()), m_title(TQString()), 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, TQ_SIGNAL(timeout()), TQ_SLOT(timerExpired())); } TDECompactDisc::~TDECompactDisc() { // Ensure nothing else starts happening. timer.stop(); wm_cd_stop(); wm_cd_set_verbosity(0x0); wm_cd_destroy(); } const TQString &TDECompactDisc::device() const { return m_device; } unsigned TDECompactDisc::discLength() const { if (NO_DISC || !m_tracks) return 0; return FRAMES_TO_MS(m_trackStartFrames[m_tracks+1] - m_trackStartFrames[0]); } unsigned TDECompactDisc::discPosition() const { return cur_pos_abs * 1000 - FRAMES_TO_MS(m_trackStartFrames[0]); } TQString TDECompactDisc::discStatus(int status) { TQString 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 = TQString::number(status); break; } return message; } /** * Do everything needed if the user requested to eject the disc. */ void TDECompactDisc::eject() { if (m_status == WM_CDM_EJECTED) { emit trayClosing(); wm_cd_closetray(); } else { wm_cd_stop(); wm_cd_eject(); } } unsigned TDECompactDisc::track() const { return m_track; } bool TDECompactDisc::isPaused() const { return (m_status == WM_CDM_PAUSED); } bool TDECompactDisc::isPlaying() const { return WM_CDS_DISC_PLAYING(m_status) && (m_status != WM_CDM_PAUSED) && (m_status != WM_CDM_TRACK_DONE); } void TDECompactDisc::pause() { // wm_cd_pause "does the right thing" by flipping between pause and resume. wm_cd_pause(); } void TDECompactDisc::play(unsigned startTrack, unsigned startTrackPosition, unsigned endTrack) { wm_cd_play(TRACK_VALID(startTrack) ? startTrack : 1, startTrackPosition / 1000, TRACK_VALID(endTrack) ? endTrack : WM_ENDTRACK ); } TQString TDECompactDisc::urlToDevice(const TQString& 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(TQString)", deviceUrl.fileName()); TQStringList properties = reply; if (!reply.isValid() || properties.count() < 7 ) { kdError() << "Invalid reply from mediamanager" << endl; return defaultDevice; } else { /* see DEVICE_NODE in tdebase/tdeioslave/media/libmediacommon/medium.h */ kdDebug() << "Reply from mediamanager " << properties[6] << endl; return properties[6]; } } return device; } bool TDECompactDisc::setDevice( const TQString &device_, unsigned volume, bool digitalPlayback, const TQString &audioSystem, const TQString &audioDevice) { timer.stop(); TQString device = urlToDevice(device_); #if !defined(BUILD_CDDA) digitalPlayback = false; #endif int status = wm_cd_init( digitalPlayback ? WM_CDDA : WM_CDIN, TQFile::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 : TQString()) << ", " << (digitalPlayback ? audioDevice : TQString()) << ", status: " << discStatus(status) << endl; if (status < 0) { // Severe (OS-level) error. m_device = TQString(); } 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 != TQString(); } void TDECompactDisc::setVolume(unsigned volume) { int status = wm_cd_volume(volume, WM_BALANCE_SYMMETRED); kdDebug() << "Volume change: " << volume << ", status: " << discStatus(status) << endl; } void TDECompactDisc::stop() { wm_cd_stop(); } const TQString &TDECompactDisc::trackArtist() const { return trackArtist(m_track); } const TQString &TDECompactDisc::trackArtist(unsigned track) const { if (NO_DISC || !TRACK_VALID(track)) return TQString(); return m_trackArtists[track - 1]; } unsigned TDECompactDisc::trackLength() const { return trackLength(m_track); } unsigned TDECompactDisc::trackLength(unsigned track) const { if (NO_DISC || !TRACK_VALID(track)) return 0; return cd->trk[track - 1].length * 1000; } unsigned TDECompactDisc::trackPosition() const { return cur_pos_rel * 1000; } unsigned TDECompactDisc::tracks() const { return m_tracks; } const TQString &TDECompactDisc::trackTitle() const { return trackTitle(m_track); } const TQString &TDECompactDisc::trackTitle(unsigned track) const { if (NO_DISC || !TRACK_VALID(track)) return TQString(); return m_trackTitles[track - 1]; } bool TDECompactDisc::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 TDECompactDisc::timerExpired() { m_status = wm_cd_status(); if (WM_CDS_NO_DISC(m_status) || (m_device == TQString())) { 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(info->blocks[0]->performer[0]); m_title = reinterpret_cast(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(info->blocks[0]->performer[i])); m_trackTitles.append(reinterpret_cast(info->blocks[0]->name[i])); } else { m_trackArtists.append(i18n("Unknown Artist")); m_trackTitles.append(i18n("Track %1").arg(TQString::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"