diff options
Diffstat (limited to 'kradio3/plugins/alsa-sound')
27 files changed, 3802 insertions, 0 deletions
diff --git a/kradio3/plugins/alsa-sound/Makefile.am b/kradio3/plugins/alsa-sound/Makefile.am new file mode 100644 index 0000000..a00e32c --- /dev/null +++ b/kradio3/plugins/alsa-sound/Makefile.am @@ -0,0 +1,20 @@ +SUBDIRS = po icons . + +INCLUDES = $(all_includes) +METASOURCES = AUTO + +libkradio_LTLIBRARIES = libalsa-sound.la +libalsa_sound_la_LDFLAGS = -module -avoid-version $(KDE_RPATH) $(all_libraries) +noinst_HEADERS = alsa-sound.h alsa-sound-configuration.h alsa-mixer-element.h \ + alsa-config-mixer-setting.h +libalsa_sound_la_SOURCES = alsa-sound.cpp alsa-sound-configuration-ui.ui \ + alsa-sound-configuration.cpp alsa-mixer-element-ui.ui alsa-mixer-element.cpp \ + alsa-config-mixer-setting.cpp +libalsa_sound_la_LIBADD = $(LIB_ALSA) + +#messages: rc.cpp +# $(XGETTEXT) *.cpp *.h -o po/kradio-alsa-sound.pot + +messages: rc.cpp + $(EXTRACTRC) *.rc *.ui >> rc.cpp + $(XGETTEXT) rc.cpp *.h *.cpp -o po/kradio-alsa-sound.pot diff --git a/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.cpp b/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.cpp new file mode 100644 index 0000000..873b29e --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.cpp @@ -0,0 +1,67 @@ +/*************************************************************************** + alsa-config-mixer-setting.cpp - description + ------------------- + begin : Mon Aug 15 2005 + copyright : (C) 2005 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 "alsa-config-mixer-setting.h" + +#include <kconfig.h> + +AlsaConfigMixerSetting::AlsaConfigMixerSetting() + : m_card(-1), + m_name(QString::null), + m_use(false), + m_active(false), + m_volume(-1) +{ +} + +AlsaConfigMixerSetting::AlsaConfigMixerSetting(KConfig *c, const QString &prefix) +{ + m_card = c->readNumEntry (prefix+"card", -1); + m_name = c->readEntry (prefix+"name", QString::null); + m_use = c->readBoolEntry (prefix+"use", false); + m_active = c->readBoolEntry (prefix+"active", false); + m_volume = c->readDoubleNumEntry(prefix+"volume", 0); +} + +AlsaConfigMixerSetting::AlsaConfigMixerSetting(int card, const QString &name, bool use, bool active, float volume) + : m_card(card), + m_name(name), + m_use(use), + m_active(active), + m_volume(volume) +{ +} + +AlsaConfigMixerSetting::~AlsaConfigMixerSetting() +{ +} + +QString AlsaConfigMixerSetting::getIDString(int card, const QString &name) +{ + return QString::number(card) + "-" + name; +} + +void AlsaConfigMixerSetting::saveState(KConfig *c, const QString &prefix) const +{ + c->writeEntry(prefix+"card", m_card); + c->writeEntry(prefix+"name", m_name); + c->writeEntry(prefix+"use", m_use); + c->writeEntry(prefix+"active", m_active); + c->writeEntry(prefix+"volume", m_volume); +} + + diff --git a/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.h b/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.h new file mode 100644 index 0000000..a9f5d88 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-config-mixer-setting.h @@ -0,0 +1,45 @@ +/*************************************************************************** + alsa-config-mixer-setting.h - description + ------------------- + begin : Mon Aug 15 2005 + copyright : (C) 2005 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. * + * * + ***************************************************************************/ + +#ifndef __KRADIO_ALSA_CONFIG_MIXER_SETTING_H +#define __KRADIO_ALSA_CONFIG_MIXER_SETTING_H + +#include <qstring.h> + +class KConfig; + +class AlsaConfigMixerSetting +{ +public: + AlsaConfigMixerSetting(); + AlsaConfigMixerSetting(KConfig *c, const QString &prefix); + AlsaConfigMixerSetting(int card, const QString &name, bool use, bool active, float volume); + ~AlsaConfigMixerSetting(); + + QString getIDString() const { return getIDString(m_card, m_name); } + static QString getIDString(int card, const QString &m_name); + + void saveState(KConfig *c, const QString &prefix) const; + + int m_card; + QString m_name; + bool m_use; + bool m_active; + float m_volume; +}; + +#endif diff --git a/kradio3/plugins/alsa-sound/alsa-mixer-element-ui.ui b/kradio3/plugins/alsa-sound/alsa-mixer-element-ui.ui new file mode 100644 index 0000000..e2c170d --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-mixer-element-ui.ui @@ -0,0 +1,270 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AlsaMixerElementUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AlsaMixerElementUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>124</width> + <height>153</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout16</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer> + <property name="name"> + <cstring>spacer23</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>1</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QSlider"> + <property name="name"> + <cstring>m_sliderVolume</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer23_2</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>1</width> + <height>40</height> + </size> + </property> + </spacer> + <spacer> + <property name="name"> + <cstring>spacer1_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout7</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3_3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>11</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="KIntSpinBox"> + <property name="name"> + <cstring>m_spinboxVolume</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>100</number> + </property> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3_4</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>11</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout5</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout4</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkboxActive</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>O&n</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + </widget> + <widget class="QCheckBox"> + <property name="name"> + <cstring>m_checkboxOverride</cstring> + </property> + <property name="text"> + <string>&Use</string> + </property> + <property name="accel"> + <string>Alt+U</string> + </property> + </widget> + </vbox> + </widget> + <spacer> + <property name="name"> + <cstring>spacer3_2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>16</width> + <height>20</height> + </size> + </property> + </spacer> + </hbox> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>m_labelMixerElementName</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>MixerName</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + </vbox> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="2"/> +<includehints> + <includehint>knuminput.h</includehint> +</includehints> +</UI> diff --git a/kradio3/plugins/alsa-sound/alsa-mixer-element.cpp b/kradio3/plugins/alsa-sound/alsa-mixer-element.cpp new file mode 100644 index 0000000..1fbc75a --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-mixer-element.cpp @@ -0,0 +1,139 @@ +/*************************************************************************** + alsa-mixer-element.cpp - description + ------------------- + begin : Mon Aug 15 2005 + copyright : (C) 2005 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 "alsa-mixer-element.h" + +#include <knuminput.h> +#include <qslider.h> +#include <qlabel.h> +#include <qcheckbox.h> + +#include <math.h> + +QAlsaMixerElement::QAlsaMixerElement(QWidget *parent, const QString &label, bool has_switch, bool has_volume) + : AlsaMixerElementUI(parent), + m_HasVolume(has_volume), + m_HasSwitch(has_switch), + m_dirty(false), + m_ignore_updates(false) +{ + setLabel(label); + setVolume(0); + + QObject::connect(m_spinboxVolume, SIGNAL(valueChanged(int)), + this, SLOT (slotSpinboxValueChanged(int))); + QObject::connect(m_sliderVolume, SIGNAL(valueChanged(int)), + this, SLOT (slotSliderValueChanged(int))); + + if (m_HasVolume) { + QObject::connect(m_checkboxOverride, SIGNAL(toggled(bool)), + m_spinboxVolume, SLOT (setEnabled(bool))); + QObject::connect(m_checkboxOverride, SIGNAL(toggled(bool)), + m_sliderVolume, SLOT (setEnabled(bool))); + } else { + m_spinboxVolume->hide(); + m_sliderVolume->hide(); + } + if (m_HasSwitch) { + QObject::connect(m_checkboxOverride, SIGNAL(toggled(bool)), + m_checkboxActive, SLOT (setEnabled(bool))); + } else { + //m_checkboxActive->hide(); + m_checkboxActive->setEnabled(false); + m_checkboxActive->setChecked(true); + } + + connect(m_checkboxOverride, SIGNAL(toggled(bool)), this, SLOT(slotSetDirty())); + connect(m_checkboxActive, SIGNAL(toggled(bool)), this, SLOT(slotSetDirty())); + connect(m_spinboxVolume, SIGNAL(valueChanged(int)), this, SLOT(slotSetDirty())); + connect(m_sliderVolume, SIGNAL(valueChanged(int)), this, SLOT(slotSetDirty())); +} + + +QAlsaMixerElement::~QAlsaMixerElement() +{ +} + +float QAlsaMixerElement::getVolume() const +{ + return ((float)m_spinboxVolume->value())/100.0; +} + +bool QAlsaMixerElement::getActive() const +{ + return m_checkboxActive->isChecked(); +} + +bool QAlsaMixerElement::getOverride() const +{ + return m_checkboxOverride->isChecked(); +} + +void QAlsaMixerElement::setLabel(const QString &label) +{ + m_labelMixerElementName->setText(label); +} + +void QAlsaMixerElement::setOverride(bool ov) +{ + m_ignore_updates = true; + m_checkboxOverride->setChecked(ov); + m_ignore_updates = false; +} + +void QAlsaMixerElement::setActive(bool active) +{ + m_ignore_updates = true; + m_checkboxActive->setChecked(active); + m_ignore_updates = false; +} + +void QAlsaMixerElement::setVolume(float vol) +{ + m_ignore_updates = true; + int v = (int)rint(vol*100 + 0.5); + m_sliderVolume->setValue(100 - v); + m_spinboxVolume->setValue(v); + m_ignore_updates = false; +} + +void QAlsaMixerElement::slotSpinboxValueChanged(int v) +{ + m_sliderVolume->setValue(100-v); +} + +void QAlsaMixerElement::slotSliderValueChanged(int v) +{ + m_spinboxVolume->setValue(100-v); +} + + +void QAlsaMixerElement::slotSetDirty() +{ + if (!m_dirty && !m_ignore_updates) { + m_dirty = true; + emit sigDirty(); + } +} + + +void QAlsaMixerElement::slotResetDirty() +{ + m_dirty = false; +} + +#include "alsa-mixer-element.moc" diff --git a/kradio3/plugins/alsa-sound/alsa-mixer-element.h b/kradio3/plugins/alsa-sound/alsa-mixer-element.h new file mode 100644 index 0000000..149e0b9 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-mixer-element.h @@ -0,0 +1,62 @@ +/*************************************************************************** + alsa-mixer-element.h - description + ------------------- + begin : Mon Aug 15 2005 + copyright : (C) 2005 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. * + * * + ***************************************************************************/ + +#ifndef __KRADIO_ALSA_MIXER_ELEMENT_H +#define __KRADIO_ALSA_MIXER_ELEMENT_H + +#include "alsa-mixer-element-ui.h" + +class QAlsaMixerElement : public AlsaMixerElementUI +{ +Q_OBJECT +public: + QAlsaMixerElement(QWidget *parent, const QString &label, bool has_switch, bool has_volume); + ~QAlsaMixerElement(); + + + float getVolume() const; + bool getActive() const; + bool getOverride() const; + + bool isDirty() const { return m_dirty; } + +public slots: + + void setLabel(const QString &label); + void setOverride(bool ov); + void setActive(bool active); + void setVolume(float vol); + void slotResetDirty(); + void slotSetDirty(); + +protected slots: + void slotSpinboxValueChanged(int v); + void slotSliderValueChanged(int v); + +signals: + + void sigDirty(); + +protected: + + bool m_HasVolume; + bool m_HasSwitch; + bool m_dirty; + bool m_ignore_updates; +}; + +#endif diff --git a/kradio3/plugins/alsa-sound/alsa-sound-configuration-ui.ui b/kradio3/plugins/alsa-sound/alsa-sound-configuration-ui.ui new file mode 100644 index 0000000..d1d2105 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-sound-configuration-ui.ui @@ -0,0 +1,323 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>AlsaSoundConfigurationUI</class> +<widget class="QWidget"> + <property name="name"> + <cstring>AlsaSoundConfigurationUI</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>475</width> + <height>260</height> + </rect> + </property> + <property name="caption"> + <string>AlsaSoundConfigurationUI</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>kTabWidget8</cstring> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Devices</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer114</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>5</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="0" column="0"> + <property name="name"> + <cstring>layout58</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>PCM Capture Card</string> + </property> + </widget> + <widget class="KComboBox" row="2" column="1"> + <property name="name"> + <cstring>m_comboCaptureCard</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="4" column="0"> + <property name="name"> + <cstring>textLabel2_2_2</cstring> + </property> + <property name="text"> + <string>Hardware Buffer Size</string> + </property> + </widget> + <widget class="KComboBox" row="3" column="1"> + <property name="name"> + <cstring>m_comboCaptureDevice</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="KIntSpinBox" row="5" column="1"> + <property name="name"> + <cstring>editBufferSize</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="suffix"> + <string> kB</string> + </property> + <property name="maxValue"> + <number>1024</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="KIntSpinBox" row="4" column="1"> + <property name="name"> + <cstring>editHWBufferSize</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="suffix"> + <string> kB</string> + </property> + <property name="maxValue"> + <number>1024</number> + </property> + <property name="minValue"> + <number>1</number> + </property> + </widget> + <widget class="KComboBox" row="0" column="1"> + <property name="name"> + <cstring>m_comboPlaybackCard</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="5" column="0"> + <property name="name"> + <cstring>textLabel2_2_2_2</cstring> + </property> + <property name="text"> + <string>Buffer Size</string> + </property> + </widget> + <widget class="KComboBox" row="1" column="1"> + <property name="name"> + <cstring>m_comboPlaybackDevice</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>3</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel2_3</cstring> + </property> + <property name="text"> + <string>PCM Playback Device</string> + </property> + </widget> + <widget class="QLabel" row="3" column="0"> + <property name="name"> + <cstring>textLabel2_4</cstring> + </property> + <property name="text"> + <string>PCM Capture Device</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>PCM Playback Card</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>E&xtended Options</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>chkDisablePlayback</cstring> + </property> + <property name="text"> + <string>Disable Pla&yback</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + </widget> + <widget class="QCheckBox" row="1" column="0"> + <property name="name"> + <cstring>chkDisableCapture</cstring> + </property> + <property name="text"> + <string>Disa&ble Capture</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + </widget> + <spacer row="2" column="0"> + <property name="name"> + <cstring>spacer113</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>TabPage</cstring> + </property> + <attribute name="title"> + <string>Capture Mixer Settings</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>11</number> + </property> + <widget class="QGroupBox" row="0" column="0"> + <property name="name"> + <cstring>m_groupMixer</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="title"> + <string></string> + </property> + </widget> + </grid> + </widget> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<layoutdefaults spacing="6" margin="0"/> +<includehints> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>knuminput.h</includehint> + <includehint>kcombobox.h</includehint> + <includehint>kcombobox.h</includehint> +</includehints> +</UI> diff --git a/kradio3/plugins/alsa-sound/alsa-sound-configuration.cpp b/kradio3/plugins/alsa-sound/alsa-sound-configuration.cpp new file mode 100644 index 0000000..f4914f2 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-sound-configuration.cpp @@ -0,0 +1,353 @@ +/*************************************************************************** + alsa-sound-configuration.cpp - description + ------------------- + begin : Thu Sep 30 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 <qcheckbox.h> +#include <qgroupbox.h> +#include <qlayout.h> +#include <qscrollview.h> + +#include <kurlrequester.h> +#include <knuminput.h> +#include <klineedit.h> +#include <kcombobox.h> +#include <ktabwidget.h> +#include <klocale.h> + +#include "alsa-mixer-element.h" +#include "alsa-sound-configuration.h" +#include "alsa-sound.h" + + +AlsaSoundConfiguration::AlsaSoundConfiguration (QWidget *parent, AlsaSoundDevice *dev) + : AlsaSoundConfigurationUI(parent), + m_SoundDevice (dev), + m_groupMixerLayout(NULL), + m_groupMixerScrollView(NULL), + m_groupMixerSubFrame(NULL), + m_dirty(true), + m_ignore_updates(false) +{ + QObject::connect(m_comboPlaybackCard, SIGNAL(activated(int)), this, SLOT(slotSetDirty())); + QObject::connect(m_comboCaptureCard, SIGNAL(activated(int)), this, SLOT(slotSetDirty())); + QObject::connect(m_comboPlaybackDevice, SIGNAL(activated(int)), this, SLOT(slotSetDirty())); + QObject::connect(m_comboCaptureDevice, SIGNAL(activated(int)), this, SLOT(slotSetDirty())); + QObject::connect(editHWBufferSize, SIGNAL(valueChanged(int)), this, SLOT(slotSetDirty())); + QObject::connect(editBufferSize, SIGNAL(valueChanged(int)), this, SLOT(slotSetDirty())); + QObject::connect(chkDisablePlayback, SIGNAL(toggled(bool)), this, SLOT(slotSetDirty())); + QObject::connect(chkDisableCapture, SIGNAL(toggled(bool)), this, SLOT(slotSetDirty())); + + QObject::connect(m_comboPlaybackCard, SIGNAL(activated(const QString &)), + this, SLOT(slotPlaybackCardSelected(const QString &))); + QObject::connect(m_comboCaptureCard, SIGNAL(activated(const QString &)), + this, SLOT(slotCaptureCardSelected(const QString &))); + + m_groupMixer->setColumnLayout(0, Qt::Horizontal ); + + QHBoxLayout *tmp_layout = new QHBoxLayout( m_groupMixer->layout() ); + + m_groupMixerScrollView = new QScrollView (m_groupMixer); + m_groupMixerScrollView->setFrameShape(QFrame::NoFrame); + m_groupMixerScrollView->setFrameShadow(QFrame::Plain); + m_groupMixerScrollView->enableClipper(true); + m_groupMixerScrollView->setResizePolicy(QScrollView::AutoOneFit); + //m_groupMixerScrollView->setHScrollBarMode(QScrollView::AlwaysOn); + + tmp_layout->addWidget(m_groupMixerScrollView); + + + int card = -1; + int ret = 0; + int idx_playback = 0; + int idx_capture = 0; + while ((ret = snd_card_next(&card)) == 0) { + char *name = NULL; + if (card >= 0 && snd_card_get_longname(card, &name) == 0) { + if (name) { + m_name2card[name] = card; + m_card2name[card] = name; + if (listSoundDevices(NULL, NULL, NULL, NULL, card, SND_PCM_STREAM_PLAYBACK)) { + m_comboPlaybackCard->insertItem(name); + m_playbackCard2idx[card] = idx_playback++; + } + if (listSoundDevices(NULL, NULL, NULL, NULL, card, SND_PCM_STREAM_CAPTURE)) { + m_comboCaptureCard->insertItem(name); + m_captureCard2idx[card] = idx_capture++; + } + } + } else { + break; + } + } + + slotCancel(); +} + + +AlsaSoundConfiguration::~AlsaSoundConfiguration () +{ +} + + +void AlsaSoundConfiguration::slotPlaybackCardSelected(const QString &cardname) +{ + if (!m_name2card.contains(cardname)) + return; + + listSoundDevices(m_comboPlaybackDevice, &m_playbackDeviceName2dev, &m_dev2playbackDeviceName, &m_playbackDevice2idx, m_name2card[cardname], SND_PCM_STREAM_PLAYBACK); +} + + +void AlsaSoundConfiguration::slotCaptureCardSelected(const QString &cardname) +{ + if (!m_name2card.contains(cardname)) + return; + + saveCaptureMixerSettings(); + + listSoundDevices(m_comboCaptureDevice, &m_captureDeviceName2dev, &m_dev2captureDeviceName, &m_captureDevice2idx, m_name2card[cardname], SND_PCM_STREAM_CAPTURE); + + m_currentCaptureCard = m_name2card[cardname]; + + QStringList vol_list, sw_list, all_list; + QMap<QString, AlsaMixerElement> vol_ch2id, sw_ch2id; + AlsaSoundDevice::getCaptureMixerChannels(m_name2card[cardname], NULL, vol_list, vol_ch2id, sw_list, sw_ch2id, &all_list); + + for (QMapIterator<QString, QAlsaMixerElement*> it = m_MixerElements.begin(); it != m_MixerElements.end(); ++it) { + delete *it; + } + m_MixerElements.clear(); + + if (m_groupMixerSubFrame) + delete m_groupMixerSubFrame; + + m_groupMixerSubFrame = new QFrame(m_groupMixerScrollView->viewport()); + m_groupMixerSubFrame->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + m_groupMixerScrollView->addChild(m_groupMixerSubFrame); + + int rows = 1; + int cols = (all_list.count()+rows-1)/rows; + m_groupMixerLayout = new QGridLayout( m_groupMixerSubFrame, rows, cols, 0, 0 ); + m_groupMixerLayout->setAlignment( Qt::AlignBottom ); + + int idx = 0; + for (QValueListConstIterator<QString> it = all_list.begin(); it != all_list.end(); ++it, ++idx) { + QAlsaMixerElement *e = new QAlsaMixerElement(m_groupMixerSubFrame, *it, + sw_list.contains(*it), vol_list.contains(*it)); + QObject::connect(e, SIGNAL(sigDirty()), this, SLOT(slotSetDirty())); + m_groupMixerLayout->addWidget(e, idx > cols, idx % cols); + e->show(); + m_MixerElements.insert(*it, e); + } + restoreCaptureMixerSettings(); + m_groupMixerSubFrame->show(); +} + +void AlsaSoundConfiguration::saveCaptureMixerSettings() +{ + for (QMapIterator<QString, QAlsaMixerElement*> it = m_MixerElements.begin(); it != m_MixerElements.end(); ++it) { + const QString &name = it.key(); + int card = m_currentCaptureCard; + QString id = AlsaConfigMixerSetting::getIDString(card, name); + QAlsaMixerElement *e = *it; + float vol = e->getVolume(); + bool use = e->getOverride(); + bool active = e->getActive(); + e->slotResetDirty(); + m_MixerSettings[id] = AlsaConfigMixerSetting(card,name,use,active,vol); + } +} + +void AlsaSoundConfiguration::restoreCaptureMixerSettings() +{ + for (QMapIterator<QString, QAlsaMixerElement*> it = m_MixerElements.begin(); it != m_MixerElements.end(); ++it) { + const QString &name = it.key(); + int card = m_currentCaptureCard; + QString id = AlsaConfigMixerSetting::getIDString(card, name); + QAlsaMixerElement *e = *it; + + if (m_MixerSettings.contains(id)) { + const AlsaConfigMixerSetting &s = m_MixerSettings[id]; + e->setVolume(s.m_volume); + e->setOverride(s.m_use); + e->setActive(s.m_active); + e->slotResetDirty(); + } else { + if (name == "ADC") { + e->setOverride(true); + e->setActive(true); + e->setVolume(1.0); + } + else if (name == "Digital") { + e->setOverride(true); + e->setActive(true); + e->setVolume(1.0); + } + else if (name == "Wave") { + e->setOverride(true); + e->setActive(false); + e->setVolume(0); + } + else if (name == "Capture") { + e->setOverride(true); + e->setActive(true); + e->setVolume(0.01); + } + e->slotSetDirty(); + } + } +} + +int AlsaSoundConfiguration::listSoundDevices(KComboBox *combobox, QMap<QString, int> *devname2dev, QMap<int, QString> *dev2devname, QMap<int, int> *dev2idx, int card, snd_pcm_stream_t stream) +{ + snd_ctl_t *handle = NULL; + int dev = -1; + snd_ctl_card_info_t *info = NULL; + snd_pcm_info_t *pcminfo = NULL; + + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca (&pcminfo); + + QString ctlname = "hw:"+QString::number(card); + + if (combobox) + combobox->clear(); + if (devname2dev) + devname2dev->clear(); + if (dev2devname) + dev2devname->clear(); + if (dev2idx) + dev2idx->clear(); + + int count = 0; + + if (snd_ctl_open (&handle, ctlname.ascii(), 0) == 0) { + if (snd_ctl_card_info(handle, info) == 0) { + + dev = -1; + while (1) { + if (snd_ctl_pcm_next_device(handle, &dev) < 0) { + //logError("snd_ctl_pcm_next_device"); + } + if (dev < 0) + break; + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, stream); + int err = 0; + if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) { + if (err != -ENOENT) { + //logError(QString("control digital audio info (%1): %2").arg(card).arg(snd_strerror(err))); + } + continue; + } + const char *dev_name = snd_pcm_info_get_name(pcminfo); + QString devname = i18n("context-card-plus-device-number", "%1 device %2").arg(dev_name).arg(dev); + if (combobox) + combobox->insertItem(devname); + if (devname2dev) + (*devname2dev)[devname] = dev; + if (dev2devname) + (*dev2devname)[dev] = devname; + if (dev2idx) + (*dev2idx)[dev] = count; + ++count; + } + } + snd_ctl_close(handle); + } + return count; +} + +void AlsaSoundConfiguration::slotOK() +{ + if (!m_dirty) + return; + + if (m_SoundDevice) { + m_SoundDevice->setHWBufferSize ( editHWBufferSize ->value() * 1024); + m_SoundDevice->setBufferSize ( editBufferSize ->value() * 1024); + m_SoundDevice->enablePlayback (!chkDisablePlayback->isChecked()); + m_SoundDevice->enableCapture (!chkDisableCapture ->isChecked()); + + int card = m_name2card[m_comboPlaybackCard->currentText()]; + int device = m_playbackDeviceName2dev[m_comboPlaybackDevice->currentText()]; + m_SoundDevice->setPlaybackDevice( card, device); + card = m_name2card[m_comboCaptureCard->currentText()]; + device = m_captureDeviceName2dev[m_comboCaptureDevice->currentText()]; + m_SoundDevice->setCaptureDevice ( card, device); + + saveCaptureMixerSettings(); + m_SoundDevice->setCaptureMixerSettings(m_MixerSettings); + } + m_dirty = false; +} + + +void AlsaSoundConfiguration::slotCancel() +{ + if (!m_dirty) + return; + m_ignore_updates = true; + + int card = m_SoundDevice ? m_SoundDevice->getPlaybackCard() : 0; + int dev = m_SoundDevice ? m_SoundDevice->getPlaybackDevice() : 0; + m_comboPlaybackCard ->setCurrentItem(m_playbackCard2idx[card]); + slotPlaybackCardSelected(m_comboPlaybackCard->currentText()); + m_comboPlaybackDevice->setCurrentItem(m_playbackDevice2idx[dev]); + + card = m_SoundDevice ? m_SoundDevice->getCaptureCard() : 0; + dev = m_SoundDevice ? m_SoundDevice->getCaptureDevice() : 0; + m_comboCaptureCard ->setCurrentItem(m_captureCard2idx[card]); + slotCaptureCardSelected(m_comboCaptureCard->currentText()); + m_comboCaptureDevice->setCurrentItem(m_captureDevice2idx[dev]); + + //IErrorLogClient::staticLogDebug(QString("capture: card = %1(%2), dev = %3").arg(card).arg(m_captureCard2idx[card]).arg(dev)); + + editHWBufferSize ->setValue (m_SoundDevice ? m_SoundDevice->getHWBufferSize()/1024 : 4); + editBufferSize ->setValue (m_SoundDevice ? m_SoundDevice->getBufferSize()/1024 : 4); + chkDisablePlayback->setChecked(m_SoundDevice ? !m_SoundDevice->isPlaybackEnabled() : false); + chkDisableCapture ->setChecked(m_SoundDevice ? !m_SoundDevice->isCaptureEnabled() : false); + + //IErrorLogClient::staticLogDebug(QString("capture: card = %1").arg(m_comboCaptureCard->currentText())); + + + if (m_SoundDevice) + m_MixerSettings = m_SoundDevice->getCaptureMixerSettings(); + else + m_MixerSettings.clear(); + restoreCaptureMixerSettings(); + + m_ignore_updates = false; + m_dirty = false; +} + + +void AlsaSoundConfiguration::slotUpdateConfig() +{ + slotSetDirty(); + slotCancel(); +} + +void AlsaSoundConfiguration::slotSetDirty() +{ + if (!m_dirty && !m_ignore_updates) { + m_dirty = true; + //emit sigDirty(); + } +} + +#include "alsa-sound-configuration.moc" diff --git a/kradio3/plugins/alsa-sound/alsa-sound-configuration.h b/kradio3/plugins/alsa-sound/alsa-sound-configuration.h new file mode 100644 index 0000000..0dd361a --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-sound-configuration.h @@ -0,0 +1,83 @@ +/*************************************************************************** + alsa-sound-configuration.h - description + ------------------- + begin : Thu Sep 30 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. * + * * + ***************************************************************************/ + +#ifndef KRADIO_ALSA_SOUND_CONFIGURATION_H +#define KRADIO_ALSA_SOUND_CONFIGURATION_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "alsa-sound-configuration-ui.h" +#include "alsa-sound.h" +#include "alsa-config-mixer-setting.h" + +class QHBoxLayout; +class QGridLayout; +class QAlsaMixerElement; +class QScrollView; +class QFrame; + +class AlsaSoundConfiguration : public AlsaSoundConfigurationUI +{ +Q_OBJECT +public : + AlsaSoundConfiguration (QWidget *parent, AlsaSoundDevice *); + ~AlsaSoundConfiguration (); + +protected slots: + + void slotOK(); + void slotCancel(); + + void slotSetDirty(); + + void slotUpdateConfig(); + + void slotPlaybackCardSelected(const QString &cardname); + void slotCaptureCardSelected(const QString &cardname); + +protected: + int listSoundDevices(KComboBox *combobox, QMap<QString, int> *devname2dev, QMap<int, QString> *dev2devname, QMap<int, int> *dev2idx, int card, snd_pcm_stream_t stream); + void saveCaptureMixerSettings(); + void restoreCaptureMixerSettings(); + + AlsaSoundDevice *m_SoundDevice; + int m_currentCaptureCard; + QMap<QString, int> m_name2card, + m_name2capturedevice, + m_playbackDeviceName2dev, + m_captureDeviceName2dev; + QMap<int, QString> m_card2name, + m_dev2playbackDeviceName, + m_dev2captureDeviceName; + QMap<int, int> m_captureCard2idx, + m_captureDevice2idx, + m_playbackCard2idx, + m_playbackDevice2idx; + QGridLayout *m_groupMixerLayout; + QScrollView *m_groupMixerScrollView; + QFrame *m_groupMixerSubFrame; + QMap<QString, QAlsaMixerElement*> m_MixerElements; + + QMap<QString, AlsaConfigMixerSetting> m_MixerSettings; + + bool m_dirty; + bool m_ignore_updates; +}; + +#endif diff --git a/kradio3/plugins/alsa-sound/alsa-sound.cpp b/kradio3/plugins/alsa-sound/alsa-sound.cpp new file mode 100644 index 0000000..d67d5c8 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-sound.cpp @@ -0,0 +1,1562 @@ +/*************************************************************************** + alsa-sound.cpp - description + ------------------- + begin : Thu May 26 2005 + copyright : (C) 2005 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 <klocale.h> +#include <kaboutdata.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <unistd.h> +#include <math.h> +#include <errno.h> + +#include <sys/soundcard.h> +#include <alsa/asoundlib.h> + +#include "alsa-sound.h" +#include "alsa-sound-configuration.h" +// #include "capture-thread.h" +#include "../../src/include/aboutwidget.h" +#include "../../src/include/utils.h" + +/////////////////////////////////////////////////////////////////////// +//// plugin library functions + +PLUGIN_LIBRARY_FUNCTIONS(AlsaSoundDevice, "kradio-alsa-sound", i18n("Advanced Linux Sound Architecture (ALSA) Support")); + +///////////////////////////////////////////////////////////////////////////// + +struct _lrvol { unsigned char l, r; short dummy; }; + +AlsaSoundDevice::AlsaSoundDevice(const QString &name) + : QObject(NULL, NULL), + PluginBase(name, i18n("KRadio ALSA Sound Plugin")), + m_hPlayback(NULL), + m_hCapture(NULL), + m_hPlaybackMixer(NULL), + m_hCaptureMixer(NULL), + m_PlaybackFormat(), + m_CaptureFormat(), + m_PlaybackCard(-1), + m_PlaybackDevice(-1), + m_CaptureCard(-1), + m_CaptureDevice(-1), + m_PlaybackLatency(50), + m_CaptureLatency(50), + m_PassivePlaybackStreams(), + m_PlaybackStreamID(), + m_CaptureStreamID(), + m_HWBufferSize(2048), + m_BufferSize(16384), + 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)//, +// m_captureThread(NULL) +{ + QObject::connect(&m_PlaybackPollingTimer, SIGNAL(timeout()), this, SLOT(slotPollPlayback())); + QObject::connect(&m_CapturePollingTimer, SIGNAL(timeout()), this, SLOT(slotPollCapture())); +} + + +AlsaSoundDevice::~AlsaSoundDevice() +{ + stopCapture(m_CaptureStreamID); + stopPlayback(m_PlaybackStreamID); + closePlaybackDevice(); + closeCaptureDevice(); + closePlaybackMixerDevice(); + closeCaptureMixerDevice(); +} + + +bool AlsaSoundDevice::connectI(Interface *i) +{ + bool a = PluginBase::connectI(i); + bool b = ISoundStreamClient::connectI(i); + return a || b; +} + + +bool AlsaSoundDevice::disconnectI(Interface *i) +{ + bool a = PluginBase::disconnectI(i); + bool b = ISoundStreamClient::disconnectI(i); + return a || b; +} + +void AlsaSoundDevice::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_sendMute(this); + s->register4_sendUnmute(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 AlsaSoundDevice::saveState (KConfig *c) const +{ + c->setGroup(QString("alsa-sound-") + PluginBase::name()); + + c->writeEntry("playback-card", m_PlaybackCard); + c->writeEntry("playback-device", m_PlaybackDevice); + c->writeEntry("capture-card", m_CaptureCard); + c->writeEntry("capture-device", m_CaptureDevice); + c->writeEntry("enable-playback", m_EnablePlayback); + c->writeEntry("enable-capture", m_EnableCapture); + c->writeEntry("hwbuffer-size", m_HWBufferSize); + c->writeEntry("buffer-size", m_BufferSize); + c->writeEntry("soundstreamclient-id", m_SoundStreamClientID); + + c->writeEntry("mixer-settings", m_CaptureMixerSettings.count()); + int i = 0; + for (QMapConstIterator<QString, AlsaConfigMixerSetting> it = m_CaptureMixerSettings.begin(); it != m_CaptureMixerSettings.end(); ++it, ++i) { + + QString prefix = QString("mixer-setting-%1-").arg(i); + (*it).saveState(c, prefix); + } + +} + + +void AlsaSoundDevice::restoreState (KConfig *c) +{ + c->setGroup(QString("alsa-sound-") + PluginBase::name()); + + m_EnablePlayback = c->readBoolEntry("enable-playback", true); + m_EnableCapture = c->readBoolEntry("enable-capture", true); + m_HWBufferSize = c->readNumEntry ("hwbuffer-size", 2048); + m_BufferSize = c->readNumEntry ("buffer-size", 16384); + int card = c->readNumEntry ("playback-card", 0); + int dev = c->readNumEntry ("playback-device", 0); + setPlaybackDevice(card, dev); + card = c->readNumEntry ("capture-card", 0); + dev = c->readNumEntry ("capture-device", 0); + setCaptureDevice(card, dev); + + m_PlaybackBuffer.resize(m_BufferSize); + m_CaptureBuffer.resize(m_BufferSize); + + setSoundStreamClientID(c->readEntry("soundstreamclient-id", getSoundStreamClientID())); + + int n = c->readNumEntry("mixer-settings", 0); + for (int i = 0; i < n; ++i) { + QString prefix = QString("mixer-setting-%1-").arg(i); + AlsaConfigMixerSetting s(c, prefix); + m_CaptureMixerSettings.insert(s.getIDString(), s); + } + + emit sigUpdateConfig(); +} + + +ConfigPageInfo AlsaSoundDevice::createConfigurationPage() +{ + AlsaSoundConfiguration *conf = new AlsaSoundConfiguration(NULL, this); + QObject::connect(this, SIGNAL(sigUpdateConfig()), conf, SLOT(slotUpdateConfig())); + return ConfigPageInfo (conf, + i18n("ALSA Sound"), + i18n("ALSA Sound Device Options"), + "kradio_alsa2"); +} + + +AboutPageInfo AlsaSoundDevice::createAboutPage() +{ +/* KAboutData aboutData("kradio", + NULL, + NULL, + I18N_NOOP("ALSA Sound Plugin for KRadio"), + KAboutData::License_GPL, + "(c) 2005 Martin Witte", + 0, + "http://sourceforge.net/projects/kradio", + 0); + aboutData.addAuthor("Martin Witte", "", "witte@kawo1.rwth-aachen.de"); + + return AboutPageInfo( + new KRadioAboutWidget(aboutData, KRadioAboutWidget::AbtTabbed), + i18n("ALSA Sound"), + i18n("ALSA Sound"), + "kradio_alsa_sound" + ); +*/ + return AboutPageInfo(); +} + + + +bool AlsaSoundDevice::preparePlayback(SoundStreamID id, const QString &channel, bool active_mode, bool start_immediately) +{ + if (id.isValid()) { + m_PlaybackStreams.insert(id, SoundStreamConfig(channel, active_mode)); + if (start_immediately) + startPlayback(id); + return true; + // FIXME: what to do if stream is already playing? + } + return false; +} + +bool AlsaSoundDevice::prepareCapture(SoundStreamID id, const QString &channel) +{ + if (id.isValid()) { + m_CaptureStreams.insert(id, SoundStreamConfig(channel)); + return true; + // FIXME: what to do if stream is already playing? + } + return false; +} + +bool AlsaSoundDevice::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 AlsaSoundDevice::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 AlsaSoundDevice::supportsPlayback() const +{ + return m_EnablePlayback; +} + + +bool AlsaSoundDevice::supportsCapture() const +{ + return m_EnableCapture; +} + + +bool AlsaSoundDevice::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) { + openPlaybackMixerDevice(); + if (cfg.m_Volume >= 0 && writePlaybackMixerVolume(cfg.m_Channel, cfg.m_Volume, cfg.m_Muted)) { + notifyPlaybackVolumeChanged(id, cfg.m_Volume); + notifyMuted(id, cfg.m_Volume); + } + m_PlaybackPollingTimer.start(m_PlaybackLatency); + } + + // error handling? + return true; + } else { + return false; + } +} + + +bool AlsaSoundDevice::pausePlayback(SoundStreamID /*id*/) +{ + //return stopPlayback(id); + return false; +} + + +bool AlsaSoundDevice::stopPlayback(SoundStreamID id) +{ + if (id.isValid() && m_PlaybackStreams.contains(id)) { + + SoundStreamConfig &cfg = m_PlaybackStreams[id]; + + if (!cfg.m_ActiveMode) { + if (m_PassivePlaybackStreams.contains(id)) { +/* float tmp = 0; + writePlaybackMixerVolume(cfg.m_Channel, tmp, true);*/ + m_PassivePlaybackStreams.remove(id); + } + } else if (m_PlaybackStreamID == id) { + m_PlaybackStreamID = SoundStreamID::InvalidID; + m_PlaybackBuffer.clear(); + closePlaybackDevice(); + } + + closePlaybackMixerDevice(); + return true; + } else { + return false; + } +} + +bool AlsaSoundDevice::isPlaybackRunning(SoundStreamID id, bool &b) const +{ + if (id.isValid() && m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id)) { + b = true; + return true; + } else { + return false; + } +} + +bool AlsaSoundDevice::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 && proposed_format != m_CaptureFormat)) { + + m_CaptureStreamID = id; + SoundStreamConfig &cfg = m_CaptureStreams[id]; + + openCaptureMixerDevice(); + selectCaptureChannel(cfg.m_Channel); + if (cfg.m_Volume >= 0 && writeCaptureMixerVolume(cfg.m_Channel, cfg.m_Volume)) { + notifyCaptureVolumeChanged(m_CaptureStreamID, cfg.m_Volume); + } + + openCaptureDevice(proposed_format); + + // FIXME: error handling? + } + + real_format = m_CaptureFormat; + m_CaptureRequestCounter++; + +// m_captureThread = new AlsaCaptureThread(this, m_hCapture, m_CaptureFormat, 5, m_BufferSize); +// m_captureThread->start(); + + slotPollCapture(); + + return true; + } else { + return false; + } +} + + +bool AlsaSoundDevice::stopCapture(SoundStreamID id) +{ + if (id.isValid() && m_CaptureStreamID == id) { + + if (--m_CaptureRequestCounter == 0) { + +// m_captureThread->setDone(); +// if (!m_captureThread->wait(4000)) { //wait at maximum 4 seconds +// logError("AlsaPlugin: capture thread did not terminate. Killing it."); +// m_captureThread->terminate(); +// m_captureThread->wait(); +// } + + slotPollCapture(); + +// if (m_captureThread->error()) { +// logError(i18n("ALSA Plugin, device plughw:%1,%2: %3").arg(m_CaptureCard) +// .arg(m_CaptureDevice) +// .arg(i18n("unknown error"))); +// } +// +// delete m_captureThread; +// m_captureThread = NULL; + + m_CaptureStreamID = SoundStreamID::InvalidID; + m_CaptureBuffer.clear(); + + closeCaptureMixerDevice(); + closeCaptureDevice(); + } + return true; + } else { + return false; + } +} + + +bool AlsaSoundDevice::isCaptureRunning(SoundStreamID id, bool &b, SoundFormat &sf) const +{ + if (id.isValid() && m_CaptureStreamID == id) { + b = true; + sf = m_CaptureFormat; + return true; + } else { + return false; + } +} + + +bool AlsaSoundDevice::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 AlsaSoundDevice::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 AlsaSoundDevice::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_hPlayback) { + openPlaybackDevice(format); + } else if (format != m_PlaybackFormat) { + // flush playback buffer + size_t buffersize = 0; + char *buffer = m_PlaybackBuffer.getData(buffersize); + + snd_pcm_writei(m_hPlayback, buffer, buffersize / m_PlaybackFormat.sampleSize()); + + // if not all could be written, it must be discarded + m_PlaybackBuffer.clear(); + closePlaybackDevice(); + openPlaybackDevice(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("plughw:%1,%2: Playback buffer overflow. Skipped %3 bytes").arg(m_PlaybackCard).arg(m_PlaybackDevice).arg(QString::number(m_PlaybackSkipCount))); + m_PlaybackSkipCount = 0; + } + return m_PlaybackSkipCount == 0;*/ + return true; +} + + + +void AlsaSoundDevice::slotPollPlayback() +{ + if (m_PlaybackStreamID.isValid()) { + + if (m_PlaybackBuffer.getFillSize() > 0 && m_hPlayback) { + + size_t buffersize = 0; + int frameSize = m_CaptureFormat.frameSize(); + char *buffer = m_PlaybackBuffer.getData(buffersize); + int framesWritten = snd_pcm_writei(m_hPlayback, buffer, buffersize / frameSize); + int bytesWritten = framesWritten * frameSize; + + if (framesWritten > 0) { + m_PlaybackBuffer.removeData(bytesWritten); + } else if (framesWritten == 0) { + logError(i18n("ALSA Plugin: cannot write data for device plughw:%1,%2").arg(m_PlaybackCard).arg(m_PlaybackDevice)); + } else if (framesWritten == -EAGAIN) { + // do nothing + } else { + snd_pcm_prepare(m_hPlayback); + logWarning(i18n("ALSA Plugin: buffer underrun for device plughw:%1,%2").arg(m_PlaybackCard).arg(m_PlaybackDevice)); + } + } + + if (m_PlaybackBuffer.getFreeSize() > m_PlaybackBuffer.getSize() / 3) { + notifyReadyForPlaybackData(m_PlaybackStreamID, m_PlaybackBuffer.getFreeSize()); + } + + checkMixerVolume(m_PlaybackStreamID); + } + + QValueListConstIterator<SoundStreamID> end = m_PassivePlaybackStreams.end(); + for (QValueListConstIterator<SoundStreamID> it = m_PassivePlaybackStreams.begin(); it != end; ++it) + checkMixerVolume(*it); +} + + +void AlsaSoundDevice::slotPollCapture() +{ + if (m_CaptureStreamID.isValid() && m_hCapture) { + +// while (m_captureThread && m_captureThread->getAvailableReadBuffers()) { +// QString dev = QString("alsa://plughw:%1,%2").arg(m_CaptureCard).arg(m_CaptureDevice); +// size_t size = 0; +// char *buffer = m_captureThread->getReadBuffer(size); +// time_t cur_time = time(NULL); +// notifySoundStreamData(m_CaptureStreamID, m_CaptureFormat, buffer, size, SoundMetaData(m_CapturePos, cur_time - m_CaptureStartTime, cur_time, dev)); +// m_CapturePos += size; +// } + + size_t bufferSize = 0; + char *buffer = m_CaptureBuffer.getFreeSpace(bufferSize); + + if (bufferSize) { + + size_t frameSize = m_CaptureFormat.frameSize(); + int framesRead = snd_pcm_readi(m_hCapture, buffer, bufferSize / frameSize); + size_t bytesRead = framesRead > 0 ? framesRead * frameSize : 0; + +// //BEGIN DEBUG +// static unsigned int debug_val = 0; +// short *debug_buf = (short*)buffer; +// for (int i = 0; i < bytesRead / 2 / sizeof(short); ++i) { +// debug_buf[2*i] = debug_val >> 10; +// debug_buf[2*i+1] = debug_val >> 10; +// ++debug_val; +// } +// //END DEBUG + + if (framesRead > 0) { + m_CaptureBuffer.removeFreeSpace(bytesRead); + } else if (framesRead == 0) { + snd_pcm_prepare(m_hCapture); + logError(i18n("ALSA Plugin: cannot read data from device plughw:%1,%2").arg(m_CaptureCard).arg(m_CaptureDevice)); + } else if (framesRead == -EAGAIN) { + // do nothing + } else { + snd_pcm_prepare(m_hCapture); + logWarning(i18n("ALSA Plugin: buffer overrun for device plughw:%1,%2 (buffersize=%3, buffer=%4)").arg(m_CaptureCard).arg(m_CaptureDevice).arg(bufferSize).arg((long long unsigned)buffer)); + } + + QString dev = QString("alsa://plughw:%1,%2").arg(m_CaptureCard).arg(m_CaptureDevice); + 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_CaptureFormat, buffer, size, consumed_size, SoundMetaData(m_CapturePos, cur_time - m_CaptureStartTime, cur_time, i18n("internal stream, not stored (%1)").arg(dev))); + + 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_CaptureStreamID.isValid()) + checkMixerVolume(m_CaptureStreamID); +} + + +bool AlsaSoundDevice::openPlaybackDevice(const SoundFormat &format, bool reopen) +{ + if (m_PlaybackCard < 0 || m_PlaybackDevice < 0) + return false; + + if (m_hPlayback) { + + if (reopen) { + + closePlaybackDevice ( /* force = */ true); + + } else { + + if (format != m_PlaybackFormat) + return false; + + return true; + } + } else { + if (reopen) // FIXME: emw: please check if this makes sense !?!? + return true; + } + + m_PlaybackFormat = format; + + QString dev = QString("plughw:%1,%2").arg(m_PlaybackCard).arg(m_PlaybackDevice); + bool error = !openAlsaDevice(m_hPlayback, m_PlaybackFormat, dev.ascii(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK, m_PlaybackLatency); + + if (!error) { + m_PlaybackPollingTimer.start(m_PlaybackLatency); + } else { + closePlaybackDevice(); + } + +// m_PlaybackSkipCount = 0; + + return !error; +} + + +bool AlsaSoundDevice::openCaptureDevice(const SoundFormat &format, bool reopen) +{ + if (m_PlaybackCard < 0 || m_PlaybackDevice < 0) + return false; + + if (m_hCapture) { + + if (reopen) { + + closeCaptureDevice ( /* force = */ true); + + } else { + + if (format != m_CaptureFormat) + return false; + + return true; + } + } else { + if (reopen) // FIXME: emw: please check if this makes sense !?!? + return true; + } + + m_CaptureFormat = format; + + QString dev = QString("plughw:%1,%2").arg(m_CaptureCard).arg(m_CaptureDevice); +// bool error = !openAlsaDevice(m_hCapture, m_CaptureFormat, dev.ascii(), SND_PCM_STREAM_CAPTURE, /*flags = block*/0, m_CaptureLatency); + bool error = !openAlsaDevice(m_hCapture, m_CaptureFormat, dev.ascii(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK, m_CaptureLatency); + + if (!error) { + m_CapturePollingTimer.start(m_CaptureLatency); + } else { + closeCaptureDevice(); + } + + m_CaptureSkipCount = 0; + + return !error; +} + + +bool AlsaSoundDevice::openAlsaDevice(snd_pcm_t *&alsa_handle, SoundFormat &format, const char *pcm_name, snd_pcm_stream_t stream, int flags, unsigned &latency) +{ + bool error = false; + int dir = 0; + + snd_pcm_hw_params_t *hwparams = NULL; + + snd_pcm_hw_params_alloca(&hwparams); + + + /* OPEN */ + + if (!error && snd_pcm_open(&alsa_handle, pcm_name, stream, flags) < 0) { + logError(i18n("ALSA Plugin: Error opening PCM device %1").arg(pcm_name)); + error = true; + } + + if (!error && snd_pcm_hw_params_any(alsa_handle, hwparams) < 0) { + logError(i18n("ALSA Plugin: Can not configure PCM device %1").arg(pcm_name)); + error = true; + } + + /* interleaved access type */ + + if (!error && snd_pcm_hw_params_set_access(alsa_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) { + logError(i18n("ALSA Plugin: Error setting access for %1").arg(pcm_name)); + error = true; + } + + /* sample format */ + snd_pcm_format_t sample_format = snd_pcm_build_linear_format(format.m_SampleBits, + format.m_SampleBits, + !format.m_IsSigned, + format.m_Endianess == BIG_ENDIAN); + if (!error && snd_pcm_hw_params_set_format(alsa_handle, hwparams, sample_format) < 0) { + logError(i18n("ALSA Plugin: Error setting sample format for %1").arg(pcm_name)); + error = true; + } + + /* channels */ + if (!error && snd_pcm_hw_params_set_channels(alsa_handle, hwparams, format.m_Channels) < 0) { + logError(i18n("ALSA Plugin: Error setting channels for %1").arg(pcm_name)); + error = true; + } + + /* sample rate */ + int rate = format.m_SampleRate; + if (!error && snd_pcm_hw_params_set_rate_near(alsa_handle, hwparams, &format.m_SampleRate, &dir) < 0) { + logError(i18n("ALSA Plugin: Error setting rate for %1").arg(pcm_name)); + error = true; + } + if (!error && format.m_SampleRate != format.m_SampleRate) { + logWarning(i18n("ALSA Plugin: The rate %1 Hz is not supported by your hardware %2. Using %3 Hz instead").arg(rate).arg(pcm_name).arg(format.m_SampleRate)); + } + + + snd_pcm_uframes_t period_size = m_HWBufferSize / format.frameSize(); + if (!error && snd_pcm_hw_params_set_period_size_near(alsa_handle, hwparams, &period_size, &dir) < 0) { + logError(i18n("ALSA Plugin: Error setting period size for %1").arg(pcm_name)); + error = true; + } + +// size_t buffersize_frames = m_HWBufferSize / format.frameSize(); +// int periods = 4; +// //int period_size = m_BufferSize / periods; +// +// /* fragments */ +// if (!error && snd_pcm_hw_params_set_periods(alsa_handle, hwparams, periods, 0) < 0) { +// logError(i18n("ALSA Plugin: Error setting periods for %1").arg(pcm_name)); +// error = true; +// } + +// /* Set buffer size (in frames). */ +// +// snd_pcm_uframes_t exact_buffersize_frames = buffersize_frames; +// if (!error && snd_pcm_hw_params_set_buffer_size_near(alsa_handle, hwparams, &exact_buffersize_frames) < 0) { +// exact_buffersize_frames = 4096; +// if (!error && snd_pcm_hw_params_set_buffer_size_near(alsa_handle, hwparams, &exact_buffersize_frames) < 0) { +// logError(i18n("ALSA Plugin: Error setting buffersize for %1").arg(pcm_name)); +// error = true; +// } +// } + +// size_t exact_buffersize = exact_buffersize_frames * format.frameSize(); +// if (!error && m_HWBufferSize != exact_buffersize) { +// logWarning(i18n("ALSA Plugin: Hardware %1 does not support buffer size of %2. Using buffer size of %3 instead.").arg(pcm_name).arg(m_HWBufferSize).arg(exact_buffersize)); +// size_t tmp = (((m_HWBufferSize - 1) / exact_buffersize) + 1) * exact_buffersize; +// setHWBufferSize(tmp); +// logInfo(i18n("ALSA Plugin: adjusted buffer size for %1 to %2 bytes").arg(pcm_name).arg(QString::number(tmp))); +// } + + /* set all params */ + + if (!error && snd_pcm_hw_params(alsa_handle, hwparams) < 0) { + logError(i18n("ALSA Plugin: Error setting HW params")); + error = true; + } + + if (!error && snd_pcm_hw_params_get_period_size(hwparams, &period_size, &dir) < 0) { + logError(i18n("ALSA Plugin: Error getting period size for %1").arg(pcm_name)); + error = true; + } + +// latency = (exact_buffersize_frames * 1000) / format.m_SampleRate / periods; /* in milli seconds */ + latency = (period_size * format.frameSize() * 1000) / format.m_SampleRate; /* in milli seconds */ + + if (!error) { + snd_pcm_prepare(alsa_handle); + } + + return !error; +} + + +bool AlsaSoundDevice::closePlaybackDevice(bool force) +{ + if (!m_PlaybackStreamID.isValid() || force) { + + if (!m_hPlaybackMixer) + m_PlaybackPollingTimer.stop(); + + if (m_hPlayback) { + snd_pcm_drop(m_hPlayback); + snd_pcm_close(m_hPlayback); + } + + m_hPlayback = NULL; + + m_PlaybackBuffer.clear(); + return true; + } + return false; +} + + +bool AlsaSoundDevice::closeCaptureDevice(bool force) +{ + if (!m_CaptureStreamID.isValid() || force) { + + if (!m_hCaptureMixer) + m_CapturePollingTimer.stop(); + + if (m_hCapture) { + snd_pcm_drop(m_hCapture); + snd_pcm_close(m_hCapture); + } + + m_hCapture = NULL; + + m_CaptureBuffer.clear(); + return true; + } + return false; +} + + +bool AlsaSoundDevice::openPlaybackMixerDevice(bool reopen) +{ + return openMixerDevice(m_hPlaybackMixer, m_PlaybackCard, reopen, &m_PlaybackPollingTimer, m_PlaybackLatency); +} + + +bool AlsaSoundDevice::openCaptureMixerDevice(bool reopen) +{ +// logDebug("AlsaSoundDevice::openCaptureMixerDevice: card == " + QString::number(m_CaptureCard)); + return openMixerDevice(m_hCaptureMixer, m_CaptureCard, reopen, &m_CapturePollingTimer, m_CaptureLatency); +} + + +bool AlsaSoundDevice::closePlaybackMixerDevice(bool force) +{ + return closeMixerDevice(m_hPlaybackMixer, m_PlaybackCard, m_PlaybackStreamID, m_hPlayback, force, &m_PlaybackPollingTimer); +} + +bool AlsaSoundDevice::closeCaptureMixerDevice(bool force) +{ + return closeMixerDevice(m_hCaptureMixer, m_CaptureCard, m_CaptureStreamID, m_hCapture, force, &m_CapturePollingTimer); +} + + +static int mixer_dummy_callback(snd_mixer_t *, unsigned int /*mask*/, snd_mixer_elem_t */*elem*/) +{ + return 0; +} + +bool AlsaSoundDevice::openMixerDevice(snd_mixer_t *&mixer_handle, int card, bool reopen, QTimer *timer, int timer_latency) +{ + if (reopen) { + if (mixer_handle >= 0) + closeMixerDevice(mixer_handle, card, SoundStreamID::InvalidID, NULL, /* force = */ true, timer); + else + return true; + } + + if (!mixer_handle) { + bool error = false; + if (snd_mixer_open (&mixer_handle, 0) < 0) { + staticLogError(i18n("ALSA Plugin: Error opening mixer")); + error = true; + } + QString cardid = "hw:" + QString::number(card); + bool attached = false; + if (!error) { + if (snd_mixer_attach (mixer_handle, cardid.ascii()) < 0) { + staticLogError(i18n("ALSA Plugin: ERROR: snd_mixer_attach for card %1").arg(card)); + error = true; + } else { + attached = true; + } + } + if (!error && snd_mixer_selem_register(mixer_handle, NULL, NULL) < 0) { + staticLogError(i18n("ALSA Plugin: Error: snd_mixer_selem_register for card %1").arg(card)); + error = true; + } + if (!error && snd_mixer_load (mixer_handle) < 0) { + staticLogError(i18n("ALSA Plugin: Error: snd_mixer_load for card %1").arg(card)); + error = true; + } + if (mixer_handle) { + snd_mixer_set_callback (mixer_handle, mixer_dummy_callback); + } + + if (error) { + if (attached) { + snd_mixer_detach(mixer_handle, cardid.ascii()); + } + snd_mixer_close(mixer_handle); + mixer_handle = NULL; + } + } + + if (mixer_handle && timer) { + timer->start(timer_latency); + } + return mixer_handle != NULL; +} + + +bool AlsaSoundDevice::closeMixerDevice(snd_mixer_t *&mixer_handle, int card, SoundStreamID id, snd_pcm_t *pcm_handle, bool force, QTimer *timer) +{ + if (!id.isValid() || force) { + + if (!pcm_handle && timer) + timer->stop(); + + if (mixer_handle) { + QString cardid = "hw:" + QString::number(card); + snd_mixer_free(mixer_handle); + snd_mixer_detach(mixer_handle, cardid.ascii()); + snd_mixer_close (mixer_handle); + } + mixer_handle = NULL; + } + return mixer_handle == NULL; +} + +void AlsaSoundDevice::getPlaybackMixerChannels( + int card, + snd_mixer_t *__mixer_handle, + QStringList &retval, QMap<QString, AlsaMixerElement> &ch2id) +{ + retval.clear(); + ch2id.clear(); + + snd_mixer_t *mixer_handle = __mixer_handle/*m_hPlaybackMixer*/; + bool use_tmp_handle = false; + + if (!mixer_handle) { + openMixerDevice(mixer_handle, card/*m_PlaybackCard*/, false, NULL, 0); + use_tmp_handle = true; + } + + if (mixer_handle) { + snd_mixer_elem_t *elem = NULL; + + for (elem = snd_mixer_first_elem(mixer_handle); elem; elem = snd_mixer_elem_next(elem)) { + AlsaMixerElement sid; + if (!snd_mixer_selem_is_active(elem)) + continue; + snd_mixer_selem_get_id(elem, sid); + QString name = snd_mixer_selem_id_get_name(sid); + int idx = snd_mixer_selem_id_get_index(sid); + if (idx) + name = i18n("context-mixername-number", "%1 %2").arg(name).arg(idx); + if (snd_mixer_selem_has_playback_volume(elem)) { + ch2id[name] = sid; + retval.append(name); + } + } + } + + if (use_tmp_handle && mixer_handle) { + closeMixerDevice(mixer_handle, card /*m_PlaybackCard*/, SoundStreamID::InvalidID, NULL, true, NULL); + } +} + +void AlsaSoundDevice::getCaptureMixerChannels( + int card, + snd_mixer_t *__mixer_handle, + QStringList &vol_list, QMap<QString, AlsaMixerElement> &vol_ch2id, + QStringList &sw_list, QMap<QString, AlsaMixerElement> &sw_ch2id, + QStringList *all_list +) +{ + vol_list.clear(); + sw_list.clear(); + if (all_list) all_list->clear(); + vol_ch2id.clear(); + sw_ch2id.clear(); + + snd_mixer_t *mixer_handle = __mixer_handle /*m_hCaptureMixer*/; + bool use_tmp_handle = false; + + if (!mixer_handle) { +// staticLogDebug("AlsaSoundDevice::getCaptureMixerChannels: card == " + QString::number(card/*m_CaptureCard*/)); + openMixerDevice(mixer_handle, card /*m_CaptureCard*/, false, NULL, 0); + use_tmp_handle = true; + } + + if (mixer_handle) { + snd_mixer_elem_t *elem = NULL; + + for (elem = snd_mixer_first_elem(mixer_handle); elem; elem = snd_mixer_elem_next(elem)) { + AlsaMixerElement sid; + if (!snd_mixer_selem_is_active(elem)) + continue; + snd_mixer_selem_get_id(elem, sid); + QString name = snd_mixer_selem_id_get_name(sid); + int idx = snd_mixer_selem_id_get_index(sid); + if (idx) + name = i18n("context-mixerelement-name-number", "%1 %2").arg(name).arg(idx); + + bool add2all = false; + if (snd_mixer_selem_has_capture_switch(elem)) { + sw_ch2id[name] = sid; + sw_list.append(name); + add2all = true; + } + if (snd_mixer_selem_has_capture_volume(elem)) { + vol_ch2id[name] = sid; + vol_list.append(name); + add2all = true; + } + if (add2all && all_list) { + all_list->append(name); + } + } + } + + if (use_tmp_handle && mixer_handle) { + closeMixerDevice(mixer_handle, card /*m_CaptureCard*/, SoundStreamID::InvalidID, NULL, true, NULL); + } +} + +const QStringList &AlsaSoundDevice::getPlaybackChannels() const +{ + return m_PlaybackChannels; +} + + +const QStringList &AlsaSoundDevice::getCaptureChannels() const +{ + return m_CaptureChannelsSwitch; +} + + +bool AlsaSoundDevice::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)) { + if (writePlaybackMixerVolume(cfg.m_Channel, cfg.m_Volume = volume, cfg.m_Muted)) { + notifyPlaybackVolumeChanged(id, cfg.m_Volume); + } + } + return true; + } + return false; +} + + +bool AlsaSoundDevice::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)) { + if (writeCaptureMixerVolume(cfg.m_Channel, cfg.m_Volume = volume)) { + notifyCaptureVolumeChanged(id, cfg.m_Volume); + } + } + return true; + } + return false; +} + + +bool AlsaSoundDevice::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 AlsaSoundDevice::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 AlsaSoundDevice::checkMixerVolume(SoundStreamID id) +{ + if (id.isValid()) { + + if (m_hPlaybackMixer && m_PassivePlaybackStreams.contains(id) || m_PlaybackStreamID == id) { + snd_mixer_handle_events(m_hPlaybackMixer); + SoundStreamConfig &cfg = m_PlaybackStreams[id]; + + bool m = false; + float v = readPlaybackMixerVolume(cfg.m_Channel, m); + if (rint(100*cfg.m_Volume) != rint(100*v)) { + cfg.m_Volume = v; + notifyPlaybackVolumeChanged(id, v); + } + if (m != cfg.m_Muted) { + cfg.m_Muted = m; + notifyMuted(id, m); + } + } + + if (m_hCaptureMixer && m_CaptureStreamID == id) { + snd_mixer_handle_events(m_hCaptureMixer); + SoundStreamConfig &cfg = m_CaptureStreams[id]; + + if (m_CaptureChannels2ID.contains(cfg.m_Channel)) { + float v = readCaptureMixerVolume(cfg.m_Channel); + if (rint(100*cfg.m_Volume) != rint(100*v)) { + cfg.m_Volume = v; + notifyCaptureVolumeChanged(id, v); + } + } + } + } +} + + +float AlsaSoundDevice::readPlaybackMixerVolume(const QString &channel, bool &muted) const +{ + if (!m_hPlaybackMixer) + return 0; // without error + + if (m_PlaybackChannels2ID.contains(channel) && m_hPlaybackMixer) { + AlsaMixerElement sid = m_PlaybackChannels2ID[channel]; + snd_mixer_elem_t *elem = snd_mixer_find_selem(m_hPlaybackMixer, sid); + if (elem) { + long min = 0; + long max = 0; + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + if (min != max) { + long val = min; + + muted = false; + int m = false; + if (snd_mixer_selem_get_playback_switch(elem, SND_MIXER_SCHN_FRONT_LEFT, &m) == 0) { + muted = !m; + } + if (snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &val) == 0) { + return ((float)(val - min)) / (float)(max - min); + } + } + } + } + logError("AlsaSound::readPlaybackMixerVolume: " + + i18n("error while reading volume from hwplug:%1,%2") + .arg(m_PlaybackCard) + .arg(m_PlaybackDevice)); + return 0; +} + + +float AlsaSoundDevice::readCaptureMixerVolume(const QString &channel) const +{ + if (!m_hCaptureMixer) + return 0; // without error + + if (m_CaptureChannels2ID.contains(channel) && m_hCaptureMixer) { + AlsaMixerElement sid = m_CaptureChannels2ID[channel]; + snd_mixer_elem_t *elem = snd_mixer_find_selem(m_hCaptureMixer, sid); + if (elem) { + if (!snd_mixer_selem_has_capture_volume(elem)) + return 0; + long min = 0; + long max = 0; + snd_mixer_selem_get_capture_volume_range(elem, &min, &max); + if (min != max) { + long val = min; + if (snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &val) == 0) { + return ((float)(val - min)) / (float)(max - min); + } + } + } + } + logError("AlsaSound::readCaptureMixerVolume: " + + i18n("error while reading volume from hwplug:%1,%2") + .arg(m_CaptureCard) + .arg(m_CaptureDevice)); + return 0; +} + + +bool AlsaSoundDevice::writePlaybackMixerVolume (const QString &channel, float &vol, bool muted) +{ + if (vol > 1.0) vol = 1.0; + if (vol < 0) vol = 0.0; + + if (!m_hPlaybackMixer) + return false; + + if (m_PlaybackChannels2ID.contains(channel) && m_hPlaybackMixer) { + AlsaMixerElement sid = m_PlaybackChannels2ID[channel]; + snd_mixer_elem_t *elem = snd_mixer_find_selem(m_hPlaybackMixer, sid); + if (elem) { + long min = 0; + long max = 0; + snd_mixer_selem_get_playback_volume_range(elem, &min, &max); + if (min != max) { + long val = (int)rint(min + (max - min) * vol); + vol = (float)(val - min) / (float)(max - min); + snd_mixer_selem_set_playback_switch_all(elem, !muted); + if (snd_mixer_selem_set_playback_volume_all(elem, val) == 0) { + return true; + } + } + } + } + logError("AlsaSound::writePlaybackMixerVolume: " + + i18n("error while writing volume %1 to hwplug:%2,%3") + .arg(vol) + .arg(m_PlaybackCard) + .arg(m_PlaybackDevice)); + return false; +} + + + + +bool AlsaSoundDevice::writeCaptureMixerVolume (const QString &channel, float &vol) +{ + if (vol > 1.0) vol = 1.0; + if (vol < 0) vol = 0.0; + + if (!m_hCaptureMixer) + return false; + + if (m_CaptureChannels2ID.contains(channel) && m_hCaptureMixer) { + AlsaMixerElement sid = m_CaptureChannels2ID[channel]; + snd_mixer_elem_t *elem = snd_mixer_find_selem(m_hCaptureMixer, sid); + if (elem) { + long min = 0; + long max = 0; + snd_mixer_selem_get_capture_volume_range(elem, &min, &max); + if (min != max) { + long val = (int)rint(min + (max - min) * vol); + vol = (float)(val - min) / (float)(max - min); + if (snd_mixer_selem_set_capture_volume_all(elem, val) == 0) { + return true; + } + } + } + } + logError("AlsaSound::writeCaptureMixerVolume: " + + i18n("error while writing volume %1 to hwplug:%2,%3") + .arg(vol) + .arg(m_CaptureCard) + .arg(m_CaptureDevice)); + return false; +} + + +bool AlsaSoundDevice::writeCaptureMixerSwitch (const QString &channel, bool capture) +{ + if (!m_hCaptureMixer) + return false; + + if (m_CaptureChannelsSwitch2ID.contains(channel) && m_hCaptureMixer) { + AlsaMixerElement sid = m_CaptureChannelsSwitch2ID[channel]; + snd_mixer_elem_t *elem = snd_mixer_find_selem(m_hCaptureMixer, sid); + if (elem) { + if (snd_mixer_selem_set_capture_switch_all(elem, capture) == 0) { + return true; + } + } + } + logError("AlsaSound::writeCaptureMixerSwitch: " + + i18n("error while setting capture switch %1 for hwplug:%2,%3") + .arg(channel) + .arg(m_CaptureCard) + .arg(m_CaptureDevice)); + return false; +} + + +void AlsaSoundDevice::selectCaptureChannel (const QString &channel) +{ + writeCaptureMixerSwitch(channel, true); + + const QString ADC = "ADC"; + if (m_CaptureChannels2ID.contains(ADC)) { + float v = readCaptureMixerVolume(ADC); + if (rint(v*100) == 0) { + float tmp_vol = 1.0; + writeCaptureMixerVolume(ADC, tmp_vol); + } + } + const QString Digital = "Digital"; + if (m_CaptureChannels2ID.contains(Digital)) { + float v = readCaptureMixerVolume(Digital); + if (rint(v*100) == 0) { + float tmp_vol = 1.0; + writeCaptureMixerVolume(Digital, tmp_vol); + } + } + const QString WAVE = "Wave"; + if (m_CaptureChannels2ID.contains(WAVE)) { + float x = 0; + writeCaptureMixerVolume(WAVE, x); + } + const QString Capture = "Capture"; + if (m_CaptureChannelsSwitch2ID.contains(Capture)) { + writeCaptureMixerSwitch(Capture, true); + } + + for (QMapConstIterator<QString, AlsaConfigMixerSetting> it = m_CaptureMixerSettings.begin(); it != m_CaptureMixerSettings.end(); ++it) { + const AlsaConfigMixerSetting &s = *it; + if (s.m_card == m_CaptureCard && s.m_use) { + float vol = s.m_volume; + if (m_CaptureChannels2ID.contains(s.m_name)) + writeCaptureMixerVolume(s.m_name, vol); + if (m_CaptureChannelsSwitch2ID.contains(s.m_name)) + writeCaptureMixerSwitch(s.m_name, s.m_active); + } + } +} + + +void AlsaSoundDevice::setHWBufferSize(int s) +{ + m_HWBufferSize = s; +} + + +void AlsaSoundDevice::setBufferSize(int s) +{ + m_BufferSize = s; + m_PlaybackBuffer.resize(m_BufferSize); + m_CaptureBuffer.resize(m_BufferSize); +} + + +void AlsaSoundDevice::enablePlayback(bool on) +{ + m_EnablePlayback = on; +} + + +void AlsaSoundDevice::enableCapture(bool on) +{ + m_EnableCapture = on; +} + + +void AlsaSoundDevice::setPlaybackDevice(int card, int dev) +{ + if (m_PlaybackCard == card && m_PlaybackDevice == dev) + return; + + m_PlaybackCard = card; + m_PlaybackDevice = dev; + SoundFormat f = m_PlaybackFormat; + if (m_hPlayback) + openPlaybackDevice(f, /* reopen = */ true); + if (m_hPlaybackMixer) + openPlaybackMixerDevice(/* reopen = */ true); + + getPlaybackMixerChannels(m_PlaybackCard, + m_hPlaybackMixer, + m_PlaybackChannels, m_PlaybackChannels2ID); + notifyPlaybackChannelsChanged(m_SoundStreamClientID, m_PlaybackChannels); +} + + +void AlsaSoundDevice::setCaptureDevice(int card, int dev) +{ +// logDebug("AlsaSoundDevice::setCaptureDevice-1: m_CaptureCard == " + QString::number(m_CaptureCard) + ", card == " + QString::number(card)); + if (m_CaptureCard == card && m_CaptureDevice == dev) + return; +// logDebug("AlsaSoundDevice::setCaptureDevice-2: m_CaptureCard == " + QString::number(m_CaptureCard) + ", card == " + QString::number(card)); + + m_CaptureCard = card; + m_CaptureDevice = dev; + SoundFormat f = m_CaptureFormat; + if (m_hCapture) + openCaptureDevice(f, /* reopen = */ true); + if (m_hCaptureMixer) + openCaptureMixerDevice(/* reopen = */ true); + + getCaptureMixerChannels(m_CaptureCard, + m_hCaptureMixer, + m_CaptureChannels, m_CaptureChannels2ID, m_CaptureChannelsSwitch, m_CaptureChannelsSwitch2ID); + notifyCaptureChannelsChanged(m_SoundStreamClientID, m_CaptureChannels); +} + + +QString AlsaSoundDevice::getSoundStreamClientDescription() const +{ + return i18n("ALSA Sound Device %1").arg(PluginBase::name()); +} + + +bool AlsaSoundDevice::mute (SoundStreamID id, bool mute) +{ + if (id.isValid() && (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id))) { + SoundStreamConfig &cfg = m_PlaybackStreams[id]; + if (mute != cfg.m_Muted) { + if (writePlaybackMixerVolume(cfg.m_Channel, cfg.m_Volume, cfg.m_Muted = mute)) { + notifyMuted(id, cfg.m_Muted); + } + } + return true; + } + return false; +} + +bool AlsaSoundDevice::unmute (SoundStreamID id, bool unmute) +{ + if (id.isValid() && (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id))) { + SoundStreamConfig &cfg = m_PlaybackStreams[id]; + bool mute = !unmute; + if (mute != cfg.m_Muted) { + if (writePlaybackMixerVolume(cfg.m_Channel, cfg.m_Volume, cfg.m_Muted = mute)) { + notifyMuted(id, cfg.m_Muted); + } + } + return true; + } + return false; +} + +bool AlsaSoundDevice::isMuted(SoundStreamID id, bool &m) const +{ + if (id.isValid() && (m_PlaybackStreamID == id || m_PassivePlaybackStreams.contains(id))) { + const SoundStreamConfig &cfg = m_PlaybackStreams[id]; + m = cfg.m_Muted; + return true; + } + return false; +} + + +void AlsaSoundDevice::setCaptureMixerSettings(const QMap<QString, AlsaConfigMixerSetting> &map) +{ + m_CaptureMixerSettings = map; +} + + + +// bool AlsaSoundDevice::event(QEvent *_e) +// { +// bool retval = false; +// +// switch (_e->type()) { +// +// case CaptureTerminated : +// retval = true; +// break; +// +// case CaptureStep : +// +// slotPollCapture(); +// +// retval = true; +// break; +// +// case CaptureError : +// case CaptureWarning : +// case CaptureInfo : +// case CaptureDebug : +// if (m_captureThread) { +// AlsaCaptureEvent *e = static_cast<AlsaCaptureEvent*>(_e); +// QString msg = i18n("ALSA Plugin, device plughw:%1,%2: %3") +// .arg(m_CaptureCard) +// .arg(m_CaptureDevice) +// .arg(e->message()); +// switch (_e->type()) { +// case CaptureError : +// logError(msg); +// m_captureThread->resetError(); +// break; +// case CaptureWarning : +// logWarning(msg); +// break; +// case CaptureInfo : +// logInfo(msg); +// break; +// case CaptureDebug : +// logDebug(msg); +// break; +// default: +// break; +// } +// } +// retval = true; +// break; +// +// default: +// retval = QObject::event(_e); +// break; +// } +// +// return retval; +// } + + + + + + + + +#include "alsa-sound.moc" diff --git a/kradio3/plugins/alsa-sound/alsa-sound.h b/kradio3/plugins/alsa-sound/alsa-sound.h new file mode 100644 index 0000000..93a9fc8 --- /dev/null +++ b/kradio3/plugins/alsa-sound/alsa-sound.h @@ -0,0 +1,296 @@ +/*************************************************************************** + alsa-sound.h - description + ------------------- + begin : Thu May 26 2005 + copyright : (C) 2005 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. * + * * + ***************************************************************************/ + +#ifndef _KRADIO_ALSA_SOUND_H +#define _KRADIO_ALSA_SOUND_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "../../src/include/ringbuffer.h" +#include "../../src/include/plugins.h" +#include "../../src/include/soundstreamclient_interfaces.h" + +#include "alsa-config-mixer-setting.h" + +#include <qobject.h> +#include <qtimer.h> +#include <alsa/asoundlib.h> + +enum DUPLEX_MODE { DUPLEX_UNKNOWN, DUPLEX_FULL, DUPLEX_HALF }; + + +struct SoundStreamConfig +{ + SoundStreamConfig() + : m_ActiveMode(false), + m_Channel(QString::null), + m_Volume(-1), + m_Muted(false) + {} + + SoundStreamConfig(const QString &_channel, bool active_mode = true) + : m_ActiveMode(active_mode), + m_Channel(_channel), + m_Volume(-1), + m_Muted(false) + {} + + SoundStreamConfig(const SoundStreamConfig &c) + : m_ActiveMode(c.m_ActiveMode), + m_Channel(c.m_Channel), + m_Volume(c.m_Volume), + m_Muted(c.m_Muted) + {} + + bool m_ActiveMode; + QString m_Channel; + float m_Volume; + bool m_Muted; +}; + + +class AlsaCaptureThread; + +class AlsaMixerElement +{ +public: + AlsaMixerElement() { snd_mixer_selem_id_malloc(&m_ID); } + AlsaMixerElement(snd_mixer_selem_id_t *id) { snd_mixer_selem_id_malloc(&m_ID); snd_mixer_selem_id_copy(m_ID, id) ; } + AlsaMixerElement(const AlsaMixerElement &x) { snd_mixer_selem_id_malloc(&m_ID); snd_mixer_selem_id_copy(m_ID, x.m_ID); } + ~AlsaMixerElement() { snd_mixer_selem_id_free (m_ID); } + + operator snd_mixer_selem_id_t *&() { return m_ID; } + + AlsaMixerElement &operator = (const AlsaMixerElement &x) { snd_mixer_selem_id_copy(m_ID, x.m_ID); return *this; } + +protected: + snd_mixer_selem_id_t *m_ID; +}; + + +class AlsaSoundDevice : public QObject, + public PluginBase, + public ISoundStreamClient +{ +Q_OBJECT + +public: + AlsaSoundDevice (const QString &name); + virtual ~AlsaSoundDevice (); + + virtual bool connectI(Interface *i); + virtual bool disconnectI(Interface *i); + + // PluginBase + +public: + virtual void saveState (KConfig *) const; + virtual void restoreState (KConfig *); + + virtual QString pluginClassName() const { return "AlsaSoundDevice"; } + + virtual const QString &name() const { return PluginBase::name(); } + virtual QString &name() { return PluginBase::name(); } + + virtual ConfigPageInfo createConfigurationPage(); + virtual AboutPageInfo createAboutPage(); + + // ISoundStreamClient: direct device access + +RECEIVERS: + void noticeConnectedI (ISoundStreamServer *s, bool pointer_valid); + bool preparePlayback(SoundStreamID id, const QString &channel, bool active_mode, bool start_immediately); + bool prepareCapture(SoundStreamID id, const QString &channel); + bool releasePlayback(SoundStreamID id); + bool releaseCapture(SoundStreamID id); + +ANSWERS: + bool supportsPlayback() const; + bool supportsCapture() const; + + QString getSoundStreamClientDescription() const; + + // ISoundStreamClient: mixer access + +public: + static + void getPlaybackMixerChannels(int card, snd_mixer_t *mixer_handle, + QStringList &retval, QMap<QString, AlsaMixerElement> &int2id); + static + void getCaptureMixerChannels (int card, snd_mixer_t *mixer_handle, + QStringList &vol_list, QMap<QString, AlsaMixerElement> &vol_ch2id, + QStringList &sw_list, QMap<QString, AlsaMixerElement> &sw_ch2id, + QStringList *all_list = NULL); + +ANSWERS: + const QStringList &getPlaybackChannels() const; + const QStringList &getCaptureChannels() const; + +RECEIVERS: + bool setPlaybackVolume(SoundStreamID id, float volume); + bool setCaptureVolume(SoundStreamID id, float volume); + bool getPlaybackVolume(SoundStreamID id, float &volume) const; + bool getCaptureVolume(SoundStreamID id, float &volume) const; + + bool mute (SoundStreamID id, bool mute); + bool unmute (SoundStreamID id, bool unmute); + bool isMuted(SoundStreamID id, bool &m) const; + + + // ISoundStreamClient: generic broadcasts + +RECEIVERS: + bool startPlayback(SoundStreamID id); + bool pausePlayback(SoundStreamID id); + bool stopPlayback(SoundStreamID id); + bool isPlaybackRunning(SoundStreamID id, bool &b) const; + + bool startCaptureWithFormat(SoundStreamID id, + const SoundFormat &proposed_format, + SoundFormat &real_format, + bool force_format); + bool stopCapture(SoundStreamID id); + bool isCaptureRunning(SoundStreamID id, bool &b, SoundFormat &sf) const; + + bool noticeSoundStreamClosed(SoundStreamID id); + bool noticeSoundStreamRedirected(SoundStreamID oldID, SoundStreamID newID); + + bool noticeSoundStreamData(SoundStreamID id, + const SoundFormat &, + const char *data, size_t size, size_t &consumed_size, + const SoundMetaData &md + ); + + + // Config Access + + int getHWBufferSize() const { return m_HWBufferSize; } + int getBufferSize() const { return m_BufferSize; } + bool isPlaybackEnabled() const { return m_EnablePlayback; } + bool isCaptureEnabled() const { return m_EnableCapture; } + int getPlaybackCard() const { return m_PlaybackCard; } + int getPlaybackDevice() const { return m_PlaybackDevice; } + int getCaptureCard() const { return m_CaptureCard; } + int getCaptureDevice() const { return m_CaptureDevice; } + const QMap<QString, AlsaConfigMixerSetting> & + getCaptureMixerSettings() const { return m_CaptureMixerSettings; } + + void setHWBufferSize(int s); + void setBufferSize(int s); + void enablePlayback(bool on); + void enableCapture(bool on); + void setPlaybackDevice(int card, int device); + void setCaptureDevice(int card, int device); + void setCaptureMixerSettings(const QMap<QString, AlsaConfigMixerSetting> &map); + +protected slots: + + void slotPollPlayback(); + void slotPollCapture(); + +signals: + + void sigUpdateConfig(); + +protected: +// bool event(QEvent *_e); + + bool openAlsaDevice(snd_pcm_t *&alsa_handle, SoundFormat &format, const char *pcm_name, snd_pcm_stream_t stream, int flags, unsigned &latency); + + bool openPlaybackDevice (const SoundFormat &format, bool reopen = false); + bool openCaptureDevice (const SoundFormat &format, bool reopen = false); + bool closePlaybackDevice(bool force = false); + bool closeCaptureDevice (bool force = false); + + bool openPlaybackMixerDevice (bool reopen = false); + bool openCaptureMixerDevice (bool reopen = false); + static bool openMixerDevice(snd_mixer_t *&mixer_handle, int card, bool reopen, QTimer *timer, int timer_latency); + bool closeCaptureMixerDevice (bool force = false); + bool closePlaybackMixerDevice(bool force = false); + static bool closeMixerDevice(snd_mixer_t *&mixer_handle, int card, SoundStreamID id, snd_pcm_t *pcm_handle, bool force, QTimer *timer); + + void checkMixerVolume(SoundStreamID id); + float readPlaybackMixerVolume(const QString &channel, bool &muted) const; + float readCaptureMixerVolume(const QString &channel) const; + bool writePlaybackMixerVolume(const QString &channel, float &vol, bool muted); + bool writeCaptureMixerVolume(const QString &channel, float &vol); + bool writeCaptureMixerSwitch(const QString &channel, bool capture); + + void selectCaptureChannel (const QString &channel); + + /* ALSA HANDLES */ + snd_pcm_t *m_hPlayback; + snd_pcm_t *m_hCapture; + snd_mixer_t *m_hPlaybackMixer; + snd_mixer_t *m_hCaptureMixer; + + SoundFormat m_PlaybackFormat; + SoundFormat m_CaptureFormat; + int m_PlaybackCard; + int m_PlaybackDevice; + int m_CaptureCard; + int m_CaptureDevice; + + unsigned m_PlaybackLatency; + unsigned m_CaptureLatency; + + QStringList m_PlaybackChannels, + m_CaptureChannels, + m_CaptureChannelsSwitch; + + QMap<QString, AlsaMixerElement> m_PlaybackChannels2ID, + m_CaptureChannels2ID, + m_CaptureChannelsSwitch2ID; + + QMap<SoundStreamID, SoundStreamConfig> + m_PlaybackStreams, + m_CaptureStreams; + + QValueList<SoundStreamID> + m_PassivePlaybackStreams; + SoundStreamID m_PlaybackStreamID, + m_CaptureStreamID; + + size_t m_HWBufferSize; + size_t m_BufferSize; + RingBuffer m_PlaybackBuffer, + m_CaptureBuffer; + + unsigned m_CaptureRequestCounter; + Q_UINT64 m_CapturePos; + time_t m_CaptureStartTime; + + size_t //m_PlaybackSkipCount, + m_CaptureSkipCount; + + bool m_EnablePlayback, + m_EnableCapture; + + QTimer m_PlaybackPollingTimer; + QTimer m_CapturePollingTimer; + +// AlsaCaptureThread *m_captureThread; + + QMap<QString, AlsaConfigMixerSetting> m_CaptureMixerSettings; + +}; + + + +#endif diff --git a/kradio3/plugins/alsa-sound/icons/Makefile.am b/kradio3/plugins/alsa-sound/icons/Makefile.am new file mode 100644 index 0000000..b3f2583 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/Makefile.am @@ -0,0 +1,2 @@ +icons_ICON = AUTO +iconsdir = $(kde_datadir)/kradio/icons diff --git a/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa.png Binary files differnew file mode 100644 index 0000000..a25cfd2 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..479a6cc --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi16-action-kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa.png Binary files differnew file mode 100644 index 0000000..796b052 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..5e6cc22 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi22-action-kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa.png Binary files differnew file mode 100644 index 0000000..d1deb5d --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..a849948 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi32-action-kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa.png Binary files differnew file mode 100644 index 0000000..aa89348 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..802f64f --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi48-action-kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa.png Binary files differnew file mode 100644 index 0000000..fef1ba3 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..1489f65 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/hi64-action-kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/icons/kradio_alsa.png b/kradio3/plugins/alsa-sound/icons/kradio_alsa.png Binary files differnew file mode 100644 index 0000000..11b3ce4 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/kradio_alsa.png diff --git a/kradio3/plugins/alsa-sound/icons/kradio_alsa2.png b/kradio3/plugins/alsa-sound/icons/kradio_alsa2.png Binary files differnew file mode 100644 index 0000000..82d97c0 --- /dev/null +++ b/kradio3/plugins/alsa-sound/icons/kradio_alsa2.png diff --git a/kradio3/plugins/alsa-sound/po/Makefile.am b/kradio3/plugins/alsa-sound/po/Makefile.am new file mode 100644 index 0000000..9de3420 --- /dev/null +++ b/kradio3/plugins/alsa-sound/po/Makefile.am @@ -0,0 +1,3 @@ + +PACKAGE = kradio-alsa-sound +POFILES = AUTO diff --git a/kradio3/plugins/alsa-sound/po/de.po b/kradio3/plugins/alsa-sound/po/de.po new file mode 100644 index 0000000..9c47ebc --- /dev/null +++ b/kradio3/plugins/alsa-sound/po/de.po @@ -0,0 +1,289 @@ +# translation of de.po to +# translation of kradio-alsa-sound.po to +# This file is put in the public domain. +# +# Ernst Martin Witte <emw@nocabal.de>, 2006. +msgid "" +msgstr "" +"Project-Id-Version: de\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-12 18:41+0100\n" +"PO-Revision-Date: 2006-11-12 18:23+0100\n" +"Last-Translator: Ernst Martin Witte <emw@nocabal.de>\n" +"Language-Team: <de@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.11.4\n" + +#. i18n: file alsa-mixer-element-ui.ui line 16 +#: rc.cpp:3 rc.cpp:70 alsa-mixer-element-ui.cpp:104 +#, no-c-format +msgid "Form1" +msgstr "Form1" + +#. i18n: file alsa-mixer-element-ui.ui line 210 +#: rc.cpp:6 rc.cpp:73 alsa-mixer-element-ui.cpp:105 +#, no-c-format +msgid "O&n" +msgstr "A&n" + +#. i18n: file alsa-mixer-element-ui.ui line 213 +#: rc.cpp:9 rc.cpp:76 alsa-mixer-element-ui.cpp:106 +#, no-c-format +msgid "Alt+N" +msgstr "Alt+N" + +#. i18n: file alsa-mixer-element-ui.ui line 221 +#: rc.cpp:12 rc.cpp:79 alsa-mixer-element-ui.cpp:107 +#, no-c-format +msgid "&Use" +msgstr "&Verwenden" + +#. i18n: file alsa-mixer-element-ui.ui line 224 +#: rc.cpp:15 rc.cpp:82 alsa-mixer-element-ui.cpp:108 +#, no-c-format +msgid "Alt+U" +msgstr "Alt+U" + +#. i18n: file alsa-mixer-element-ui.ui line 256 +#: rc.cpp:18 rc.cpp:85 alsa-mixer-element-ui.cpp:109 +#, no-c-format +msgid "MixerName" +msgstr "MixerName" + +#. i18n: file alsa-sound-configuration-ui.ui line 16 +#: rc.cpp:21 rc.cpp:88 alsa-sound-configuration-ui.cpp:152 +#, no-c-format +msgid "AlsaSoundConfigurationUI" +msgstr "AlsaSoundConfigurationUI" + +#. i18n: file alsa-sound-configuration-ui.ui line 34 +#: rc.cpp:24 rc.cpp:91 alsa-sound-configuration-ui.cpp:161 +#, no-c-format +msgid "Devices" +msgstr "Geräte" + +#. i18n: file alsa-sound-configuration-ui.ui line 73 +#: rc.cpp:27 rc.cpp:94 alsa-sound-configuration-ui.cpp:153 +#, no-c-format +msgid "PCM Capture Card" +msgstr "Soundkarte für die Aufnahme" + +#. i18n: file alsa-sound-configuration-ui.ui line 94 +#: rc.cpp:30 rc.cpp:97 alsa-sound-configuration-ui.cpp:154 +#, no-c-format +msgid "Hardware Buffer Size" +msgstr "Hardware-Puffergröße" + +#. i18n: file alsa-sound-configuration-ui.ui line 123 +#. i18n: file alsa-sound-configuration-ui.ui line 145 +#. i18n: file alsa-sound-configuration-ui.ui line 123 +#. i18n: file alsa-sound-configuration-ui.ui line 145 +#: rc.cpp:33 rc.cpp:36 rc.cpp:100 rc.cpp:103 +#: alsa-sound-configuration-ui.cpp:155 alsa-sound-configuration-ui.cpp:156 +#, no-c-format +msgid " kB" +msgstr " kB" + +#. i18n: file alsa-sound-configuration-ui.ui line 172 +#: rc.cpp:39 rc.cpp:106 alsa-sound-configuration-ui.cpp:157 +#, no-c-format +msgid "Buffer Size" +msgstr "Puffergröße" + +#. i18n: file alsa-sound-configuration-ui.ui line 193 +#: rc.cpp:42 rc.cpp:109 alsa-sound-configuration-ui.cpp:158 +#, no-c-format +msgid "PCM Playback Device" +msgstr "Gerät für die Wiedergabe" + +#. i18n: file alsa-sound-configuration-ui.ui line 201 +#: rc.cpp:45 rc.cpp:112 alsa-sound-configuration-ui.cpp:159 +#, no-c-format +msgid "PCM Capture Device" +msgstr "Gerät für die Aufnahme" + +#. i18n: file alsa-sound-configuration-ui.ui line 209 +#: rc.cpp:48 rc.cpp:115 alsa-sound-configuration-ui.cpp:160 +#, no-c-format +msgid "PCM Playback Card" +msgstr "Soundkarte für die Wiedergabe" + +#. i18n: file alsa-sound-configuration-ui.ui line 221 +#: rc.cpp:51 rc.cpp:118 alsa-sound-configuration-ui.cpp:166 +#, no-c-format +msgid "E&xtended Options" +msgstr "Erweiterte Optionen" + +#. i18n: file alsa-sound-configuration-ui.ui line 235 +#: rc.cpp:54 rc.cpp:121 alsa-sound-configuration-ui.cpp:162 +#, no-c-format +msgid "Disable Pla&yback" +msgstr "Wiedergabe abschalten" + +#. i18n: file alsa-sound-configuration-ui.ui line 238 +#: rc.cpp:57 rc.cpp:124 alsa-sound-configuration-ui.cpp:163 +#, no-c-format +msgid "Alt+Y" +msgstr "Alt+Y" + +#. i18n: file alsa-sound-configuration-ui.ui line 246 +#: rc.cpp:60 rc.cpp:127 alsa-sound-configuration-ui.cpp:164 +#, no-c-format +msgid "Disa&ble Capture" +msgstr "Aufnahme abschalten" + +#. i18n: file alsa-sound-configuration-ui.ui line 249 +#: rc.cpp:63 rc.cpp:130 alsa-sound-configuration-ui.cpp:165 +#, no-c-format +msgid "Alt+B" +msgstr "Alt+B" + +#. i18n: file alsa-sound-configuration-ui.ui line 276 +#: rc.cpp:66 rc.cpp:133 alsa-sound-configuration-ui.cpp:168 +#, no-c-format +msgid "Capture Mixer Settings" +msgstr "Mixereinstellungen für die Aufnahme" + +#: _translatorinfo.cpp:1 +msgid "" +"_: NAME OF TRANSLATORS\n" +"Your names" +msgstr "Ernst Martin Witte" + +#: _translatorinfo.cpp:3 +msgid "" +"_: EMAIL OF TRANSLATORS\n" +"Your emails" +msgstr "emw@nocabal.de" + +#: alsa-sound-configuration.cpp:258 +msgid "context-card-plus-device-number" +msgstr "%1 Gerät %2" + +#: alsa-sound.cpp:40 +msgid "Advanced Linux Sound Architecture (ALSA) Support" +msgstr "Unterstützung für die \"Advanced Linux Sound Architecture\" (ALSA)" + +#: alsa-sound.cpp:48 +msgid "KRadio ALSA Sound Plugin" +msgstr "KRadio ALSA Sound Plugin" + +#: alsa-sound.cpp:196 +msgid "ALSA Sound" +msgstr "ALSA Sound" + +#: alsa-sound.cpp:197 +msgid "ALSA Sound Device Options" +msgstr "Optionen für die ALSA-Sound-Geräte" + +#: alsa-sound.cpp:553 +msgid "ALSA Plugin: cannot write data for device plughw:%1,%2" +msgstr "ALSA Plugin: Das schreiben auf das Gerät plughw:%1,%2 schlug fehl" + +#: alsa-sound.cpp:558 +msgid "ALSA Plugin: buffer underrun for device plughw:%1,%2" +msgstr "ALSA Plugin: Pufferunterlauf im Gerät plughw:%1,%2" + +#: alsa-sound.cpp:611 +msgid "ALSA Plugin: cannot read data from device plughw:%1,%2" +msgstr "ALSA Plugin: Das Lesen vom Gerät plughw:%1,%2 schlug fehl" + +#: alsa-sound.cpp:616 +msgid "" +"ALSA Plugin: buffer overrun for device plughw:%1,%2 (buffersize=%3, buffer=%" +"4)" +msgstr "" +"ALSA Plugin: Pufferüberlauf im Gerät plughw:%1,%2 (Puffergröße=%3, buffer=%4)" + +#: alsa-sound.cpp:626 +msgid "internal stream, not stored (%1)" +msgstr "interner, nicht aufgezeichneter Datenstrom (%1)" + +#: alsa-sound.cpp:736 +msgid "ALSA Plugin: Error opening PCM device %1" +msgstr "ALSA Plugin: Fehler beim Öffnen des Gerätes %1" + +#: alsa-sound.cpp:741 +msgid "ALSA Plugin: Can not configure PCM device %1" +msgstr "ALSA Plugin: Das Konfigurieren des Gerätes %1 schlug fehl" + +#: alsa-sound.cpp:748 +msgid "ALSA Plugin: Error setting access for %1" +msgstr "ALSA Plugin: Fehler beim Konfigurieren des Zugriffsmodus auf Gerät %1" + +#: alsa-sound.cpp:758 +msgid "ALSA Plugin: Error setting sample format for %1" +msgstr "ALSA Plugin: Fehler beim Einstellen des Abtastformats für Gerät %1" + +#: alsa-sound.cpp:764 +msgid "ALSA Plugin: Error setting channels for %1" +msgstr "ALSA Plugin: Fehler beim Einstellen der Kanäle für Gerät %1" + +#: alsa-sound.cpp:771 +msgid "ALSA Plugin: Error setting rate for %1" +msgstr "ALSA Plugin: Fehler beim Einstellen der Abtastrate für Gerät %1" + +#: alsa-sound.cpp:775 +msgid "" +"ALSA Plugin: The rate %1 Hz is not supported by your hardware %2. Using %3 " +"Hz instead" +msgstr "" +"ALSA Plugin: Die Abtastrate von %1 Hz wird von Ihrer Soundkarte %2 nicht " +"unterstützt. Es werden stattdessen %3 Hz verwendet" + +#: alsa-sound.cpp:781 +msgid "ALSA Plugin: Error setting period size for %1" +msgstr "" +"ALSA Plugin: Fehler beim Einstellen der Puffer-Periodengröße für Gerät %1" + +#: alsa-sound.cpp:817 +msgid "ALSA Plugin: Error setting HW params" +msgstr "ALSA Plugin: Fehler beim Einstellen der Hardwareparameter" + +#: alsa-sound.cpp:822 +msgid "ALSA Plugin: Error getting period size for %1" +msgstr "ALSA Plugin: Fehler beim Lesen der Puffer-Periodengröße von Gerät %1" + +#: alsa-sound.cpp:920 +msgid "ALSA Plugin: Error opening mixer" +msgstr "ALSA Plugin: Fehler beim Öffnen des Mixers" + +#: alsa-sound.cpp:927 +msgid "ALSA Plugin: ERROR: snd_mixer_attach for card %1" +msgstr "ALSA Plugin: Fehler in Funktion snd_mixer_attach bei Soundkarte %1" + +#: alsa-sound.cpp:934 +msgid "ALSA Plugin: Error: snd_mixer_selem_register for card %1" +msgstr "" +"ALSA Plugin: Fehler in Funktion snd_mixer_selem_register bei Soundkarte %1" + +#: alsa-sound.cpp:938 +msgid "ALSA Plugin: Error: snd_mixer_load for card %1" +msgstr "ALSA Plugin: Fehler in Funktion snd_mixer_load bei Soundkarte %1" + +#: alsa-sound.cpp:1006 +msgid "context-mixername-number" +msgstr "%1 - %2" + +#: alsa-sound.cpp:1053 +msgid "context-mixerelement-name-number" +msgstr "%1 - %2" + +#: alsa-sound.cpp:1206 alsa-sound.cpp:1236 +msgid "error while reading volume from hwplug:%1,%2" +msgstr "Fehler beim Lesen der Lautstärke von Gerät hwplug:%1,%2" + +#: alsa-sound.cpp:1269 alsa-sound.cpp:1304 +msgid "error while writing volume %1 to hwplug:%2,%3" +msgstr "Fehler beim Setzen der Lautstärke von Gerät hwplug:%1,%2" + +#: alsa-sound.cpp:1327 +msgid "error while setting capture switch %1 for hwplug:%2,%3" +msgstr "" +"Fehler beim Einstellen des Aufnahmeauswahlschalters %1 für Gerät hwplug:%2,%3" + +#: alsa-sound.cpp:1448 +msgid "ALSA Sound Device %1" +msgstr "ALSA Soundkarte %1" diff --git a/kradio3/plugins/alsa-sound/po/ru.po b/kradio3/plugins/alsa-sound/po/ru.po new file mode 100644 index 0000000..afdd1cc --- /dev/null +++ b/kradio3/plugins/alsa-sound/po/ru.po @@ -0,0 +1,288 @@ +# translation of ru.po to +# translation of kradio-alsa-sound.po to +# This file is put in the public domain. +# Алексей Кузнецов <Alexey.Kouznetsov@GMail.com>, 2006. +# +msgid "" +msgstr "" +"Project-Id-Version: ru\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2006-11-12 18:20+0100\n" +"PO-Revision-Date: 2006-11-08 12:15+0300\n" +"Last-Translator: Алексей Кузнецов <Alexey.Kouznetsov@GMail.com>\n" +"Language-Team: <ru@li.org>\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Generator: KBabel 1.10\n" + +#. i18n: file alsa-mixer-element-ui.ui line 16 +#: rc.cpp:3 rc.cpp:70 alsa-mixer-element-ui.cpp:104 +#, no-c-format +msgid "Form1" +msgstr "Form1" + +#. i18n: file alsa-mixer-element-ui.ui line 210 +#: rc.cpp:6 rc.cpp:73 alsa-mixer-element-ui.cpp:105 +#, no-c-format +msgid "O&n" +msgstr "&Вкл." + +#. i18n: file alsa-mixer-element-ui.ui line 213 +#: rc.cpp:9 rc.cpp:76 alsa-mixer-element-ui.cpp:106 +#, no-c-format +msgid "Alt+N" +msgstr "Alt+N" + +#. i18n: file alsa-mixer-element-ui.ui line 221 +#: rc.cpp:12 rc.cpp:79 alsa-mixer-element-ui.cpp:107 +#, no-c-format +msgid "&Use" +msgstr "&Исп." + +#. i18n: file alsa-mixer-element-ui.ui line 224 +#: rc.cpp:15 rc.cpp:82 alsa-mixer-element-ui.cpp:108 +#, no-c-format +msgid "Alt+U" +msgstr "Alt+U" + +#. i18n: file alsa-mixer-element-ui.ui line 256 +#: rc.cpp:18 rc.cpp:85 alsa-mixer-element-ui.cpp:109 +#, no-c-format +msgid "MixerName" +msgstr "" + +#. i18n: file alsa-sound-configuration-ui.ui line 16 +#: rc.cpp:21 rc.cpp:88 alsa-sound-configuration-ui.cpp:152 +#, no-c-format +msgid "AlsaSoundConfigurationUI" +msgstr "AlsaSoundConfigurationUI" + +#. i18n: file alsa-sound-configuration-ui.ui line 34 +#: rc.cpp:24 rc.cpp:91 alsa-sound-configuration-ui.cpp:161 +#, no-c-format +msgid "Devices" +msgstr "Устройства" + +#. i18n: file alsa-sound-configuration-ui.ui line 73 +#: rc.cpp:27 rc.cpp:94 alsa-sound-configuration-ui.cpp:153 +#, no-c-format +msgid "PCM Capture Card" +msgstr "Плата для захвата" + +#. i18n: file alsa-sound-configuration-ui.ui line 94 +#: rc.cpp:30 rc.cpp:97 alsa-sound-configuration-ui.cpp:154 +#, no-c-format +msgid "Hardware Buffer Size" +msgstr "Аппаратный размер буфера" + +#. i18n: file alsa-sound-configuration-ui.ui line 123 +#. i18n: file alsa-sound-configuration-ui.ui line 145 +#. i18n: file alsa-sound-configuration-ui.ui line 123 +#. i18n: file alsa-sound-configuration-ui.ui line 145 +#: rc.cpp:33 rc.cpp:36 rc.cpp:100 rc.cpp:103 +#: alsa-sound-configuration-ui.cpp:155 alsa-sound-configuration-ui.cpp:156 +#, no-c-format +msgid " kB" +msgstr " кБ" + +#. i18n: file alsa-sound-configuration-ui.ui line 172 +#: rc.cpp:39 rc.cpp:106 alsa-sound-configuration-ui.cpp:157 +#, no-c-format +msgid "Buffer Size" +msgstr "Размер буфера" + +#. i18n: file alsa-sound-configuration-ui.ui line 193 +#: rc.cpp:42 rc.cpp:109 alsa-sound-configuration-ui.cpp:158 +#, no-c-format +msgid "PCM Playback Device" +msgstr "Устройство воспроизведения" + +#. i18n: file alsa-sound-configuration-ui.ui line 201 +#: rc.cpp:45 rc.cpp:112 alsa-sound-configuration-ui.cpp:159 +#, no-c-format +msgid "PCM Capture Device" +msgstr "Устройство записи" + +#. i18n: file alsa-sound-configuration-ui.ui line 209 +#: rc.cpp:48 rc.cpp:115 alsa-sound-configuration-ui.cpp:160 +#, no-c-format +msgid "PCM Playback Card" +msgstr "Плата для проигрывания" + +#. i18n: file alsa-sound-configuration-ui.ui line 221 +#: rc.cpp:51 rc.cpp:118 alsa-sound-configuration-ui.cpp:166 +#, no-c-format +msgid "E&xtended Options" +msgstr "&Дополнительные параметры" + +#. i18n: file alsa-sound-configuration-ui.ui line 235 +#: rc.cpp:54 rc.cpp:121 alsa-sound-configuration-ui.cpp:162 +#, no-c-format +msgid "Disable Pla&yback" +msgstr "Запретить &воспроизведение" + +#. i18n: file alsa-sound-configuration-ui.ui line 238 +#: rc.cpp:57 rc.cpp:124 alsa-sound-configuration-ui.cpp:163 +#, no-c-format +msgid "Alt+Y" +msgstr "Alt+Y" + +#. i18n: file alsa-sound-configuration-ui.ui line 246 +#: rc.cpp:60 rc.cpp:127 alsa-sound-configuration-ui.cpp:164 +#, no-c-format +msgid "Disa&ble Capture" +msgstr "Запретить &запись" + +#. i18n: file alsa-sound-configuration-ui.ui line 249 +#: rc.cpp:63 rc.cpp:130 alsa-sound-configuration-ui.cpp:165 +#, no-c-format +msgid "Alt+B" +msgstr "Alt+B" + +#. i18n: file alsa-sound-configuration-ui.ui line 276 +#: rc.cpp:66 rc.cpp:133 alsa-sound-configuration-ui.cpp:168 +#, no-c-format +msgid "Capture Mixer Settings" +msgstr "&Параметры микшера для записи" + +#: _translatorinfo.cpp:1 +msgid "" +"_: NAME OF TRANSLATORS\n" +"Your names" +msgstr "Алексей Кузнецов" + +#: _translatorinfo.cpp:3 +msgid "" +"_: EMAIL OF TRANSLATORS\n" +"Your emails" +msgstr "Alexey.Kouznetsov@GMail.com" + +#: alsa-sound-configuration.cpp:258 +msgid "context-card-plus-device-number" +msgstr "" + +#: alsa-sound.cpp:40 +msgid "Advanced Linux Sound Architecture (ALSA) Support" +msgstr "Поддержка Расширенной звуковой архитектуры Linux (ALSA)" + +#: alsa-sound.cpp:48 +msgid "KRadio ALSA Sound Plugin" +msgstr "Модуль ALSA для KRadio" + +#: alsa-sound.cpp:196 +msgid "ALSA Sound" +msgstr "ALSA" + +#: alsa-sound.cpp:197 +msgid "ALSA Sound Device Options" +msgstr "Параметры звука для драйвера ALSA" + +#: alsa-sound.cpp:553 +msgid "ALSA Plugin: cannot write data for device plughw:%1,%2" +msgstr "Модуль ALSA: не могу записать данные в устройство plughw:%1,%2" + +#: alsa-sound.cpp:558 +msgid "ALSA Plugin: buffer underrun for device plughw:%1,%2" +msgstr "Модуль ALSA: нехватка данных в буфере устройства plughw:%1,%2" + +#: alsa-sound.cpp:611 +msgid "ALSA Plugin: cannot read data from device plughw:%1,%2" +msgstr "Модуль ALSA: не могу прочесть данные с устройства plughw:%1,%2" + +#: alsa-sound.cpp:616 +msgid "" +"ALSA Plugin: buffer overrun for device plughw:%1,%2 (buffersize=%3, buffer=%" +"4)" +msgstr "" +"Модуль ALSA: переполнение буфера устройства plughw:%1,%2 (размер буфера=%3, " +"буфер=%4)" + +#: alsa-sound.cpp:626 +msgid "internal stream, not stored (%1)" +msgstr "" + +#: alsa-sound.cpp:736 +msgid "ALSA Plugin: Error opening PCM device %1" +msgstr "Модуль ALSA: Ошибка при открытии устройства PCM: %1" + +#: alsa-sound.cpp:741 +msgid "ALSA Plugin: Can not configure PCM device %1" +msgstr "Модуль ALSA: не могу настроить устройство PCM %1" + +#: alsa-sound.cpp:748 +msgid "ALSA Plugin: Error setting access for %1" +msgstr "" + +#: alsa-sound.cpp:758 +msgid "ALSA Plugin: Error setting sample format for %1" +msgstr "Модуль ALSA: ошибка при установке формата данных для %1" + +#: alsa-sound.cpp:764 +msgid "ALSA Plugin: Error setting channels for %1" +msgstr "Модуль ALSA: ошибка при установке числа каналов для %1" + +#: alsa-sound.cpp:771 +msgid "ALSA Plugin: Error setting rate for %1" +msgstr "Модуль ALSA: ошибка при установке частоты дискретизации для %1" + +#: alsa-sound.cpp:775 +msgid "" +"ALSA Plugin: The rate %1 Hz is not supported by your hardware %2. Using %3 " +"Hz instead" +msgstr "" +"Модуль ALSA: частота дискретизации %1 Гц не поддерживается Вашим " +"оборудованием %2. Вместо неё использую %3 Гц." + +#: alsa-sound.cpp:781 +msgid "ALSA Plugin: Error setting period size for %1" +msgstr "" + +#: alsa-sound.cpp:817 +msgid "ALSA Plugin: Error setting HW params" +msgstr "Модуль ALSA: ошибка при установке параметров оборудоания" + +#: alsa-sound.cpp:822 +msgid "ALSA Plugin: Error getting period size for %1" +msgstr "" + +#: alsa-sound.cpp:920 +msgid "ALSA Plugin: Error opening mixer" +msgstr "Модуль ALSA: ошибка при открытии микшера" + +#: alsa-sound.cpp:927 +msgid "ALSA Plugin: ERROR: snd_mixer_attach for card %1" +msgstr "Модуль ALSA: ошибка при вызове функции snd_mixer_attach для платы %1" + +#: alsa-sound.cpp:934 +msgid "ALSA Plugin: Error: snd_mixer_selem_register for card %1" +msgstr "" +"Модуль ALSA: ошибка при вызове функции snd_mixer_selem_register для платы %1" + +#: alsa-sound.cpp:938 +msgid "ALSA Plugin: Error: snd_mixer_load for card %1" +msgstr "Модуль ALSA: ошибка при вызове функции snd_mixer_load для платы %1" + +#: alsa-sound.cpp:1006 +msgid "context-mixername-number" +msgstr "context-mixername-number" + +#: alsa-sound.cpp:1053 +msgid "context-mixerelement-name-number" +msgstr "context-mixerelement-name-number" + +#: alsa-sound.cpp:1206 alsa-sound.cpp:1236 +msgid "error while reading volume from hwplug:%1,%2" +msgstr "Ошибка считывания громкости устройства hwplug:%1,%2" + +#: alsa-sound.cpp:1269 alsa-sound.cpp:1304 +msgid "error while writing volume %1 to hwplug:%2,%3" +msgstr "Ошибка при записи громкости %1 в устройство hwplug:%2,%3" + +#: alsa-sound.cpp:1327 +msgid "error while setting capture switch %1 for hwplug:%2,%3" +msgstr "Ошибка при установке флажка записи %1 для устройства hwplug:%2,%3" + +#: alsa-sound.cpp:1448 +msgid "ALSA Sound Device %1" +msgstr "Устройство ALSA: %1" |