/*************************************************************************** oss-sound.cpp - description ------------------- begin : Sun Mar 21 2004 copyright : (C) 2004 by 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. * * * ***************************************************************************/ #include "oss-sound.h" #include "../../src/include/aboutwidget.h" #include #include #include #include #include #include #include #include #include #include #include "oss-sound-configuration.h" #include "../../src/include/utils.h" /////////////////////////////////////////////////////////////////////// //// plugin library functions PLUGIN_LIBRARY_FUNCTIONS(OSSSoundDevice, "tderadio-oss-sound", i18n("Open Sound System (OSS) Support")); ///////////////////////////////////////////////////////////////////////////// struct _lrvol { unsigned char l, r; short dummy; }; OSSSoundDevice::OSSSoundDevice(const TQString &name) : TQObject(NULL, NULL), PluginBase(name, i18n("TDERadio OSS Sound Plugin")), m_DSPDeviceName(""), m_MixerDeviceName(""), m_DSP_fd(-1), m_Mixer_fd(-1), m_DuplexMode(DUPLEX_UNKNOWN), m_DSPFormat(), m_PassivePlaybackStreams(), m_PlaybackStreamID(), m_CaptureStreamID(), m_BufferSize(65536), m_PlaybackBuffer(m_BufferSize), m_CaptureBuffer(m_BufferSize), m_CaptureRequestCounter(0), m_CapturePos(0), m_CaptureStartTime(0), //m_PlaybackSkipCount(0), m_CaptureSkipCount(0), m_EnablePlayback(true), m_EnableCapture(true) { TQObject::connect(&m_PollingTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotPoll())); } OSSSoundDevice::~OSSSoundDevice() { stopCapture(m_CaptureStreamID); stopPlayback(m_PlaybackStreamID); closeDSPDevice(); closeMixerDevice(); } bool OSSSoundDevice::connectI(Interface *i) { bool a = PluginBase::connectI(i); bool b = ISoundStreamClient::connectI(i); return a || b; } bool OSSSoundDevice::disconnectI(Interface *i) { bool a = PluginBase::disconnectI(i); bool b = ISoundStreamClient::disconnectI(i); return a || b; } void OSSSoundDevice::noticeConnectedI (ISoundStreamServer *s, bool pointer_valid) { ISoundStreamClient::noticeConnectedI(s, pointer_valid); if (s && pointer_valid) { s->register4_sendReleasePlayback(this); s->register4_sendReleaseCapture(this); s->register4_sendPlaybackVolume(this); s->register4_sendCaptureVolume(this); s->register4_queryPlaybackVolume(this); s->register4_queryCaptureVolume(this); s->register4_sendStartPlayback(this); s->register4_sendPausePlayback(this); s->register4_sendStopPlayback(this); s->register4_queryIsPlaybackRunning(this); s->register4_sendStartCaptureWithFormat(this); s->register4_sendStopCapture(this); s->register4_queryIsCaptureRunning(this); s->register4_notifySoundStreamClosed(this); s->register4_notifySoundStreamRedirected(this); s->register4_notifySoundStreamData(this); } } // PluginBase void OSSSoundDevice::saveState (TDEConfig *c) const { c->setGroup(TQString("oss-sound-") + PluginBase::name()); c->writeEntry("dsp-device", m_DSPDeviceName); c->writeEntry("mixer-device", m_MixerDeviceName); c->writeEntry("enable-playback", m_EnablePlayback); c->writeEntry("enable-capture", m_EnableCapture); c->writeEntry("buffer-size", m_BufferSize); c->writeEntry("soundstreamclient-id", m_SoundStreamClientID); } void OSSSoundDevice::restoreState (TDEConfig *c) { c->setGroup(TQString("oss-sound-") + PluginBase::name()); m_EnablePlayback = c->readBoolEntry("enable-playback", true); m_EnableCapture = c->readBoolEntry("enable-capture", true); m_BufferSize = c->readNumEntry ("buffer-size", 65536); setDSPDeviceName (c->readEntry ("dsp-device", "/dev/dsp")); setMixerDeviceName (c->readEntry ("mixer-device", "/dev/mixer")); m_PlaybackBuffer.resize(m_BufferSize); m_CaptureBuffer.resize(m_BufferSize); setSoundStreamClientID(c->readEntry("soundstreamclient-id", getSoundStreamClientID())); emit sigUpdateConfig(); } void OSSSoundDevice::setMixerDeviceName(const TQString &dev_name) { if (m_MixerDeviceName != dev_name) { m_MixerDeviceName = dev_name; if (m_Mixer_fd >= 0) openMixerDevice(true); getMixerChannels(SOUND_MIXER_DEVMASK, m_PlaybackChannels, m_revPlaybackChannels); getMixerChannels(SOUND_MIXER_RECMASK, m_CaptureChannels, m_revCaptureChannels); notifyPlaybackChannelsChanged(m_SoundStreamClientID, m_PlaybackChannels); notifyCaptureChannelsChanged(m_SoundStreamClientID, m_CaptureChannels); } } ConfigPageInfo OSSSoundDevice::createConfigurationPage() { OSSSoundConfiguration *conf = new OSSSoundConfiguration(NULL, this); TQObject::connect(this, TQT_SIGNAL(sigUpdateConfig()), conf, TQT_SLOT(slotUpdateConfig())); return ConfigPageInfo (conf, i18n("OSS Sound"), i18n("OSS Sound Device Options"), "tderadio_oss"); } AboutPageInfo OSSSoundDevice::createAboutPage() { /* TDEAboutData aboutData("tderadio", NULL, NULL, I18N_NOOP("OSS Sound Plugin for TDERadio"), TDEAboutData::License_GPL, "(c) 2004 Martin Witte", 0, "http://sourceforge.net/projects/tderadio", 0); aboutData.addAuthor("Martin Witte", "", "witte@kawo1.rwth-aachen.de"); return AboutPageInfo( new TDERadioAboutWidget(aboutData, TDERadioAboutWidget::AbtTabbed), i18n("OSS Sound"), i18n("OSS Sound"), "tderadio_oss_sound" ); */ return AboutPageInfo(); } bool OSSSoundDevice::preparePlayback(SoundStreamID id, const TQString &channel, bool active_mode, bool start_immediately) { if (id.isValid() && m_revPlaybackChannels.contains(channel)) { m_PlaybackStreams.insert(id, SoundStreamConfig(m_revPlaybackChannels[channel], active_mode)); if (start_immediately) startPlayback(id); return true; // FIXME: what to do if stream is already playing? } return false; } bool OSSSoundDevice::prepareCapture(SoundStreamID id, const TQString &channel) { if (id.isValid() && m_revCaptureChannels.contains(channel)) { m_CaptureStreams.insert(id, SoundStreamConfig(m_revCaptureChannels[channel])); return true; // FIXME: what to do if stream is already playing? } return false; } bool OSSSoundDevice::releasePlayback(SoundStreamID id) { if (id.isValid() && m_PlaybackStreams.contains(id)) { if (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id)) { stopPlayback(id); } m_PlaybackStreams.remove(id); return true; } return false; } bool OSSSoundDevice::releaseCapture(SoundStreamID id) { if (id.isValid() && m_CaptureStreams.contains(id)) { if (m_CaptureStreamID == id) { stopCapture(id); } m_CaptureStreams.remove(id); return true; } return false; } bool OSSSoundDevice::supportsPlayback() const { return m_EnablePlayback; } bool OSSSoundDevice::supportsCapture() const { return m_EnableCapture; } bool OSSSoundDevice::startPlayback(SoundStreamID id) { if (id.isValid() && m_PlaybackStreams.contains(id) && m_EnablePlayback) { SoundStreamConfig &cfg = m_PlaybackStreams[id]; bool ok = false; if (cfg.m_ActiveMode) { if (!m_PlaybackStreamID.isValid()) { m_PlaybackStreamID = id; ok = true; } } else { if (!m_PassivePlaybackStreams.contains(id)) m_PassivePlaybackStreams.append(id); ok = true; } if (ok) { openMixerDevice(); if (cfg.m_Volume >= 0) writeMixerVolume(cfg.m_Channel, cfg.m_Volume); } // error handling? return true; } else { return false; } } bool OSSSoundDevice::pausePlayback(SoundStreamID /*id*/) { //return stopPlayback(id); return false; } bool OSSSoundDevice::stopPlayback(SoundStreamID id) { if (id.isValid() && m_PlaybackStreams.contains(id)) { SoundStreamConfig &cfg = m_PlaybackStreams[id]; if (!cfg.m_ActiveMode) { if (m_PassivePlaybackStreams.contains(id)) { // writeMixerVolume(cfg.m_Channel, 0); m_PassivePlaybackStreams.remove(id); } } else if (m_PlaybackStreamID == id) { m_PlaybackStreamID = SoundStreamID::InvalidID; m_PlaybackBuffer.clear(); closeDSPDevice(); } closeMixerDevice(); return true; } else { return false; } } bool OSSSoundDevice::isPlaybackRunning(SoundStreamID id, bool &b) const { if (id.isValid() && m_PlaybackStreams.contains(id)) { b = true; return true; } else { return false; } } bool OSSSoundDevice::startCaptureWithFormat(SoundStreamID id, const SoundFormat &proposed_format, SoundFormat &real_format, bool force_format) { if (m_CaptureStreams.contains(id) && m_EnableCapture) { if (m_CaptureStreamID != id) { m_CapturePos = 0; m_CaptureStartTime = time(NULL); } if (m_CaptureStreamID != id || force_format) { m_CaptureStreamID = id; SoundStreamConfig &cfg = m_CaptureStreams[id]; openMixerDevice(); selectCaptureChannel(cfg.m_Channel); if (cfg.m_Volume >= 0) writeMixerVolume(cfg.m_Channel, cfg.m_Volume); openDSPDevice(proposed_format); // FIXME: error handling? } real_format = m_DSPFormat; m_CaptureRequestCounter++; return true; } else { return false; } } bool OSSSoundDevice::stopCapture(SoundStreamID id) { if (id.isValid() && m_CaptureStreamID == id) { if (--m_CaptureRequestCounter == 0) { m_CaptureStreamID = SoundStreamID::InvalidID; m_CaptureBuffer.clear(); closeMixerDevice(); closeDSPDevice(); } return true; } else { return false; } } bool OSSSoundDevice::isCaptureRunning(SoundStreamID id, bool &b, SoundFormat &sf) const { if (id.isValid() && m_CaptureStreamID == id) { b = true; sf = m_DSPFormat; return true; } else { return false; } } bool OSSSoundDevice::noticeSoundStreamClosed(SoundStreamID id) { bool found = false; if (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id)) { stopPlayback(id); found = true; } if (m_CaptureStreamID == id) { stopCapture(id); found = true; } m_PlaybackStreams.remove(id); m_CaptureStreams.remove(id); return found; } bool OSSSoundDevice::noticeSoundStreamRedirected(SoundStreamID oldID, SoundStreamID newID) { bool found = false; if (m_PlaybackStreams.contains(oldID)) { m_PlaybackStreams.insert(newID, m_PlaybackStreams[oldID]); if (newID != oldID) m_PlaybackStreams.remove(oldID); found = true; } if (m_CaptureStreams.contains(oldID)) { m_CaptureStreams.insert(newID, m_CaptureStreams[oldID]); if (newID != oldID) m_CaptureStreams.remove(oldID); found = true; } if (m_PlaybackStreamID == oldID) m_PlaybackStreamID = newID; if (m_CaptureStreamID == oldID) m_CaptureStreamID = newID; if (m_PassivePlaybackStreams.contains(oldID)) { m_PassivePlaybackStreams.remove(oldID); m_PassivePlaybackStreams.append(newID); } return found; } bool OSSSoundDevice::noticeSoundStreamData(SoundStreamID id, const SoundFormat &format, const char *data, size_t size, size_t &consumed_size, const SoundMetaData &/*md*/ ) { if (!id.isValid() || id != m_PlaybackStreamID) return false; if (m_DSP_fd < 0) { openDSPDevice(format); } else if (format != m_DSPFormat) { if (m_CaptureStreamID.isValid()) return false; // flush playback buffer size_t buffersize = 0; char *buffer = m_PlaybackBuffer.getData(buffersize); write(m_DSP_fd, buffer, buffersize); // if not all could be written, it must be discarded m_PlaybackBuffer.clear(); closeDSPDevice(); openDSPDevice(format); // error handling ? } size_t n = m_PlaybackBuffer.addData(data, size); consumed_size = (consumed_size == SIZE_T_DONT_CARE) ? n : min(consumed_size, n); // if (n < size) { // m_PlaybackSkipCount += size - n; // } else if (m_PlaybackSkipCount > 0) { // logWarning(i18n("%1: Playback buffer overflow. Skipped %1 bytes").arg(m_DSPDeviceName).arg(TQString::number(m_PlaybackSkipCount))); // m_PlaybackSkipCount = 0; // } return true; //m_PlaybackSkipCount == 0; } void OSSSoundDevice::slotPoll() { int err = 0; if (m_CaptureStreamID.isValid() && m_DSP_fd >= 0) { size_t bufferSize = 0; char *buffer = m_CaptureBuffer.getFreeSpace(bufferSize); int bytesRead = read(m_DSP_fd, buffer, bufferSize); if (bytesRead > 0) { m_CaptureBuffer.removeFreeSpace(bytesRead); } else if (bytesRead < 0 && errno == EAGAIN) { bytesRead = 0; } else if (bytesRead == 0) { err = -1; logError(i18n("OSS device %1: No data to record").arg(m_DSPDeviceName)); } else { err = errno; } while (m_CaptureBuffer.getFillSize() > m_CaptureBuffer.getSize() / 3) { size_t size = 0; buffer = m_CaptureBuffer.getData(size); time_t cur_time = time(NULL); size_t consumed_size = SIZE_T_DONT_CARE; notifySoundStreamData(m_CaptureStreamID, m_DSPFormat, buffer, size, consumed_size, SoundMetaData(m_CapturePos, cur_time - m_CaptureStartTime, cur_time, i18n("internal stream, not stored (%1)").arg(m_DSPDeviceName))); if (consumed_size == SIZE_T_DONT_CARE) consumed_size = size; m_CaptureBuffer.removeData(consumed_size); m_CapturePos += consumed_size; if (consumed_size < size) break; } } if (m_PlaybackStreamID.isValid()/* && m_DSP_fd >= 0*/) { if (m_PlaybackBuffer.getFillSize() > 0 && m_DSP_fd >= 0) { size_t buffersize = 0; char *buffer = m_PlaybackBuffer.getData(buffersize); int bytesWritten = write(m_DSP_fd, buffer, buffersize); if (bytesWritten > 0) { m_PlaybackBuffer.removeData(bytesWritten); } else if (bytesWritten < 0 && errno == EAGAIN) { bytesWritten = 0; } else { err = errno; } } if (m_PlaybackBuffer.getFreeSize() > 0) notifyReadyForPlaybackData(m_PlaybackStreamID, m_PlaybackBuffer.getFreeSize()); } if (err) { logError(i18n("Error %1 while handling OSS device %2").arg(TQString().setNum(err)).arg(m_DSPDeviceName)); } if (m_PlaybackStreamID.isValid()) checkMixerVolume(m_PlaybackStreamID); if (m_CaptureStreamID.isValid()) checkMixerVolume(m_CaptureStreamID); TQValueListConstIterator end = m_PassivePlaybackStreams.end(); for (TQValueListConstIterator it = m_PassivePlaybackStreams.begin(); it != end; ++it) checkMixerVolume(*it); } bool OSSSoundDevice::openDSPDevice(const SoundFormat &format, bool reopen) { if (m_DSP_fd >= 0) { if (reopen) { closeDSPDevice ( /* force = */ true); } else { if (format != m_DSPFormat) return false; if (m_DuplexMode != DUPLEX_FULL && m_CaptureStreamID.isValid() && m_PlaybackStreamID.isValid()) return false; return true; } } else { if (reopen) return true; } m_DSPFormat = format; // first testopen for CAPS m_DSP_fd = open(m_DSPDeviceName.ascii(), O_NONBLOCK | O_RDONLY); bool err = m_DSP_fd < 0; if (err) { logError(i18n("Cannot open DSP device %1").arg(m_DSPDeviceName)); return false; } int caps = 0; err |= (ioctl (m_DSP_fd, SNDCTL_DSP_GETCAPS, &caps) != 0); if (err) logError(i18n("Cannot read DSP capabilities for %1").arg(m_DSPDeviceName)); m_DuplexMode = (caps & DSP_CAP_DUPLEX) ? DUPLEX_FULL : DUPLEX_HALF; close (m_DSP_fd); m_DSP_fd = -1; // opening and seeting up the device file int mode = O_NONBLOCK; if (m_DuplexMode == DUPLEX_FULL) { mode |= O_RDWR; } else if (m_CaptureStreamID.isValid()) { mode |= O_RDONLY; } else { mode |= O_WRONLY; } m_DSP_fd = open(m_DSPDeviceName.ascii(), mode); err = m_DSP_fd < 0; if (err) { logError(i18n("Cannot open DSP device %1").arg(m_DSPDeviceName)); return false; } int oss_format = getOSSFormat(m_DSPFormat); err |= (ioctl(m_DSP_fd, SNDCTL_DSP_SETFMT, &oss_format) != 0); if (err) logError(i18n("Cannot set DSP sample format for %1").arg(m_DSPDeviceName)); int channels = m_DSPFormat.m_Channels; err |= (ioctl(m_DSP_fd, SNDCTL_DSP_CHANNELS, &channels) != 0); if (err) logError(i18n("Cannot set number of channels for %1").arg(m_DSPDeviceName)); int rate = m_DSPFormat.m_SampleRate; err |= (ioctl(m_DSP_fd, SNDCTL_DSP_SPEED, &rate) != 0); if (err) logError(i18n("Cannot set sampling rate for %1").arg(m_DSPDeviceName)); if (rate != (int)m_DSPFormat.m_SampleRate) { logWarning(i18n("Asking for %1 Hz but %2 uses %3 Hz"). arg(TQString::number(m_DSPFormat.m_SampleRate)). arg(m_DSPDeviceName). arg(TQString::number(rate))); m_DSPFormat.m_SampleRate = rate; } int stereo = m_DSPFormat.m_Channels == 2; err |= (ioctl(m_DSP_fd, SNDCTL_DSP_STEREO, &stereo) != 0); if (err) logError(i18n("Cannot set stereo mode for %1").arg(m_DSPDeviceName)); unsigned sampleSize = m_DSPFormat.m_SampleBits; err |= (ioctl(m_DSP_fd, SNDCTL_DSP_SAMPLESIZE, &sampleSize) != 0); if (err || sampleSize != m_DSPFormat.m_SampleBits) logError(i18n("Cannot set sample size for %1").arg(m_DSPDeviceName)); // setup buffer, ask for 40ms latency int tmp = (400 * m_DSPFormat.frameSize() * m_DSPFormat.m_SampleRate) / 1000; int mask = -1; for (; tmp; tmp >>= 1) ++mask; if (mask < 8) mask = 12; // default 4kB mask |= 0x7FFF0000; err |= ioctl (m_DSP_fd, SNDCTL_DSP_SETFRAGMENT, &mask); if (err) logError(i18n("Cannot set buffers for %1").arg(m_DSPDeviceName)); int bufferBlockSize = 0; err |= ioctl (m_DSP_fd, SNDCTL_DSP_GETBLKSIZE, &bufferBlockSize); if (err) { logError(i18n("Cannot read buffer size for %1").arg(m_DSPDeviceName)); } else { logInfo(i18n("%1 uses buffer blocks of %2 bytes").arg(m_DSPDeviceName).arg(TQString::number(bufferBlockSize))); size_t tmp = (((m_BufferSize - 1) / bufferBlockSize) + 1) * bufferBlockSize; setBufferSize(tmp); logInfo(i18n("adjusted own buffer size to %1 bytes").arg(TQString::number(tmp))); } int trigger = ~PCM_ENABLE_INPUT & ~PCM_ENABLE_OUTPUT; ioctl(m_DSP_fd, SNDCTL_DSP_SETTRIGGER, &trigger); trigger = PCM_ENABLE_INPUT | PCM_ENABLE_OUTPUT; ioctl(m_DSP_fd, SNDCTL_DSP_SETTRIGGER, &trigger); if (!err) { m_PollingTimer.start(40); } else { closeDSPDevice(); } m_CaptureSkipCount = 0; //m_PlaybackSkipCount = 0; return !err; } bool OSSSoundDevice::closeDSPDevice(bool force) { if ((!m_PlaybackStreamID.isValid() && !m_CaptureStreamID.isValid()) || force) { if (m_Mixer_fd < 0) m_PollingTimer.stop(); if (m_DSP_fd >= 0) close (m_DSP_fd); m_DSP_fd = -1; m_PlaybackBuffer.clear(); m_CaptureBuffer.clear(); } return true; } bool OSSSoundDevice::openMixerDevice(bool reopen) { if (reopen) { if (m_Mixer_fd >= 0) closeMixerDevice(/* force = */ true); else return true; } if (m_Mixer_fd < 0) m_Mixer_fd = open(m_MixerDeviceName.ascii(), O_RDONLY); if (m_Mixer_fd < 0) { logError(i18n("Cannot open mixer device %1").arg(m_MixerDeviceName)); } else { m_PollingTimer.start(40); } return m_Mixer_fd >= 0; } bool OSSSoundDevice::closeMixerDevice(bool force) { if ((!m_PlaybackStreamID.isValid() && !m_CaptureStreamID.isValid()) || force) { if (m_DSP_fd < 0) m_PollingTimer.stop(); if (m_Mixer_fd >= 0) close (m_Mixer_fd); m_Mixer_fd = -1; } return m_Mixer_fd < 0; } void OSSSoundDevice::getMixerChannels(int query, TQStringList &retval, TQMap &revmap) const { retval.clear(); revmap.clear(); int fd = m_Mixer_fd; if (fd < 0) fd = open(m_MixerDeviceName.ascii(), O_RDONLY); if (fd < 0) { logError(i18n("OSSSoundDevice::getMixerChannels: Cannot open mixer device %1").arg(m_MixerDeviceName)); } if (fd >= 0) { int mask = 0; if ( ioctl(fd, MIXER_READ(query), &mask) == 0 ) { for (int i = 0; i < SOUND_MIXER_NRDEVICES; ++i) { if (mask & (1 << i)) { static const char *labels[] = SOUND_DEVICE_LABELS; retval.append(i18n(labels[i])); revmap.insert(i18n(labels[i]), i); } } } else { logError(i18n("OSSSoundDevice::getMixerChannels: Cannot read mixer device mask on device %1").arg(m_MixerDeviceName)); } } if (fd != m_Mixer_fd) close(fd); } const TQStringList &OSSSoundDevice::getPlaybackChannels() const { return m_PlaybackChannels; } const TQStringList &OSSSoundDevice::getCaptureChannels() const { return m_CaptureChannels; } bool OSSSoundDevice::setPlaybackVolume(SoundStreamID id, float volume) { if (id.isValid() && (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id))) { SoundStreamConfig &cfg = m_PlaybackStreams[id]; if (rint(100*volume) != rint(100*cfg.m_Volume)) { cfg.m_Volume = writeMixerVolume(cfg.m_Channel, volume); notifyPlaybackVolumeChanged(id, cfg.m_Volume); } return true; } return false; } bool OSSSoundDevice::setCaptureVolume(SoundStreamID id, float volume) { if (id.isValid() && m_CaptureStreamID == id) { SoundStreamConfig &cfg = m_CaptureStreams[id]; if (rint(100*volume) != rint(100*cfg.m_Volume)) { cfg.m_Volume = writeMixerVolume(cfg.m_Channel, volume); notifyCaptureVolumeChanged(id, cfg.m_Volume); } return true; } return false; } bool OSSSoundDevice::getPlaybackVolume(SoundStreamID id, float &volume) const { if (id.isValid() && (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id))) { const SoundStreamConfig &cfg = m_PlaybackStreams[id]; volume = cfg.m_Volume; return true; } return false; } bool OSSSoundDevice::getCaptureVolume(SoundStreamID id, float &volume) const { if (id.isValid() && m_CaptureStreamID == id) { const SoundStreamConfig &cfg = m_CaptureStreams[id]; volume = cfg.m_Volume; return true; } return false; } void OSSSoundDevice::checkMixerVolume(SoundStreamID id) { if (m_Mixer_fd >= 0 && id.isValid()) { if (m_PassivePlaybackStreams.contains(id) || m_PlaybackStreamID == id) { SoundStreamConfig &cfg = m_PlaybackStreams[id]; float v = readMixerVolume(cfg.m_Channel); if (rint(100*cfg.m_Volume) != rint(100*v)) { cfg.m_Volume = v; notifyPlaybackVolumeChanged(id, v); } } if (m_CaptureStreamID == id) { SoundStreamConfig &cfg = m_CaptureStreams[id]; float v = readMixerVolume(cfg.m_Channel); if (rint(100*cfg.m_Volume) != rint(100*v)) { cfg.m_Volume = v; notifyCaptureVolumeChanged(id, v); } } } } float OSSSoundDevice::readMixerVolume(int channel) const { _lrvol tmpvol; int err = ioctl(m_Mixer_fd, MIXER_READ(channel), &tmpvol); if (err) { logError("OSSSound::readMixerVolume: " + i18n("error %1 while reading volume from %2") .arg(TQString().setNum(err)) .arg(m_MixerDeviceName)); tmpvol.l = tmpvol.r = 0; } return float(tmpvol.l) / 100.0; } float OSSSoundDevice::writeMixerVolume (int channel, float vol) { if (vol > 1.0) vol = 1.0; if (vol < 0) vol = 0.0; const int divs = 100; vol = rint(vol * divs) / float(divs); if (m_Mixer_fd >= 0) { _lrvol tmpvol; tmpvol.r = tmpvol.l = (unsigned int)(rint(vol * divs)); int err = ioctl(m_Mixer_fd, MIXER_WRITE(channel), &tmpvol); if (err != 0) { logError("OSSSoundDevice::writeMixerVolume: " + i18n("error %1 while setting volume to %2 on device %3") .arg(TQString().setNum(err)) .arg(TQString().setNum(vol)) .arg(m_MixerDeviceName)); return -1; } } return vol; } void OSSSoundDevice::selectCaptureChannel (int channel) { int x = 1 << channel; int err = ioctl(m_Mixer_fd, SOUND_MIXER_WRITE_RECSRC, &x); if (err) logError(i18n("Selecting recording source on device %1 failed with error code %2") .arg(m_MixerDeviceName) .arg(TQString::number(err))); _lrvol tmpvol; err = ioctl(m_Mixer_fd, MIXER_READ(SOUND_MIXER_IGAIN), &tmpvol); if (err) logError(i18n("Reading igain volume on device %1 failed with error code %2") .arg(m_MixerDeviceName) .arg(TQString::number(err))); if (tmpvol.r == 0 && tmpvol.l == 0) { tmpvol.r = tmpvol.l = 1; err = ioctl(m_Mixer_fd, MIXER_WRITE(SOUND_MIXER_IGAIN), &tmpvol); if (err) logError(i18n("Setting igain volume on device %1 failed with error code %2") .arg(m_MixerDeviceName) .arg(TQString::number(err))); } } int OSSSoundDevice::getOSSFormat(const SoundFormat &f) { if (f.m_SampleBits == 16) { switch (2 * f.m_IsSigned + (f.m_Endianess == LITTLE_ENDIAN)) { case 0: return AFMT_U16_BE; case 1: return AFMT_U16_LE; case 2: return AFMT_S16_BE; case 3: return AFMT_S16_LE; } } if (f.m_SampleBits == 8) { switch (f.m_IsSigned) { case 0: return AFMT_U8; case 1: return AFMT_S8; } } return 0; } void OSSSoundDevice::setBufferSize(int s) { m_BufferSize = s; m_PlaybackBuffer.resize(m_BufferSize); m_CaptureBuffer.resize(m_BufferSize); } void OSSSoundDevice::enablePlayback(bool on) { m_EnablePlayback = on; } void OSSSoundDevice::enableCapture(bool on) { m_EnableCapture = on; } void OSSSoundDevice::setDSPDeviceName(const TQString &s) { m_DSPDeviceName = s; SoundFormat f = m_DSPFormat; if (m_DSP_fd >= 0) openDSPDevice(f, /* reopen = */ true); } TQString OSSSoundDevice::getSoundStreamClientDescription() const { return i18n("OSS Sound Device %1").arg(PluginBase::name()); } #include "oss-sound.moc"