diff options
author | Michele Calgaro <michele.calgaro@yahoo.it> | 2022-05-30 19:40:31 +0900 |
---|---|---|
committer | Michele Calgaro <michele.calgaro@yahoo.it> | 2022-05-31 01:17:58 +0900 |
commit | d95a4fea540b371fa86493d069fdbd54f33c5b40 (patch) | |
tree | 079b038ab559439eb7ded40a07bd79fd92926b3b /plugins/oss-sound/oss-sound.cpp | |
parent | e54890e0480e5adee69f5220a7c6dd072bbd75ea (diff) | |
download | tderadio-d95a4fea540b371fa86493d069fdbd54f33c5b40.tar.gz tderadio-d95a4fea540b371fa86493d069fdbd54f33c5b40.zip |
Standardize folder structure.
Signed-off-by: Michele Calgaro <michele.calgaro@yahoo.it>
Diffstat (limited to 'plugins/oss-sound/oss-sound.cpp')
-rw-r--r-- | plugins/oss-sound/oss-sound.cpp | 991 |
1 files changed, 991 insertions, 0 deletions
diff --git a/plugins/oss-sound/oss-sound.cpp b/plugins/oss-sound/oss-sound.cpp new file mode 100644 index 0000000..00801c0 --- /dev/null +++ b/plugins/oss-sound/oss-sound.cpp @@ -0,0 +1,991 @@ +/*************************************************************************** + 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 <tdelocale.h> +#include <tdeaboutdata.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/soundcard.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <math.h> +#include <errno.h> + +#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<SoundStreamID> end = m_PassivePlaybackStreams.end(); + for (TQValueListConstIterator<SoundStreamID> 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<TQString, int> &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" |