diff options
Diffstat (limited to 'tderadio3/plugins/v4lradio/v4lradio.cpp')
-rw-r--r-- | tderadio3/plugins/v4lradio/v4lradio.cpp | 1621 |
1 files changed, 1621 insertions, 0 deletions
diff --git a/tderadio3/plugins/v4lradio/v4lradio.cpp b/tderadio3/plugins/v4lradio/v4lradio.cpp new file mode 100644 index 0000000..82ed12e --- /dev/null +++ b/tderadio3/plugins/v4lradio/v4lradio.cpp @@ -0,0 +1,1621 @@ +/*************************************************************************** + v4lradio.cpp - description + ------------------- + begin : Don M�r 8 21:57:17 CET 2001 + copyright : (C) 2002-2005 by Ernst Martin Witte + email : witte@kawo1.rwth-aachen.de + ***************************************************************************/ + +/*************************************************************************** + * * + * 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <fcntl.h> +#include <sys/ioctl.h> +#include <unistd.h> + +#ifdef HAVE_V4L2 +#include "linux/videodev2.h" +#endif +#include "linux/videodev.h" +#include <linux/soundcard.h> + +#include <string.h> // memcpy needed + +#include <tqlayout.h> +#include <tqfile.h> +#include <tqfileinfo.h> +#include <tqvaluelist.h> + +#include <tdeconfig.h> +#include <kiconloader.h> +#include <kdialogbase.h> +#include <kaboutdata.h> +#include <klocale.h> + +#include "../../src/include/aboutwidget.h" +#include "../../src/include/utils.h" +#include "v4lradio.h" +#include "v4lradio-configuration.h" + +#include "../../src/include/debug-profiler.h" + +struct _lrvol { unsigned char l, r; short dummy; }; + +/////////////////////////////////////////////////////////////////////// + +PLUGIN_LIBRARY_FUNCTIONS(V4LRadio, "kradio-v4lradio", i18n("Support for V4L(2) Radio Devices")); + +/////////////////////////////////////////////////////////////////////// + +V4LRadio::V4LRadio(const TQString &name) + : PluginBase(name, i18n("Video For Linux Plugin")), + m_treble(0.5), + m_bass(0.5), + m_balance(0), + m_deviceVolume(0.9), + m_muted(false), + m_signalQuality(0), + m_stereo(false), + m_minQuality(0.75), + m_minFrequency(87.0), + m_maxFrequency(108.0), + m_lastMinDevFrequency(87.0), + m_lastMaxDevFrequency(108.0), + + m_defaultPlaybackVolume(0.5), + + m_scanStep(0.05), + + m_radioDev("/dev/radio0"), + m_radio_fd(-1), + m_useOldV4L2Calls(true), + m_pollTimer(this), + + m_blockReadTuner(false), + m_blockReadAudio(false), + + m_SoundStreamID(createNewSoundStream(false)), + m_PlaybackMixerID(TQString()), + m_CaptureMixerID(TQString()), + m_PlaybackMixerChannel(TQString()), + m_CaptureMixerChannel(TQString()), + m_ActivePlayback(false), + m_MuteOnPowerOff(false), + m_VolumeZeroOnPowerOff(false), + m_restorePowerOn(false) +{ + TQObject::connect (&m_pollTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(poll())); + m_pollTimer.start(333); + + m_audio = new video_audio; + bzero(m_audio, sizeof(video_audio)); + m_tuner = new video_tuner; + bzero(m_tuner, sizeof(video_tuner)); +#ifdef HAVE_V4L2 + m_tuner2 = new v4l2_tuner; + bzero(m_tuner2, sizeof(v4l2_tuner)); +#endif + m_caps.version = 0; + + m_seekHelper = new FrequencySeekHelper(*this); + m_seekHelper->connectI(this); +} + + +V4LRadio::~V4LRadio() +{ + setPower(false); + + if (m_seekHelper) + delete m_seekHelper; + + if (m_audio) delete m_audio; + if (m_tuner) delete m_tuner; +#ifdef HAVE_V4L2 + if (m_tuner2) delete m_tuner2; +#endif +} + + +bool V4LRadio::connectI (Interface *i) +{ + bool a = IRadioDevice::connectI(i); + bool b = ISeekRadio::connectI(i); + bool c = IFrequencyRadio::connectI(i); + bool d = IV4LCfg::connectI(i); + bool e = PluginBase::connectI(i); + bool f = ISoundStreamClient::connectI(i); + return a || b || c || d || e || f; +} + + +bool V4LRadio::disconnectI (Interface *i) +{ + bool a = IRadioDevice::disconnectI(i); + bool b = ISeekRadio::disconnectI(i); + bool c = IFrequencyRadio::disconnectI(i); + bool d = IV4LCfg::disconnectI(i); + bool e = PluginBase::disconnectI(i); + bool f = ISoundStreamClient::disconnectI(i); + m_seekHelper->disconnectI(i); + return a || b || c || d || e || f; +} + + +void V4LRadio::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid) +{ + ISoundStreamClient::noticeConnectedI(s, pointer_valid); + if (s && pointer_valid) { + m_seekHelper->connectI(s); + + s->register4_queryPlaybackVolume(this); + s->register4_sendTreble(this); + s->register4_sendBass(this); + s->register4_sendBalance(this); + s->register4_sendMute(this); + s->register4_sendUnmute(this); + s->register4_sendSignalMinQuality(this); + s->register4_sendStereo(this); + + s->register4_queryTreble(this); + s->register4_queryBass(this); + s->register4_queryBalance(this); + s->register4_querySignalQuality(this); + s->register4_querySignalMinQuality(this); + s->register4_queryHasGoodQuality(this); + s->register4_queryIsStereo(this); + s->register4_queryIsMuted(this); + + + s->register4_sendPlaybackVolume(this); + s->register4_sendCaptureVolume(this); + + s->register4_sendStopCapture(this); + + s->register4_querySoundStreamDescription(this); + s->register4_querySoundStreamRadioStation(this); + s->register4_queryEnumerateSoundStreams(this); + + notifySoundStreamCreated(m_SoundStreamID); + } +} + +void V4LRadio::noticeConnectedSoundClient(ISoundStreamClient::thisInterface *i, bool pointer_valid) +{ + if (i && pointer_valid && i->getSoundStreamClientID() == m_PlaybackMixerID) { + setPlaybackMixer(m_PlaybackMixerID, m_PlaybackMixerChannel); + } + if (i && pointer_valid && i->getSoundStreamClientID() == m_CaptureMixerID) { + setCaptureMixer(m_CaptureMixerID, m_CaptureMixerChannel); + } +} + +// IRadioDevice methods + +bool V4LRadio::setPower (bool on) +{ + return on ? powerOn() : powerOff(); +} + +void V4LRadio::searchMixers(ISoundStreamClient **playback_mixer, ISoundStreamClient **capture_mixer) +{ + if (playback_mixer) { + *playback_mixer = getSoundStreamClientWithID(m_PlaybackMixerID); + if (!*playback_mixer) { + TQPtrList<ISoundStreamClient> playback_mixers = queryPlaybackMixers(); + if (!playback_mixers.isEmpty()) + *playback_mixer = playback_mixers.first(); + } + } + if (capture_mixer) { + *capture_mixer = getSoundStreamClientWithID(m_CaptureMixerID); + if (!*capture_mixer) { + TQPtrList<ISoundStreamClient> capture_mixers = queryCaptureMixers(); + if (!capture_mixers.isEmpty()) + *capture_mixer = capture_mixers.first(); + } + } +} + + +bool V4LRadio::powerOn () +{ + if (isPowerOn()) + return true; + + radio_init(); + + if (isPowerOn()) { + ISoundStreamClient *playback_mixer = NULL, + *capture_mixer = NULL; + + searchMixers(&playback_mixer, &capture_mixer); + + if (playback_mixer) + playback_mixer->preparePlayback(m_SoundStreamID, m_PlaybackMixerChannel, m_ActivePlayback, false); + if (capture_mixer) + capture_mixer->prepareCapture(m_SoundStreamID, m_CaptureMixerChannel); + + sendStartPlayback(m_SoundStreamID); + float tmp_vol = 0; + queryPlaybackVolume(m_SoundStreamID, tmp_vol); + if (tmp_vol < 0.005) + sendPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + + if (m_ActivePlayback) { + SoundFormat sf; + sendStartCaptureWithFormat(m_SoundStreamID, sf, sf); + } + + unmute(m_SoundStreamID); + notifyPowerChanged(true); + } + + return true; +} + + +bool V4LRadio::powerOff () +{ + if (! isPowerOn()) + return true; + + queryPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + if (m_MuteOnPowerOff) + sendMute(m_SoundStreamID, true); + if (m_VolumeZeroOnPowerOff) + sendPlaybackVolume(m_SoundStreamID, 0.0); + mute(m_SoundStreamID); + radio_done(); + + sendStopPlayback(m_SoundStreamID); + sendStopCapture(m_SoundStreamID); + closeSoundStream(m_SoundStreamID); + m_SoundStreamID = createNewSoundStream(m_SoundStreamID, false); + notifySoundStreamCreated(m_SoundStreamID); + + if (isPowerOff()) { + notifyPowerChanged(false); + } + + return true; +} + + +bool V4LRadio::activateStation(const RadioStation &rs) +{ + const FrequencyRadioStation *frs = dynamic_cast<const FrequencyRadioStation*>(&rs); + if (frs == NULL) + return false; + + if (setFrequency(frs->frequency())) { + m_currentStation = *frs; + + if (frs->initialVolume() > 0) + setPlaybackVolume(m_SoundStreamID, frs->initialVolume()); + + return true; + } + + return false; +} + + + +bool V4LRadio::isPowerOn() const +{ + return m_radio_fd >= 0; +} + + +bool V4LRadio::isPowerOff() const +{ + return m_radio_fd < 0; +} + + +SoundStreamID V4LRadio::getSoundStreamID() const +{ + return m_SoundStreamID; +} + + +const RadioStation &V4LRadio::getCurrentStation() const +{ + return m_currentStation; +} + + +const TQString &V4LRadio::getDescription() const +{ + return m_caps.description; +} + + +SoundStreamID V4LRadio::getCurrentSoundStreamID() const +{ + return m_SoundStreamID; +} + + + + +bool V4LRadio::setTreble (SoundStreamID id, float t) +{ + if (id != m_SoundStreamID) + return false; + + if (t > 1.0) t = 1.0; + if (t < 0) t = 0.0; + if ((int)rint(m_treble*65535) != (int)rint(t*65535)) { + m_treble = t; + writeAudioInfo(); + notifyTrebleChanged(id, t); + } + return true; +} + + +bool V4LRadio::setBass (SoundStreamID id, float b) +{ + if (id != m_SoundStreamID) + return false; + + if (b > 1.0) b = 1.0; + if (b < 0) b = 0.0; + if ((int)rint(m_bass*65535) != (int)rint(b*65535)) { + m_bass = b; + writeAudioInfo(); + notifyBassChanged(id, b); + } + + return true; +} + + +bool V4LRadio::setBalance (SoundStreamID id, float b) +{ + if (id != m_SoundStreamID) + return false; + + if (b > +1.0) b = +1.0; + if (b < -1.0) b = -1.0; + if ((int)rint(m_balance*32767) != (int)rint(b*32767)) { + m_balance = b; + writeAudioInfo(); + notifyBalanceChanged(id, b); + } + return true; +} + + +bool V4LRadio::setDeviceVolume (float v) +{ + if (v > 1.0) v = 1.0; + if (v < 0) v = 0; + if ((int)rint(m_deviceVolume*65535) != (int)rint(v*65535)) { + m_deviceVolume = v; + writeAudioInfo(); + notifyDeviceVolumeChanged(v); + } + return true; +} + + +bool V4LRadio::mute (SoundStreamID id, bool mute) +{ + if (id != m_SoundStreamID) + return false; + + if (m_muted != mute) { + m_muted = mute; + bool r = writeAudioInfo(); + if (r) + notifyMuted(id, m_muted); + return r; + } + return false; +} + + +bool V4LRadio::unmute (SoundStreamID id, bool unmute) +{ + return mute(id, !unmute); +} + + +bool V4LRadio::setSignalMinQuality (SoundStreamID id, float mq) +{ + if (id != m_SoundStreamID) + return false; + if (rint(mq*100) == rint(m_minQuality*100)) + return true; + + m_minQuality = mq; + notifySignalMinQualityChanged(id, m_minQuality); + return true; +} + + +bool V4LRadio::setStereo(SoundStreamID /*id*/, bool /*b*/) +{ + // FIXME if possible + return false; // we can't do that currently, not even switch stereo to mono +} + + + + +bool V4LRadio::getTreble (SoundStreamID id, float &t) const +{ + if (id != m_SoundStreamID) + return false; + + readAudioInfo(); + t = m_treble; + return true; +} + + +bool V4LRadio::getBass (SoundStreamID id, float &b) const +{ + if (id != m_SoundStreamID) + return false; + + readAudioInfo(); + b = m_bass; + return true; +} + + +bool V4LRadio::getBalance (SoundStreamID id, float &b) const +{ + if (id != m_SoundStreamID) + return false; + + readAudioInfo(); + b = m_balance; + return true; +} + + +float V4LRadio::getDeviceVolume () const +{ + readAudioInfo(); + return m_deviceVolume; +} + + + +bool V4LRadio::getSignalMinQuality(SoundStreamID id, float &q) const +{ + if (id != m_SoundStreamID) + return false; + + q = m_minQuality; + return true; +} + + +bool V4LRadio::getSignalQuality(SoundStreamID id, float &q) const +{ + if (id != m_SoundStreamID) + return false; + + readTunerInfo(); + q = m_signalQuality; + return true; +} + + +bool V4LRadio::hasGoodQuality(SoundStreamID id, bool &good) const +{ + if (id != m_SoundStreamID) + return false; + + float q = 0; + if (getSignalQuality(id, q)) + good = q >= m_minQuality; + return true; +} + + +bool V4LRadio::isStereo(SoundStreamID id, bool &s) const +{ + if (id != m_SoundStreamID) + return false; + + readAudioInfo(); + s = m_stereo; + return true; +} + + +bool V4LRadio::isMuted(SoundStreamID id, bool &m) const +{ + if (id != m_SoundStreamID) + return false; + + readAudioInfo(); + m = m_muted; + return true; +} + + +// ISeekRadio + +bool V4LRadio::toBeginning() +{ + setFrequency(getMinFrequency()); + return true; +} + +bool V4LRadio::toEnd() +{ + setFrequency(getMaxFrequency()); + return true; +} + +bool V4LRadio::startSeekUp() +{ + return startSeek(true); +} + +bool V4LRadio::startSeekDown() +{ + return startSeek(false); +} + +bool V4LRadio::startSeek(bool up) +{ + if (isPowerOn() && m_seekHelper) { + m_seekHelper->start(m_SoundStreamID, up ? SeekHelper::up : SeekHelper::down); + return true; + } else { + return false; + } +} + +bool V4LRadio::stopSeek() +{ + if (m_seekHelper) m_seekHelper->stop(); + return true; +} + +bool V4LRadio::isSeekRunning() const +{ + if (m_seekHelper) + return m_seekHelper->isRunning(); + else + return false; +} + + +bool V4LRadio::isSeekUpRunning() const +{ + if (m_seekHelper) + return m_seekHelper->isRunningUp(); + else + return false; +} + + +bool V4LRadio::isSeekDownRunning() const +{ + if (m_seekHelper) + return m_seekHelper->isRunningDown(); + else + return false; +} + +float V4LRadio::getProgress () const +{ + float min = getMinFrequency(); + float max = getMaxFrequency(); + + return (getFrequency() - min) / (max - min); +} + + +// IFrequencyRadio + +bool V4LRadio::setFrequency(float freq) +{ +// if (isSeekRunning()) +// stopSeek(); + + if (m_currentStation.frequency() == freq) { + return true; + } + + float minf = getMinFrequency(); + float maxf = getMaxFrequency(); + + if (isPowerOn()) { + + bool oldMute = false; + isMuted(m_SoundStreamID, oldMute); + if (!oldMute && !m_ActivePlayback) + mute(m_SoundStreamID); + + + if (!m_tunercache.valid) readTunerInfo(); + float df = m_tunercache.deltaF; + + unsigned long lfreq = (unsigned long) rint(freq / df); + + if (freq > maxf || freq < minf) { + logError("V4LRadio::setFrequency: " + + i18n("invalid frequency %1").arg(TQString().setNum(freq))); + if (!oldMute && !m_ActivePlayback) + unmute(m_SoundStreamID); + return false; + } + + int r = -1; + if (m_caps.version == 1) { + r = ioctl(m_radio_fd, VIDIOCSFREQ, &lfreq); + } +#ifdef HAVE_V4L2 + else if (m_caps.version == 2) { + v4l2_frequency tmp; + tmp.tuner = 0; + tmp.type = V4L2_TUNER_RADIO; + tmp.frequency = lfreq; + r = ioctl(m_radio_fd, VIDIOC_S_FREQUENCY, &tmp); + } +#endif + else { + logError("V4LRadio::setFrequency: " + + i18n("don't known how to handle V4L-version %1") + .arg(m_caps.version)); + } + + if (r) { + logError("V4LRadio::setFrequency: " + + i18n("error setting frequency to %1 (%2)") + .arg(TQString().setNum(freq)) + .arg(TQString().setNum(r))); + // unmute the old radio with the old radio station + if (!oldMute && !m_ActivePlayback) + unmute(m_SoundStreamID); + return false; + } + + // unmute this radio device, because we now have the current + // radio station + if (!oldMute && !m_ActivePlayback) + unmute(m_SoundStreamID); + } + + m_currentStation.setFrequency(freq); + notifyFrequencyChanged(freq, &m_currentStation); + notifyStationChanged(m_currentStation); + notifyProgress((freq - minf) / (maxf - minf)); + notifySoundStreamChanged(m_SoundStreamID); + return true; +} + + +bool V4LRadio::setMinFrequency (float minF) +{ + float oldm = getMinFrequency(); + m_minFrequency = minF; + + float newm = getMinFrequency(); + if (oldm != newm) + notifyMinMaxFrequencyChanged(newm, getMaxFrequency()); + + return true; +} + + +bool V4LRadio::setMaxFrequency (float maxF) +{ + float oldm = getMaxFrequency(); + m_maxFrequency = maxF; + + float newm = getMaxFrequency(); + if (oldm != newm) + notifyMinMaxFrequencyChanged(getMinFrequency(), newm); + + return true; +} + + +bool V4LRadio::setScanStep(float s) +{ + float old = m_scanStep; + m_scanStep = s; + + if (old != s) notifyScanStepChanged(m_scanStep); + return true; +} + + +float V4LRadio::getFrequency() const +{ + return m_currentStation.frequency(); +} + + +float V4LRadio::getMinFrequency() const +{ + return m_minFrequency ? m_minFrequency : getMinDeviceFrequency(); +} + + +float V4LRadio::getMaxFrequency() const +{ + return m_maxFrequency ? m_maxFrequency : getMaxDeviceFrequency(); +} + + +float V4LRadio::getMinDeviceFrequency() const +{ + if (!m_tunercache.valid) + readTunerInfo(); + + return m_tunercache.minF; +} + + +float V4LRadio::getMaxDeviceFrequency() const +{ + if (!m_tunercache.valid) + readTunerInfo(); + + return m_tunercache.maxF; +} + + +float V4LRadio::getScanStep() const +{ + return m_scanStep; +} + + + +// IV4LCfg methods + +bool V4LRadio::setRadioDevice(const TQString &s) +{ + if (m_radioDev != s) { + bool p = isPowerOn(); + powerOff(); + m_radioDev = s; + + m_caps = readV4LCaps(m_radioDev); + notifyRadioDeviceChanged(m_radioDev); + notifyDescriptionChanged(m_caps.description); + notifyCapabilitiesChanged(m_caps); + setPower(p); + } + return true; +} + + +bool V4LRadio::setPlaybackMixer(const TQString &soundStreamClientID, const TQString &ch) +{ + bool change = m_PlaybackMixerID != soundStreamClientID || m_PlaybackMixerChannel != ch; + m_PlaybackMixerID = soundStreamClientID; + m_PlaybackMixerChannel = ch; + + + if (isPowerOn()) { + queryPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + sendStopPlayback(m_SoundStreamID); + sendReleasePlayback(m_SoundStreamID); + } + + ISoundStreamClient *playback_mixer = NULL; + searchMixers(&playback_mixer, NULL); + if (playback_mixer) + playback_mixer->preparePlayback(m_SoundStreamID, m_PlaybackMixerChannel, m_ActivePlayback, false); + + if (isPowerOn()) { + sendStartPlayback(m_SoundStreamID); + sendPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + if (m_ActivePlayback) { + SoundFormat sf; + sendStartCaptureWithFormat(m_SoundStreamID, sf, sf); + } + } + + if (change) + notifyPlaybackMixerChanged(soundStreamClientID, ch); + + return true; +} + + +bool V4LRadio::setCaptureMixer(const TQString &soundStreamClientID, const TQString &ch) +{ + bool change = m_PlaybackMixerID != soundStreamClientID || m_PlaybackMixerChannel != ch; + m_CaptureMixerID = soundStreamClientID; + m_CaptureMixerChannel = ch; + + bool r = false; + SoundFormat sf; + queryIsCaptureRunning(m_SoundStreamID, r, sf); + + float v = 0; + if (isPowerOn() && r) { + queryCaptureVolume(m_SoundStreamID, v); + sendStopCapture(m_SoundStreamID); + sendReleaseCapture(m_SoundStreamID); + } + + ISoundStreamClient *capture_mixer = NULL; + searchMixers(NULL, &capture_mixer); + if (capture_mixer) + capture_mixer->prepareCapture(m_SoundStreamID, m_CaptureMixerChannel); + + if (isPowerOn() && r) { + sendStartCaptureWithFormat(m_SoundStreamID, sf, sf); + sendCaptureVolume(m_SoundStreamID, v); + } + + if (change) + notifyCaptureMixerChanged(soundStreamClientID, ch); + + return true; +} + + +V4LCaps V4LRadio::getCapabilities(TQString dev) const +{ + if (dev.isNull()) { + return m_caps; + } else { + return readV4LCaps(dev); + } +} + + +bool V4LRadio::setActivePlayback(bool a) +{ + if (a == m_ActivePlayback) + return true; + + + if (isPowerOn()) { + queryPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + sendStopPlayback(m_SoundStreamID); + sendReleasePlayback(m_SoundStreamID); + if (m_ActivePlayback) { + sendStopCapture(m_SoundStreamID); + } + } + + m_ActivePlayback = a; + + ISoundStreamClient *playback_mixer = NULL; + searchMixers(&playback_mixer, NULL); + if (playback_mixer) + playback_mixer->preparePlayback(m_SoundStreamID, m_PlaybackMixerChannel, m_ActivePlayback, false); + + if (isPowerOn()) { + sendStartPlayback(m_SoundStreamID); + sendPlaybackVolume(m_SoundStreamID, m_defaultPlaybackVolume); + if (m_ActivePlayback) { + SoundFormat sf; + sendStartCaptureWithFormat(m_SoundStreamID, sf, sf); + } + } + + // FIXME: restart playback/capture + notifyActivePlaybackChanged(m_ActivePlayback); + + return true; +} + +bool V4LRadio::setMuteOnPowerOff(bool a) +{ + if (a != m_MuteOnPowerOff) { + m_MuteOnPowerOff = a; + notifyMuteOnPowerOffChanged(m_MuteOnPowerOff); + } + return true; +} + +bool V4LRadio::setVolumeZeroOnPowerOff(bool a) +{ + if (a != m_VolumeZeroOnPowerOff) { + m_VolumeZeroOnPowerOff = a; + notifyVolumeZeroOnPowerOffChanged(m_VolumeZeroOnPowerOff); + } + return true; +} + +// PluginBase methods + +void V4LRadio::saveState (TDEConfig *config) const +{ + config->setGroup(TQString("v4lradio-") + name()); + + config->writeEntry("RadioDev", m_radioDev); + + config->writeEntry("PlaybackMixerID", m_PlaybackMixerID); + config->writeEntry("PlaybackMixerChannel", m_PlaybackMixerChannel); + config->writeEntry("CaptureMixerID", m_CaptureMixerID); + config->writeEntry("CaptureMixerChannel", m_CaptureMixerChannel); + + config->writeEntry("fMinOverride", m_minFrequency); + config->writeEntry("fMaxOverride", m_maxFrequency); + config->writeEntry("fLastDevMin", m_lastMinDevFrequency); + config->writeEntry("fLastDevMax", m_lastMaxDevFrequency); + + config->writeEntry("defaultPlaybackVolume", m_defaultPlaybackVolume); + + config->writeEntry("signalMinQuality", m_minQuality); + + config->writeEntry("scanStep", m_scanStep); + + config->writeEntry("Frequency", m_currentStation.frequency()); + config->writeEntry("Treble", m_treble); + config->writeEntry("Bass", m_bass); + config->writeEntry("Balance", m_balance); + config->writeEntry("DeviceVolume", m_deviceVolume); + + config->writeEntry("PowerOn", isPowerOn()); + config->writeEntry("UseOldV4L2Calls", m_useOldV4L2Calls); + + config->writeEntry("ActivePlayback", m_ActivePlayback); + config->writeEntry("MuteOnPowerOff", m_MuteOnPowerOff); + config->writeEntry("VolumeZeroOnPowerOff", m_VolumeZeroOnPowerOff); +} + + +void V4LRadio::restoreState (TDEConfig *config) +{ + BlockProfiler p("V4LRadio::restoreState"); + + config->setGroup(TQString("v4lradio-") + name()); + + TQString base_devname = "/dev/radio"; + + TQStringList testlist (base_devname ); + for (int i = 0; i < 9; ++i) + testlist.append(base_devname + TQString::number(i)); + + TQString found_devname(TQString::null); + for (TQValueListConstIterator<TQString> it = testlist.begin(); it != testlist.end(); ++it) { + TQFile f(*it); + if (f.exists()) { + TQFileInfo info(f); + if (info.isReadable() && info.isWritable()) { + found_devname = *it; + break; + } + else { + if (found_devname.isNull()) + found_devname = *it; + logWarning(i18n("Device %1 does exist but is not readable/writable. Please check device permissions.").arg(*it)); + } + } + } + + TQString default_devname = found_devname.isNull() ? base_devname : found_devname; + + TQString devname = config->readEntry ("RadioDev", default_devname); + + if (found_devname.isNull() && devname == default_devname) { + logError(i18n("Could not find an accessible v4l(2) radio device.")); + } + + setRadioDevice(devname); + + TQString PlaybackMixerID = config->readEntry ("PlaybackMixerID", TQString()); + TQString PlaybackMixerChannel = config->readEntry ("PlaybackMixerChannel", "Line"); + + TQString CaptureMixerID = config->readEntry ("CaptureMixerID", TQString()); + TQString CaptureMixerChannel = config->readEntry ("CaptureMixerChannel", "Line"); + + m_ActivePlayback = config->readBoolEntry("ActivePlayback", false); + m_MuteOnPowerOff = config->readBoolEntry("MuteOnPowerOff", false); + m_VolumeZeroOnPowerOff = config->readBoolEntry("VolumeZeroOnPowerOff", false); + + m_lastMinDevFrequency = config->readDoubleNumEntry ("fLastDevMin", 65.0); + m_lastMaxDevFrequency = config->readDoubleNumEntry ("fLastDevMax", 108.0); + m_minFrequency = config->readDoubleNumEntry ("fMinOverride", m_lastMinDevFrequency); + m_maxFrequency = config->readDoubleNumEntry ("fMaxOverride", m_lastMaxDevFrequency); + + m_minQuality = config->readDoubleNumEntry ("signalMinQuality", 0.75); + m_scanStep = config->readDoubleNumEntry ("scanStep", 0.05); + m_defaultPlaybackVolume = config->readDoubleNumEntry ("defaultPlaybackVolume", 0.5); + + setPlaybackMixer(PlaybackMixerID, PlaybackMixerChannel); + setCaptureMixer (CaptureMixerID, CaptureMixerChannel); + notifyDeviceMinMaxFrequencyChanged(m_lastMinDevFrequency, m_lastMaxDevFrequency); + notifyMinMaxFrequencyChanged(m_minFrequency, m_maxFrequency); + notifySignalMinQualityChanged(m_SoundStreamID, m_minQuality); + notifyScanStepChanged(m_scanStep); + notifyActivePlaybackChanged(m_ActivePlayback); + notifyMuteOnPowerOffChanged(m_MuteOnPowerOff); + notifyVolumeZeroOnPowerOffChanged(m_VolumeZeroOnPowerOff); + + BlockProfiler p2("V4LRadio::restoreState2"); + + setFrequency(config->readDoubleNumEntry("Frequency", 88)); + m_restorePowerOn = config->readBoolEntry ("PowerOn", false); + + BlockProfiler p3("V4LRadio::restoreState3"); + + setTreble (m_SoundStreamID, config->readDoubleNumEntry("Treble", 0.5)); + setBass (m_SoundStreamID, config->readDoubleNumEntry("Bass", 0.5)); + setBalance (m_SoundStreamID, config->readDoubleNumEntry("Balance", 0.0)); + setDeviceVolume( config->readDoubleNumEntry("DeviceVolume", 0.9)); + + m_useOldV4L2Calls = config->readBoolEntry("UseOldV4L2Calls", true); + + if (isPowerOff()) + notifyPlaybackVolumeChanged(m_SoundStreamID, m_defaultPlaybackVolume); +} + +void V4LRadio::startPlugin() +{ + PluginBase::startPlugin(); + setPower(m_restorePowerOn); +} + +ConfigPageInfo V4LRadio::createConfigurationPage() +{ + V4LRadioConfiguration *v4lconf = new V4LRadioConfiguration(NULL, m_SoundStreamID); + connectI(v4lconf); + return ConfigPageInfo (v4lconf, + i18n("V4L Radio"), + i18n("V4L Radio Options"), + "package_utilities"); +} + + +AboutPageInfo V4LRadio::createAboutPage() +{ + TDEAboutData aboutData("kradio", + NULL, + NULL, + I18N_NOOP("V4L/V4L2 Plugin for TDERadio." + "<P>" + "Provides Support for V4L/V4L2 based Radio Cards" + "<P>"), + 0, + //TDEAboutData::License_GPL, + "(c) 2002-2005 Martin Witte, Klas Kalass", + 0, + "http://sourceforge.net/projects/kradio", + 0); + aboutData.addAuthor("Martin Witte", "", "witte@kawo1.rwth-aachen.de"); + aboutData.addAuthor("Klas Kalass", "", "klas.kalass@gmx.de"); + + return AboutPageInfo( + new TDERadioAboutWidget(aboutData, TDERadioAboutWidget::AbtTabbed), + i18n("V4L/V4L2"), + i18n("V4L/V4L2 Plugin"), + "package_utilities" + ); +} + +//////////////////////////////////////// +// anything else + +void V4LRadio::radio_init() +{ + if (isSeekRunning()) + stopSeek(); + + m_caps = readV4LCaps(m_radioDev); + notifyCapabilitiesChanged(m_caps); + notifyDescriptionChanged(m_caps.description); + +/* m_mixer_fd = open(m_mixerDev, O_RDONLY); + if (m_mixer_fd < 0) { + radio_done(); + + logError("V4LRadio::radio_init: " + + i18n("Cannot open mixer device %1").arg(m_mixerDev)); + return; + } +*/ + m_radio_fd = open(m_radioDev.ascii(), O_RDONLY); + if (m_radio_fd < 0) { + radio_done(); + + logError("V4LRadio::radio_init: " + + i18n("Cannot open radio device %1").arg(m_radioDev)); + return; + } + + readTunerInfo(); + writeAudioInfo(); // set tuner-audio config as used last time + readAudioInfo(); // reread tuner-audio and read-only flags (e.g. stereo) + + // restore frequency + float old = getFrequency(); + m_currentStation.setFrequency(0); + setFrequency(old); + + // read volume level from mixer + // FIXME: do we still need this +/* float v = 0; + getVolume(m_SoundStreamID, v) + setVolume (m_SoundStreamID, v);*/ +} + + +void V4LRadio::radio_done() +{ + if (isSeekRunning()) + stopSeek(); + + if (m_radio_fd >= 0) close (m_radio_fd); +// if (m_mixer_fd >= 0) close (m_mixer_fd); + + m_radio_fd = -1; +// m_mixer_fd = -1; +} + + + + + +#define CAPS_NAME_LEN 127 +V4LCaps V4LRadio::readV4LCaps(const TQString &device) const +{ + char buffer[CAPS_NAME_LEN+1]; + int r; + int fd; + + V4LCaps c; + c.description = device; + + fd = open(device.ascii(), O_RDONLY); + + if (fd < 0) { + logError("V4LRadio::readV4LCaps: " + + i18n("cannot open %1").arg(device)); + return c; + } + + video_capability caps; + r = ioctl(fd, VIDIOCGCAP, &caps); + if (r == 0) { + c.version = 1; + + size_t l = sizeof(caps.name); + l = l < CAPS_NAME_LEN ? l : CAPS_NAME_LEN; + memcpy(buffer, caps.name, l); + buffer[l] = 0; + c.description = buffer; + + c.hasMute = false; + c.unsetVolume(); + c.unsetTreble(); + c.unsetBass(); + c.unsetBalance(); + + video_audio audiocaps; + if (0 == ioctl(fd, VIDIOCGAUDIO, &audiocaps)) { + logDebug("V4LRadio::readV4LCaps: " + + i18n("audio caps = %1").arg(TQString().setNum(audiocaps.flags))); + if ((audiocaps.flags & VIDEO_AUDIO_MUTABLE) != 0) + c.hasMute = true; + if ((audiocaps.flags & VIDEO_AUDIO_VOLUME) != 0) + c.setVolume (0, 65535); + if ((audiocaps.flags & VIDEO_AUDIO_TREBLE) != 0) + c.setTreble (0, 65535); + if ((audiocaps.flags & VIDEO_AUDIO_BASS) != 0) + c.setBass (0, 65535); + // at least my driver has support for balance, but the bit is not set ... + c.setBalance(0, 65535); + } + } else { + logError("V4LRadio::readV4LCaps: " + + i18n("error reading V4L1 caps")); + } + +#ifdef HAVE_V4L2 + v4l2_capability caps2; + r = ioctl(fd, VIDIOC_QUERYCAP, &caps2); + if (r == 0) { + c.version = 2; + + logDebug(i18n("V4L2 - Version: %1").arg(TQString().sprintf("%08X", caps2.version))); + + size_t l = sizeof(caps.name); + l = l < CAPS_NAME_LEN ? l : CAPS_NAME_LEN; + memcpy(buffer, caps.name, l); + buffer[l] = 0; + // c.description = buffer; + + v4l2_queryctrl ctrl; + + c.hasMute = false; + c.unsetVolume(); + c.unsetTreble(); + c.unsetBass(); + c.unsetBalance(); + + ctrl.id = V4L2_CID_AUDIO_MUTE; + if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) + c.hasMute = !(ctrl.flags & V4L2_CTRL_FLAG_DISABLED); + else + logError(i18n("V4L2: Querying mute control failed")); + + ctrl.id = V4L2_CID_AUDIO_VOLUME; + if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) { + if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + c.setVolume(ctrl.minimum, ctrl.maximum); + } else { + logError(i18n("V4L2: Querying volume control failed")); + } + + ctrl.id = V4L2_CID_AUDIO_TREBLE; + if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) { + if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + c.setTreble(ctrl.minimum, ctrl.maximum); + } else { + logError(i18n("V4L2: Querying treble control failed")); + } + + ctrl.id = V4L2_CID_AUDIO_BASS; + if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) { + if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + c.setBass(ctrl.minimum, c.maxBass = ctrl.maximum); + } else { + logError(i18n("V4L2: Querying bass control failed")); + } + + ctrl.id = V4L2_CID_AUDIO_BALANCE; + if (0 == ioctl(fd, VIDIOC_QUERYCTRL, &ctrl)) { + if (!(ctrl.flags & V4L2_CTRL_FLAG_DISABLED)) + c.setBalance(ctrl.minimum, ctrl.maximum); + } else { + logError(i18n("V4L2: Querying balance control failed")); + } + + } else { + logWarning(i18n("V4LRadio::readV4LCaps: Reading V4L2 caps failed")); + } +#endif + if (c.version > 0) { + logInfo(i18n("V4L %1 detected").arg(c.version)); + } else { + logError(i18n("V4L not detected")); + } + + logInfo(c.hasMute ? i18n("Radio is mutable") : i18n("Radio is not mutable")); + logInfo(c.hasVolume ? i18n("Radio has Volume Control") : i18n("Radio has no Volume Control")); + logInfo(c.hasBass ? i18n("Radio has Bass Control") : i18n("Radio has no Bass Control")); + logInfo(c.hasTreble ? i18n("Radio has Treble Control") : i18n("Radio has no Treble Control")); + + close(fd); + return c; +} + + +bool V4LRadio::readTunerInfo() const +{ + if (m_blockReadTuner) return true; + + float oldq = m_signalQuality; + float oldminf = m_tunercache.minF; + float oldmaxf = m_tunercache.maxF; + + if (!m_tunercache.valid) { + m_tunercache.minF = m_lastMinDevFrequency; + m_tunercache.maxF = m_lastMaxDevFrequency; + m_tunercache.deltaF = 1.0/16.0; + m_tunercache.valid = true; + } + + int r = 0; + if (isPowerOn()) { + + // v4l1 + if (m_caps.version == 1) { + + r = ioctl(m_radio_fd, VIDIOCGTUNER, m_tuner); + + if (r == 0) { + if ((m_tuner->flags & VIDEO_TUNER_LOW) != 0) + m_tunercache.deltaF = 1.0 / 16000.0; + m_tunercache.minF = float(m_tuner->rangelow) * m_tunercache.deltaF; + m_tunercache.maxF = float(m_tuner->rangehigh) * m_tunercache.deltaF; + m_tunercache.valid = true; + m_signalQuality = float(m_tuner->signal) / 32767.0; + + } + } +#ifdef HAVE_V4L2 + // v4l2 + else if (m_caps.version == 2) { + + r = ioctl(m_radio_fd, VIDIOC_G_TUNER, m_tuner2); + + if (r == 0) { + if ((m_tuner2->capability & V4L2_TUNER_CAP_LOW) != 0) + m_tunercache.deltaF = 1.0 / 16000.0; + m_tunercache.minF = float(m_tuner2->rangelow) * m_tunercache.deltaF; + m_tunercache.maxF = float(m_tuner2->rangehigh) * m_tunercache.deltaF; + m_tunercache.valid = true; + m_signalQuality = float(m_tuner2->signal) / 32767.0; + } + } +#endif + else { + logError("V4LRadio::readTunerInfo: " + + i18n("don't known how to handle V4L-version %1") + .arg(TQString().setNum(m_caps.version))); + } + + if (r != 0) { + m_signalQuality = 0; + logError("V4LRadio::readTunerInfo: " + + i18n("cannot get tuner info (error %1)").arg(TQString().setNum(r))); + } + } else { + m_signalQuality = 0; + } + + // prevent loops, if noticeXYZ-method is reading my state + m_blockReadTuner = true; + + if (oldminf != m_tunercache.minF || oldmaxf != m_tunercache.maxF) + notifyDeviceMinMaxFrequencyChanged(m_tunercache.minF, m_tunercache.maxF); + m_lastMinDevFrequency = m_tunercache.minF; + m_lastMaxDevFrequency = m_tunercache.maxF; + + if ( ! m_minFrequency && (oldminf != m_tunercache.minF) + || ! m_maxFrequency && (oldmaxf != m_tunercache.maxF)) + notifyMinMaxFrequencyChanged(getMinFrequency(), getMaxFrequency()); + + + if (m_signalQuality != oldq) + notifySignalQualityChanged(m_SoundStreamID, m_signalQuality); + if ( (m_signalQuality >= m_minQuality) != (oldq >= m_minQuality)) + notifySignalQualityBoolChanged(m_SoundStreamID, m_signalQuality > m_minQuality); + + m_blockReadTuner = false; + + return true; +} + + + +#define V4L2_S_CTRL(what,val) \ + { ctl.value = (val); \ + ctl.id = (what); \ + /* Problem: Current V4L2 development has changed the IOCTL-IDs for VIDIOC_S_CTRL */ \ + /* => we must du "try and error" to figure out what version we should use */ \ + r = ioctl (m_radio_fd, m_useOldV4L2Calls ? VIDIOC_S_CTRL_OLD : VIDIOC_S_CTRL, &ctl); \ + /* in case this did not work, try the other version of the call */ \ + if (r) { \ + r = ioctl (m_radio_fd, !m_useOldV4L2Calls ? VIDIOC_S_CTRL_OLD : VIDIOC_S_CTRL, &ctl); \ + if (!r) m_useOldV4L2Calls = !m_useOldV4L2Calls; \ + } \ + x = x ? x : r; \ + if (r) \ + logError(i18n("error setting %1: %2").arg(#what).arg(TQString().setNum(r))); \ + } + +#define V4L2_G_CTRL(what) \ + { ctl.id = (what); \ + r = ioctl (m_radio_fd, VIDIOC_G_CTRL, &ctl); \ + x = x ? x : r; \ + if (r) \ + logError(i18n("error reading %1: %2").arg(#what).arg(TQString().setNum(r))); \ + } + + +bool V4LRadio::updateAudioInfo(bool write) const +{ + if (m_blockReadAudio && !write) + return true; + + bool oldStereo = m_stereo; + bool oldMute = m_muted; + int iOldDeviceVolume = m_caps.intGetVolume (m_deviceVolume); + int iOldTreble = m_caps.intGetTreble (m_treble); + int iOldBass = m_caps.intGetBass (m_bass); + int iOldBalance = m_caps.intGetBalance(m_balance); + + if (isPowerOn()) { + int r = 0; + if (m_caps.version == 1) { + m_audio->audio = 0; + if (m_muted) m_audio->flags |= VIDEO_AUDIO_MUTE; + else m_audio->flags &= ~VIDEO_AUDIO_MUTE; + + m_audio->volume = m_caps.intGetVolume (m_deviceVolume); + m_audio->treble = m_caps.intGetTreble (m_treble); + m_audio->bass = m_caps.intGetBass (m_bass); + m_audio->balance = m_caps.intGetBalance(m_balance); + + r = ioctl(m_radio_fd, write ? VIDIOCSAUDIO : VIDIOCGAUDIO, m_audio); + + m_stereo = (r == 0) && ((m_audio->mode & VIDEO_SOUND_STEREO) != 0); + + m_muted = m_caps.hasMute && + ((r != 0) || ((m_audio->flags & VIDEO_AUDIO_MUTE) != 0)); + + /* Some drivers seem to set volumes to zero if they are muted. + Thus we do not reload them if radio is muted */ + if (!m_muted && !write) { + m_deviceVolume = m_caps.hasVolume && !r ? m_caps.floatGetVolume (m_audio->volume) : 1; + m_treble = m_caps.hasTreble && !r ? m_caps.floatGetTreble (m_audio->treble) : 1; + m_bass = m_caps.hasBass && !r ? m_caps.floatGetBass (m_audio->bass) : 1; + m_balance = m_caps.hasBalance && !r ? m_caps.floatGetBalance(m_audio->balance) : 0; + } + } +#ifdef HAVE_V4L2 + else if (m_caps.version == 2) { + v4l2_control ctl; + int x = 0; // x stores first ioctl error + if (write) { + if (m_caps.hasMute) + V4L2_S_CTRL(V4L2_CID_AUDIO_MUTE, m_muted); + if (m_caps.hasTreble) + V4L2_S_CTRL(V4L2_CID_AUDIO_TREBLE, m_caps.intGetTreble(m_treble)); + if (m_caps.hasBass) + V4L2_S_CTRL(V4L2_CID_AUDIO_BASS, m_caps.intGetBass(m_bass)); + if (m_caps.hasBalance) + V4L2_S_CTRL(V4L2_CID_AUDIO_BALANCE, m_caps.intGetBalance(m_balance)); + if (m_caps.hasVolume) + V4L2_S_CTRL(V4L2_CID_AUDIO_VOLUME, m_caps.intGetVolume(m_deviceVolume)); + } else { + if (m_caps.hasMute) + V4L2_G_CTRL(V4L2_CID_AUDIO_MUTE); + m_muted = m_caps.hasMute && ((r != 0) || ctl.value); + + /* Some drivers seem to set volumes to zero if they are muted. + Thus we do not reload them if radio is muted */ + if (!m_muted) { + if (m_caps.hasVolume) + V4L2_G_CTRL(V4L2_CID_AUDIO_VOLUME); + m_deviceVolume = m_caps.hasVolume && !r ? m_caps.floatGetVolume (ctl.value) : 1; + if (m_caps.hasTreble) + V4L2_G_CTRL(V4L2_CID_AUDIO_TREBLE); + m_treble = m_caps.hasTreble && !r ? m_caps.floatGetTreble (ctl.value) : 1; + if (m_caps.hasBass) + V4L2_G_CTRL(V4L2_CID_AUDIO_BASS); + m_bass = m_caps.hasBass && !r ? m_caps.floatGetBass (ctl.value) : 1; + if (m_caps.hasBalance) + V4L2_G_CTRL(V4L2_CID_AUDIO_BALANCE); + m_balance = m_caps.hasBalance&& !r ? m_caps.floatGetBalance(ctl.value) : 0; + } + + r = ioctl (m_radio_fd, VIDIOC_G_TUNER, m_tuner2); + m_stereo = (r == 0) && ((m_tuner2->rxsubchans & V4L2_TUNER_SUB_STEREO) != 0); + x = x ? x : r; + } + r = x; // store first error back to r, used below for error message + } +#endif + else { + logError("V4LRadio::updateAudioInfo: " + + i18n("don't known how to handle V4L-version %1") + .arg(TQString().setNum(m_caps.version))); + } + + if (r) { + logError("V4LRadio::updateAudioInfo: " + + i18n("error updating radio audio info (%1): %2") + .arg(write ? i18n("write") : i18n("read")) + .arg(TQString().setNum(r))); + return false; + } + } + + // prevent loops, if noticeXYZ-method is reading my state + bool oldBlock = m_blockReadAudio; + m_blockReadAudio = true; + + // send notifications + + if (oldStereo != m_stereo) + notifyStereoChanged(m_SoundStreamID, m_stereo); + if (oldMute != m_muted) + notifyMuted(m_SoundStreamID, m_muted); + if (iOldDeviceVolume != m_caps.intGetVolume(m_deviceVolume)) + notifyDeviceVolumeChanged(m_deviceVolume); + if (iOldTreble != m_caps.intGetTreble(m_treble)) + notifyTrebleChanged(m_SoundStreamID, m_treble); + if (iOldBass != m_caps.intGetBass(m_bass)) + notifyBassChanged(m_SoundStreamID, m_bass); + if (iOldBalance != m_caps.intGetBalance(m_balance)) + notifyBalanceChanged(m_SoundStreamID, m_balance); + + m_blockReadAudio = oldBlock; + + return isPowerOn(); +} + + + + +void V4LRadio::poll() +{ + readTunerInfo(); + readAudioInfo(); +} + + +bool V4LRadio::setPlaybackVolume(SoundStreamID id, float volume) +{ + if (isPowerOff() && id == m_SoundStreamID) { + m_defaultPlaybackVolume = min(max(volume, 0.0), 1.0); + return true; + } else { + return false; + } +} + +bool V4LRadio::getPlaybackVolume(SoundStreamID id, float &volume) const +{ + if (isPowerOff() && id == m_SoundStreamID) { + volume = m_defaultPlaybackVolume; + return true; + } else { + return false; + } +} + + + +bool V4LRadio::getSoundStreamDescription(SoundStreamID id, TQString &descr) const +{ + if (id == m_SoundStreamID) { + descr = name() + " - " + m_currentStation.name(); + return true; + } + else { + return false; + } +} + + +bool V4LRadio::getSoundStreamRadioStation(SoundStreamID id, const RadioStation *&rs) const +{ + if (id == m_SoundStreamID) { + rs = &m_currentStation; + return true; + } + else { + return false; + } +} + + +bool V4LRadio::enumerateSoundStreams(TQMap<TQString, SoundStreamID> &list) const +{ + if (m_SoundStreamID.isValid()) { + TQString tmp = TQString(); + getSoundStreamDescription(m_SoundStreamID, tmp); + list[tmp] = m_SoundStreamID; + return true; + } + return false; +} + + +// bool V4LRadio::stopCapture(SoundStreamID id) +// { +// if (id.isValid() && id == m_SoundStreamID && m_ActivePlayback) { +// sendStopPlayback(id); +// return true; +// } +// return false; +// } + +#include "v4lradio.moc" |