aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/Output/alsa
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/Output/alsa')
-rw-r--r--src/plugins/Output/alsa/CMakeLists.txt65
-rw-r--r--src/plugins/Output/alsa/alsa.pro37
-rw-r--r--src/plugins/Output/alsa/outputalsa.cpp539
-rw-r--r--src/plugins/Output/alsa/outputalsa.h85
-rw-r--r--src/plugins/Output/alsa/outputalsafactory.cpp63
-rw-r--r--src/plugins/Output/alsa/outputalsafactory.h48
-rw-r--r--src/plugins/Output/alsa/settingsdialog.cpp237
-rw-r--r--src/plugins/Output/alsa/settingsdialog.h59
-rw-r--r--src/plugins/Output/alsa/settingsdialog.ui261
-rw-r--r--src/plugins/Output/alsa/translations/alsa_plugin_cs.ts90
-rw-r--r--src/plugins/Output/alsa/translations/alsa_plugin_ru.qmbin0 -> 1580 bytes
-rw-r--r--src/plugins/Output/alsa/translations/alsa_plugin_ru.ts89
-rw-r--r--src/plugins/Output/alsa/translations/translations.qrc6
13 files changed, 1579 insertions, 0 deletions
diff --git a/src/plugins/Output/alsa/CMakeLists.txt b/src/plugins/Output/alsa/CMakeLists.txt
new file mode 100644
index 000000000..92f7af8d9
--- /dev/null
+++ b/src/plugins/Output/alsa/CMakeLists.txt
@@ -0,0 +1,65 @@
+project(libalsa)
+
+cmake_minimum_required(VERSION 2.4.0)
+
+
+INCLUDE(UsePkgConfig)
+INCLUDE(FindQt4)
+
+find_package(Qt4 REQUIRED) # find and setup Qt4 for this project
+include(${QT_USE_FILE})
+
+# qt plugin
+ADD_DEFINITIONS( -Wall )
+ADD_DEFINITIONS(${QT_DEFINITIONS})
+ADD_DEFINITIONS(-DQT_PLUGIN)
+ADD_DEFINITIONS(-DQT_NO_DEBUG)
+ADD_DEFINITIONS(-DQT_SHARED)
+ADD_DEFINITIONS(-DQT_THREAD)
+
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+SET(QT_INCLUDES
+ ${QT_INCLUDES}
+ ${CMAKE_CURRENT_BINARY_DIR}/../../../
+)
+
+# libqmmp
+include_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+link_directories(${CMAKE_CURRENT_BINARY_DIR}/../../../)
+
+SET(libalsa_SRCS
+ outputalsa.cpp
+ outputalsafactory.cpp
+ settingsdialog.cpp
+)
+
+SET(libalsa_MOC_HDRS
+ outputalsa.h
+ outputalsafactory.h
+ settingsdialog.h
+)
+
+SET(libalsa_RCCS translations/translations.qrc)
+
+QT4_ADD_RESOURCES(libalsa_RCC_SRCS ${libalsa_RCCS})
+
+QT4_WRAP_CPP(libalsa_MOC_SRCS ${libalsa_MOC_HDRS})
+
+# user interface
+
+
+SET(libalsa_UIS
+ settingsdialog.ui
+)
+
+QT4_WRAP_UI(libalsa_UIS_H ${libalsa_UIS})
+# Don't forget to include output directory, otherwise
+# the UI file won't be wrapped!
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+ADD_LIBRARY(alsa SHARED ${libalsa_SRCS} ${libalsa_MOC_SRCS} ${libalsa_UIS_H}
+ ${libalsa_RCC_SRCS})
+target_link_libraries(alsa ${QT_LIBRARIES} -lqmmp -lasound)
+install(TARGETS alsa DESTINATION ${LIB_DIR}/qmmp/Output PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ)
+
diff --git a/src/plugins/Output/alsa/alsa.pro b/src/plugins/Output/alsa/alsa.pro
new file mode 100644
index 000000000..465fabe4a
--- /dev/null
+++ b/src/plugins/Output/alsa/alsa.pro
@@ -0,0 +1,37 @@
+# ???? ?????? ? KDevelop ?????????? qmake.
+# -------------------------------------------
+# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Output/alsa
+# ???? - ??????????:
+
+include(../../plugins.pri)
+
+HEADERS += outputalsa.h \
+ outputalsafactory.h \
+ settingsdialog.h
+SOURCES += outputalsa.cpp \
+ outputalsafactory.cpp \
+ settingsdialog.cpp
+
+TARGET=$$PLUGINS_PREFIX/Output/alsa
+QMAKE_CLEAN =$$PLUGINS_PREFIX/Output/libalsa.so
+
+
+INCLUDEPATH += ../../../qmmp
+QMAKE_LIBDIR += ../../../../lib
+
+CONFIG += release \
+warn_on \
+thread \
+plugin
+TEMPLATE = lib
+LIBS += -lqmmp -lasound
+FORMS += settingsdialog.ui
+#TRANSLATIONS = translations/alsa_plugin_ru.ts
+#RESOURCES = translations/translations.qrc
+
+isEmpty (LIB_DIR){
+LIB_DIR = /lib
+}
+
+target.path = $$LIB_DIR/qmmp/Output
+INSTALLS += target
diff --git a/src/plugins/Output/alsa/outputalsa.cpp b/src/plugins/Output/alsa/outputalsa.cpp
new file mode 100644
index 000000000..66c031bbb
--- /dev/null
+++ b/src/plugins/Output/alsa/outputalsa.cpp
@@ -0,0 +1,539 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QObject>
+#include <QApplication>
+#include <QtGlobal>
+#include <QDir>
+#include <QSettings>
+#include <QTimer>
+
+#include <stdio.h>
+#include <string.h>
+#include <iostream>
+
+#include "outputalsa.h"
+#include "constants.h"
+#include "buffer.h"
+#include "visual.h"
+
+OutputALSA::OutputALSA(QObject * parent, bool useVolume)
+ : Output(parent), m_inited(FALSE), m_pause(FALSE), m_play(FALSE),
+ m_userStop(FALSE), m_totalWritten(0), m_currentSeconds(-1),
+ m_bps(-1), m_frequency(-1), m_channels(-1), m_precision(-1)
+{
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ QString dev_name = settings.value("ALSA/device","default").toString();
+ pcm_name = strdup(dev_name.toAscii().data());
+ stream = SND_PCM_STREAM_PLAYBACK;
+ snd_pcm_hw_params_alloca(&hwparams);
+ pcm_handle = 0;
+ //alsa mixer
+ mixer = 0;
+ if (useVolume)
+ {
+ QString card = settings.value("ALSA/mixer_card","hw:0").toString();
+ QString dev = settings.value("ALSA/mixer_device", "PCM").toString();
+ setupMixer(card, dev);
+ }
+}
+
+OutputALSA::~OutputALSA()
+{
+ uninitialize();
+ free (pcm_name);
+ if (mixer)
+ snd_mixer_close(mixer);
+}
+
+void OutputALSA::stop()
+{
+ m_userStop = TRUE;
+}
+
+void OutputALSA::status()
+{
+ long ct = (m_totalWritten - latency()) / m_bps;
+
+ if (ct < 0)
+ ct = 0;
+
+ if (ct > m_currentSeconds)
+ {
+ m_currentSeconds = ct;
+ dispatch(m_currentSeconds, m_totalWritten, m_rate,
+ m_frequency, m_precision, m_channels);
+ }
+}
+
+long OutputALSA::written()
+{
+ return m_totalWritten;
+}
+
+void OutputALSA::seek(long pos)
+{
+ m_totalWritten = (pos * m_bps);
+ m_currentSeconds = -1;
+}
+
+void OutputALSA::configure(long freq, int chan, int prec, int brate)
+{
+ // we need to configure
+ if (freq != m_frequency || chan != m_channels || prec != m_precision)
+ {
+ m_frequency = freq;
+ m_channels = chan;
+ m_precision = prec;
+ m_bps = freq * chan * (prec / 8);
+ snd_pcm_hw_params_alloca(&hwparams);
+ if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
+ {
+ qWarning("OutputALSA: Can not configure this PCM device.");
+ return;
+ }
+
+ uint rate = m_frequency; /* Sample rate */
+ uint exact_rate = m_frequency; /* Sample rate returned by */
+
+ /* load settings from config */
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ uint buffer_time = settings.value("buffer_time",500).toUInt()*1000;
+ uint period_time = settings.value("period_time",100).toUInt()*1000;
+ settings.endGroup();
+
+ if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
+ {
+ qWarning("OutputALSA: Error setting access.");
+ return;
+ }
+
+
+ if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
+ {
+ qDebug("OutputALSA: Error setting format.");
+ return;
+ }
+
+
+ exact_rate = rate;// = 11000;
+ qDebug("OutputALSA: frequency=%d, channels=%d, bitrate=%d",
+ rate, chan, brate);
+ if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0)
+ {
+ qWarning("OutputALSA: Error setting rate.\n");
+ return;
+ }
+ if (rate != exact_rate)
+ {
+ qWarning("OutputALSA: The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.", rate, exact_rate);
+ }
+
+ uint c = m_channels;
+ if (snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &c) < 0)
+ {
+ qWarning("OutputALSA: Error setting channels.");
+ return;
+ }
+
+ if (snd_pcm_hw_params_set_period_time_near(pcm_handle, hwparams,
+ &period_time ,0) < 0 )
+ {
+ qWarning("OutputALSA: Error setting HW buffer.");
+ return;
+ }
+ if (snd_pcm_hw_params_set_buffer_time_near(pcm_handle, hwparams,
+ &buffer_time ,0) < 0 )
+ {
+ qWarning("Error setting HW buffer.\n");
+ return;
+ }
+ if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
+ {
+ qWarning("OutputALSA: Error setting HW params.");
+ return;
+ }
+ }
+}
+
+void OutputALSA::reset()
+{
+ if (pcm_handle)
+ {
+ snd_pcm_close(pcm_handle);
+ pcm_handle = 0;
+ }
+ if (snd_pcm_open(&pcm_handle, pcm_name, stream, SND_PCM_NONBLOCK) < 0)
+ {
+ qWarning ("OutputALSA: Error opening PCM device %s", pcm_name);
+ return;
+ }
+}
+
+
+void OutputALSA::pause()
+{
+ if (!m_play)
+ return;
+ m_pause = (m_pause) ? FALSE : TRUE;
+ OutputState::Type state = m_pause ? OutputState::Paused: OutputState::Playing;
+ dispatch(state);
+}
+
+bool OutputALSA::initialize()
+{
+ m_inited = m_pause = m_play = m_userStop = FALSE;
+
+ if (!pcm_handle < 0)
+ return FALSE;
+
+ m_currentSeconds = -1;
+ m_totalWritten = 0;
+ if (snd_pcm_open(&pcm_handle, pcm_name, stream, SND_PCM_NONBLOCK) < 0)
+ {
+ qWarning ("OutputALSA: Error opening PCM device %s", pcm_name);
+ return FALSE;
+ }
+
+ m_inited = TRUE;
+ return TRUE;
+}
+
+
+long OutputALSA::latency()
+{
+ long used = 0;
+
+ /*if (! m_pause)
+ {
+ if (ioctl(audio_fd, SNDCTL_DSP_GETODELAY, &used) == -1)
+ used = 0;
+ }*/
+
+ return used;
+}
+
+void OutputALSA::run()
+{
+
+ mutex()->lock ();
+ if (! m_inited)
+ {
+ mutex()->unlock();
+ return;
+ }
+
+ m_play = TRUE;
+
+ mutex()->unlock();
+
+ Buffer *b = 0;
+ bool done = FALSE;
+ unsigned long n = 0;
+ long m = 0;
+ snd_pcm_uframes_t l;
+
+ dispatch(OutputState::Playing);
+
+ while (! done)
+ {
+ mutex()->lock ();
+ recycler()->mutex()->lock ();
+
+ done = m_userStop;
+
+ while (! done && (recycler()->empty() || m_pause))
+ {
+ mutex()->unlock();
+ recycler()->cond()->wakeOne();
+ recycler()->cond()->wait(recycler()->mutex());
+ mutex()->lock ();
+ done = m_userStop;
+ status();
+ }
+
+ if (! b)
+ {
+ b = recycler()->next();
+ if (b->rate)
+ m_rate = b->rate;
+ }
+
+ recycler()->cond()->wakeOne();
+ recycler()->mutex()->unlock();
+
+ if (b)
+ {
+ l = snd_pcm_bytes_to_frames(pcm_handle, b->nbytes - n);
+ while (l>0)
+ {
+ m = snd_pcm_writei (pcm_handle, b->data+n, l);
+
+ if (m > 0)
+ {
+ n += snd_pcm_frames_to_bytes(pcm_handle, m);
+ l -= m;
+ status();
+ dispatchVisual(b, m_totalWritten, m_channels, m_precision);
+ }
+ else if (m == -EAGAIN)
+ {
+ mutex()->unlock();
+ snd_pcm_wait(pcm_handle, 500);
+ mutex()->lock ();
+ }
+ else if (m == -EPIPE)
+ {
+ qDebug ("OutputALSA: underrun!");
+ if ((m = snd_pcm_prepare(pcm_handle)) < 0)
+ {
+ qDebug ("OutputALSA: Can't recover after underrun: %s",
+ snd_strerror(m));
+ /* TODO: reopen the device */
+ break;
+ }
+ }
+ else if (m == -ESTRPIPE)
+ {
+ qDebug ("OutputALSA: Suspend, trying to resume");
+ while ((m = snd_pcm_resume(pcm_handle))
+ == -EAGAIN)
+ sleep (1);
+ if (m < 0)
+ {
+ qDebug ("OutputALSA: Failed, restarting");
+ if ((m = snd_pcm_prepare(pcm_handle))
+ < 0)
+ {
+ qDebug ("OutputALSA: Failed to restart device: %s.",
+ snd_strerror(m));
+ break;
+ }
+ }
+ }
+ else if (m < 0)
+ {
+ qDebug ("OutputALSA: Can't play: %s", snd_strerror(m));
+ break;
+ }
+ }
+ status();
+ // force buffer change
+ m_totalWritten += n;
+ n = b->nbytes;
+ m = 0;
+ }
+ if (n == b->nbytes)
+ {
+ recycler()->mutex()->lock ();
+ recycler()->done();
+ recycler()->mutex()->unlock();
+ b = 0;
+ n = 0;
+ }
+ mutex()->unlock();
+ }
+
+ mutex()->lock ();
+
+ m_play = FALSE;
+
+ dispatch(OutputState::Stopped);
+
+ mutex()->unlock();
+
+}
+
+void OutputALSA::uninitialize()
+{
+ if (!m_inited)
+ return;
+ m_inited = FALSE;
+ m_pause = FALSE;
+ m_play = FALSE;
+ m_userStop = FALSE;
+ m_totalWritten = 0;
+ m_currentSeconds = -1;
+ m_bps = -1;
+ m_frequency = -1;
+ m_channels = -1;
+ m_precision = -1;
+ if (pcm_handle)
+ {
+ qDebug("OutputALSA: closing pcm_handle");
+ snd_pcm_close(pcm_handle);
+ pcm_handle = 0;
+ }
+ dispatch(OutputState::Stopped);
+}
+/* ****** MIXER ******* */
+
+int OutputALSA::setupMixer(QString card, QString device)
+{
+ char *name;
+ long int a, b;
+ long alsa_min_vol = 0, alsa_max_vol = 100;
+ int err, index;
+
+ qDebug("OutputALSA: setupMixer()");
+
+ if ((err = getMixer(&mixer, card)) < 0)
+ return err;
+
+ parseMixerName(device.toAscii().data(), &name, &index);
+
+ pcm_element = getMixerElem(mixer, name, index);
+
+ free(name);
+
+ if (!pcm_element)
+ {
+ qWarning("OutputALSA: Failed to find mixer element");
+ return -1;
+ }
+
+ /* This hack was copied from xmms.
+ * Work around a bug in alsa-lib up to 1.0.0rc2 where the
+ * new range don't take effect until the volume is changed.
+ * This hack should be removed once we depend on Alsa 1.0.0.
+ */
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT, &a);
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT, &b);
+
+ snd_mixer_selem_get_playback_volume_range(pcm_element,
+ &alsa_min_vol, &alsa_max_vol);
+ snd_mixer_selem_set_playback_volume_range(pcm_element, 0, 100);
+
+ if (alsa_max_vol == 0)
+ {
+ pcm_element = NULL;
+ return -1;
+ }
+
+ setVolume(a * 100 / alsa_max_vol, b * 100 / alsa_max_vol);
+
+ qDebug("OutputALSA: setupMixer() succes");
+
+ return 0;
+}
+
+void OutputALSA::parseMixerName(char *str, char **name, int *index)
+{
+ char *end;
+
+ while (isspace(*str))
+ str++;
+
+ if ((end = strchr(str, ',')) != NULL)
+ {
+ *name = strndup(str, end - str);
+ end++;
+ *index = atoi(end);
+ }
+ else
+ {
+ *name = strdup(str);
+ *index = 0;
+ }
+}
+
+snd_mixer_elem_t* OutputALSA::getMixerElem(snd_mixer_t *mixer, char *name, int index)
+{
+ snd_mixer_selem_id_t* selem_id;
+ snd_mixer_elem_t* elem;
+ snd_mixer_selem_id_alloca(&selem_id);
+
+ if (index != -1)
+ snd_mixer_selem_id_set_index(selem_id, index);
+ if (name != NULL)
+ snd_mixer_selem_id_set_name(selem_id, name);
+
+ elem = snd_mixer_find_selem(mixer, selem_id);
+
+ return elem;
+}
+
+void OutputALSA::setVolume(int l, int r)
+{
+
+ if (!pcm_element)
+ return;
+
+ snd_mixer_selem_set_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT, l);
+ snd_mixer_selem_set_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT, r);
+}
+
+void OutputALSA::volume(int * l, int * r)
+{
+ if (!pcm_element)
+ return;
+ snd_mixer_handle_events(mixer);
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_LEFT, (long int*)l);
+ snd_mixer_selem_get_playback_volume(pcm_element,
+ SND_MIXER_SCHN_FRONT_RIGHT, (long int*)r);
+}
+
+int OutputALSA::getMixer(snd_mixer_t **mixer, QString card)
+{
+ char *dev;
+ int err;
+
+
+ dev = strdup(card.toAscii().data());
+
+ if ((err = snd_mixer_open(mixer, 0)) < 0)
+ {
+ qWarning("OutputALSA: Failed to open empty mixer: %s",
+ snd_strerror(-err));
+ mixer = NULL;
+ return -1;
+ }
+ if ((err = snd_mixer_attach(*mixer, dev)) < 0)
+ {
+ qWarning("OutputALSA: Attaching to mixer %s failed: %s",
+ dev, snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
+ {
+ qWarning("OutputALSA: Failed to register mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_load(*mixer)) < 0)
+ {
+ qWarning("OutputALSA: Failed to load mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+
+ free(dev);
+
+ return (*mixer != NULL);
+}
+
+
+
diff --git a/src/plugins/Output/alsa/outputalsa.h b/src/plugins/Output/alsa/outputalsa.h
new file mode 100644
index 000000000..f3dd222fe
--- /dev/null
+++ b/src/plugins/Output/alsa/outputalsa.h
@@ -0,0 +1,85 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef OUTPUTALSA_H
+#define OUTPUTALSA_H
+
+class OutputALSA;
+
+#include <output.h>
+#include <QObject>
+extern "C" {
+#include <alsa/asoundlib.h>
+}
+#if defined( Q_OS_WIN32 )
+#include <dsound.h>
+#include "constants.h"
+#endif
+
+
+class OutputALSA : public Output
+{
+Q_OBJECT
+public:
+ OutputALSA(QObject * parent = 0, bool useVolume = TRUE);
+ ~OutputALSA();
+
+ bool initialize();
+ bool isInitialized() const { return m_inited; }
+ void uninitialize();
+ void configure(long, int, int, int);
+ void stop();
+ void pause();
+ long written();
+ long latency();
+ void seek(long);
+ void setVolume(int l, int r);
+ void volume(int *l, int *r);
+ void checkVolume();
+
+private:
+ // thread run function
+ void run();
+
+ // helper functions
+ void reset();
+ void status();
+
+ bool m_inited, m_pause, m_play, m_userStop;
+ long m_totalWritten, m_currentSeconds, m_bps;
+ int m_rate, m_frequency, m_channels, m_precision;
+ //alsa
+ snd_pcm_t *pcm_handle;
+ snd_pcm_stream_t stream;
+ snd_pcm_hw_params_t *hwparams;
+ char *pcm_name;
+ //alsa
+
+ //alsa mixer
+ int setupMixer(QString card, QString device);
+ void parseMixerName(char *str, char **name, int *index);
+ int getMixer(snd_mixer_t **mixer, QString card);
+ snd_mixer_elem_t* getMixerElem(snd_mixer_t *mixer, char *name, int index);
+ snd_mixer_t *mixer;
+ snd_mixer_elem_t *pcm_element;
+};
+
+
+#endif // OUTPUTALSA_H
diff --git a/src/plugins/Output/alsa/outputalsafactory.cpp b/src/plugins/Output/alsa/outputalsafactory.cpp
new file mode 100644
index 000000000..641ff1278
--- /dev/null
+++ b/src/plugins/Output/alsa/outputalsafactory.cpp
@@ -0,0 +1,63 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include <QtGui>
+
+#include "settingsdialog.h"
+#include "outputalsa.h"
+#include "outputalsafactory.h"
+
+
+const OutputProperties OutputALSAFactory::properties() const
+{
+ OutputProperties properties;
+ properties.name = tr("ALSA Plugin");
+ properties.hasAbout = TRUE;
+ properties.hasSettings = TRUE;
+ return properties;
+}
+
+Output* OutputALSAFactory::create(QObject* parent, bool volume)
+{
+ return new OutputALSA(parent, volume);
+}
+
+void OutputALSAFactory::showSettings(QWidget* parent)
+{
+ SettingsDialog *s = new SettingsDialog(parent);
+ s -> show();
+}
+
+void OutputALSAFactory::showAbout(QWidget *parent)
+{
+ QMessageBox::about (parent, tr("About ALSA Output Plugin"),
+ tr("Qmmp ALSA Output Plugin")+"\n"+
+ tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>"));
+}
+
+QTranslator *OutputALSAFactory::createTranslator(QObject *parent)
+{
+ QTranslator *translator = new QTranslator(parent);
+ QString locale = QLocale::system().name();
+ translator->load(QString(":/alsa_plugin_") + locale);
+ return translator;
+}
+
+Q_EXPORT_PLUGIN(OutputALSAFactory)
diff --git a/src/plugins/Output/alsa/outputalsafactory.h b/src/plugins/Output/alsa/outputalsafactory.h
new file mode 100644
index 000000000..afaa18358
--- /dev/null
+++ b/src/plugins/Output/alsa/outputalsafactory.h
@@ -0,0 +1,48 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef OUTPUTALSAFACTORY_H
+#define OUTPUTALSAFACTORY_H
+
+
+#include <QObject>
+#include <QString>
+#include <QIODevice>
+#include <QWidget>
+
+#include <output.h>
+#include <outputfactory.h>
+
+
+class OutputALSAFactory : public QObject,
+ OutputFactory
+{
+Q_OBJECT
+Q_INTERFACES(OutputFactory);
+
+public:
+ const OutputProperties properties() const;
+ Output* create(QObject* parent, bool volume);
+ void showSettings(QWidget* parent);
+ void showAbout(QWidget *parent);
+ QTranslator *createTranslator(QObject *parent);
+
+};
+
+#endif
diff --git a/src/plugins/Output/alsa/settingsdialog.cpp b/src/plugins/Output/alsa/settingsdialog.cpp
new file mode 100644
index 000000000..89c6cae84
--- /dev/null
+++ b/src/plugins/Output/alsa/settingsdialog.cpp
@@ -0,0 +1,237 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#include <QSettings>
+#include <QDir>
+
+extern "C"
+{
+#include <alsa/asoundlib.h>
+}
+
+#include "settingsdialog.h"
+
+SettingsDialog::SettingsDialog ( QWidget *parent )
+ : QDialog ( parent )
+{
+ ui.setupUi ( this );
+ setAttribute ( Qt::WA_DeleteOnClose );
+ ui.deviceComboBox->setEditable ( TRUE );
+ getCards();
+ connect (ui.deviceComboBox, SIGNAL(activated(int)),SLOT(setText(int)));
+ connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings()));
+ connect(ui.mixerCardComboBox, SIGNAL(activated(int)), SLOT(showMixerDevices(int)));
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ ui.deviceComboBox->setEditText(settings.value("device","default").toString());
+ ui.bufferSpinBox->setValue(settings.value("buffer_time",500).toInt());
+ ui.periodSpinBox->setValue(settings.value("period_time",100).toInt());
+
+ int d = m_cards.indexOf(settings.value("mixer_card","hw:0").toString());
+ if (d >= 0)
+ ui.mixerCardComboBox->setCurrentIndex(d);
+
+ showMixerDevices(ui.mixerCardComboBox->currentIndex ());
+ d = ui.mixerDeviceComboBox->findText(settings.value("mixer_device",
+ "PCM").toString());
+
+ if (d >= 0)
+ ui.mixerDeviceComboBox->setCurrentIndex(d);
+
+ settings.endGroup();
+}
+
+
+SettingsDialog::~SettingsDialog()
+{}
+
+void SettingsDialog::getCards()
+{
+ int card = -1, err;
+
+ m_devices.clear();
+ m_devices << "default";
+ ui.deviceComboBox->addItem("Default PCM device (default)");
+
+ if ((err = snd_card_next(&card)) !=0)
+ qWarning("SettingsDialog (ALSA): snd_next_card() failed: %s",
+ snd_strerror(-err));
+
+ while (card > -1)
+ {
+ getCardDevices(card);
+ m_cards << QString("hw:%1").arg(card);
+ if ((err = snd_card_next(&card)) !=0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_next_card() failed: %s",
+ snd_strerror(-err));
+ break;
+ }
+ }
+}
+
+void SettingsDialog::getCardDevices(int card)
+{
+ int pcm_device = -1, err;
+ snd_pcm_info_t *pcm_info;
+ snd_ctl_t *ctl;
+ char dev[64], *card_name;
+
+ sprintf(dev, "hw:%i", card);
+
+ if ((err = snd_ctl_open(&ctl, dev, 0)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_ctl_open() failed: %s",
+ snd_strerror(-err));
+ return;
+ }
+
+ if ((err = snd_card_get_name(card, &card_name)) != 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_card_get_name() failed: %s",
+ snd_strerror(-err));
+ card_name = "Unknown soundcard";
+ }
+ ui.mixerCardComboBox->addItem(QString(card_name));
+
+ snd_pcm_info_alloca(&pcm_info);
+
+ qDebug("SettingsDialog (ALSA): detected sound cards:");
+
+ for (;;)
+ {
+ QString device;
+ if ((err = snd_ctl_pcm_next_device(ctl, &pcm_device)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): snd_ctl_pcm_next_device() failed: %s",
+ snd_strerror(-err));
+ pcm_device = -1;
+ }
+ if (pcm_device < 0)
+ break;
+
+ snd_pcm_info_set_device(pcm_info, pcm_device);
+ snd_pcm_info_set_subdevice(pcm_info, 0);
+ snd_pcm_info_set_stream(pcm_info, SND_PCM_STREAM_PLAYBACK);
+
+ if ((err = snd_ctl_pcm_info(ctl, pcm_info)) < 0)
+ {
+ if (err != -ENOENT)
+ qWarning("SettingsDialog (ALSA): get_devices_for_card(): "
+ "snd_ctl_pcm_info() "
+ "failed (%d:%d): %s.", card,
+ pcm_device, snd_strerror(-err));
+ }
+ device = QString("hw:%1,%2").arg(card).arg(pcm_device);
+ m_devices << device;
+ QString str;
+ str = QString(card_name) + ": "+
+ snd_pcm_info_get_name(pcm_info)+" ("+device+")";
+ qDebug(str.toAscii());
+ ui.deviceComboBox->addItem(str);
+ }
+
+ snd_ctl_close(ctl);
+}
+
+void SettingsDialog::getMixerDevices(QString card)
+{
+ ui.mixerDeviceComboBox->clear();
+ int err;
+ snd_mixer_t *mixer;
+ snd_mixer_elem_t *current;
+
+ if ((err = getMixer(&mixer, card)) < 0)
+ return;
+
+ current = snd_mixer_first_elem(mixer);
+
+ while (current)
+ {
+ const char *sname = snd_mixer_selem_get_name(current);
+ if (snd_mixer_selem_is_active(current) &&
+ snd_mixer_selem_has_playback_volume(current))
+ ui.mixerDeviceComboBox->addItem(QString(sname));
+ current = snd_mixer_elem_next(current);
+ }
+}
+
+void SettingsDialog::setText(int n)
+{
+ ui.deviceComboBox->setEditText(m_devices.at(n));
+}
+
+void SettingsDialog::writeSettings()
+{
+ qDebug("SettingsDialog (ALSA):: writeSettings()");
+ QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat);
+ settings.beginGroup("ALSA");
+ settings.setValue("device", ui.deviceComboBox->currentText ());
+ settings.setValue("buffer_time",ui.bufferSpinBox->value());
+ settings.setValue("period_time",ui.periodSpinBox->value());
+ QString card = m_cards.at(ui.mixerCardComboBox->currentIndex());
+ settings.setValue("mixer_card", card);
+ settings.setValue("mixer_device", ui.mixerDeviceComboBox->currentText ());
+ settings.endGroup();
+ accept();
+}
+
+int SettingsDialog::getMixer(snd_mixer_t **mixer, QString card)
+{
+ char *dev;
+ int err;
+
+ dev = strdup(QString(card).toAscii().data());
+ if ((err = snd_mixer_open(mixer, 0)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Failed to open empty mixer: %s", snd_strerror(-err));
+ mixer = NULL;
+ return -1;
+ }
+ if ((err = snd_mixer_attach(*mixer, dev)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Attaching to mixer %s failed: %s", dev, snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_selem_register(*mixer, NULL, NULL)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): "
+ "Failed to register mixer: %s", snd_strerror(-err));
+ return -1;
+ }
+ if ((err = snd_mixer_load(*mixer)) < 0)
+ {
+ qWarning("SettingsDialog (ALSA): alsa_get_mixer(): Failed to load mixer: %s",
+ snd_strerror(-err));
+ return -1;
+ }
+
+ free (dev);
+
+ return (*mixer != NULL);
+}
+
+void SettingsDialog::showMixerDevices(int d)
+{
+ if (0<=d && d<m_cards.size())
+ getMixerDevices(m_cards.at(d));
+}
+
diff --git a/src/plugins/Output/alsa/settingsdialog.h b/src/plugins/Output/alsa/settingsdialog.h
new file mode 100644
index 000000000..467b25a03
--- /dev/null
+++ b/src/plugins/Output/alsa/settingsdialog.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * Copyright (C) 2006 by Ilya Kotov *
+ * forkotov02@hotmail.ru *
+ * *
+ * 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. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+#ifndef SETTINGSDIALOG_H
+#define SETTINGSDIALOG_H
+
+#include <QDialog>
+
+extern "C"{
+#include <alsa/asoundlib.h>
+}
+//#include <alsa/pcm_plugin.h>
+
+#include "ui_settingsdialog.h"
+
+/**
+ @author Ilya Kotov <forkotov02@hotmail.ru>
+*/
+class SettingsDialog : public QDialog
+{
+Q_OBJECT
+public:
+ SettingsDialog(QWidget *parent = 0);
+
+ ~SettingsDialog();
+
+private slots:
+ void setText(int);
+ void writeSettings();
+ void showMixerDevices(int);
+
+private:
+ Ui::SettingsDialog ui;
+ void getCards();
+ void getCardDevices(int card);
+ void getMixerDevices(QString card);
+ int getMixer(snd_mixer_t **mixer, QString card);
+ QStringList m_devices;
+ QList <QString> m_cards;
+
+};
+
+#endif
diff --git a/src/plugins/Output/alsa/settingsdialog.ui b/src/plugins/Output/alsa/settingsdialog.ui
new file mode 100644
index 000000000..2f9a20753
--- /dev/null
+++ b/src/plugins/Output/alsa/settingsdialog.ui
@@ -0,0 +1,261 @@
+<ui version="4.0" >
+ <class>SettingsDialog</class>
+ <widget class="QDialog" name="SettingsDialog" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>403</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>ALSA Plugin Settings</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="0" colspan="3" >
+ <widget class="QTabWidget" name="tabWidget" >
+ <property name="currentIndex" >
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tab" >
+ <attribute name="title" >
+ <string>Device Settings</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox" >
+ <property name="title" >
+ <string>Audio device</string>
+ </property>
+ <widget class="QComboBox" name="deviceComboBox" >
+ <property name="geometry" >
+ <rect>
+ <x>11</x>
+ <y>47</y>
+ <width>338</width>
+ <height>22</height>
+ </rect>
+ </property>
+ </widget>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="groupBox_2" >
+ <property name="title" >
+ <string>Mixer</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="0" column="1" >
+ <widget class="QComboBox" name="mixerCardComboBox" />
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label_3" >
+ <property name="text" >
+ <string>Mixer card:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_4" >
+ <property name="text" >
+ <string>Mixer device:</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QComboBox" name="mixerDeviceComboBox" />
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <widget class="QWidget" name="tab_2" >
+ <attribute name="title" >
+ <string>Advanced Settings</string>
+ </attribute>
+ <layout class="QVBoxLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="groupBox_3" >
+ <property name="title" >
+ <string>Soundcard</string>
+ </property>
+ <layout class="QGridLayout" >
+ <property name="margin" >
+ <number>9</number>
+ </property>
+ <property name="spacing" >
+ <number>6</number>
+ </property>
+ <item row="2" column="1" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>20</width>
+ <height>111</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>188</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QSpinBox" name="periodSpinBox" >
+ <property name="maximum" >
+ <number>5000</number>
+ </property>
+ <property name="minimum" >
+ <number>20</number>
+ </property>
+ <property name="value" >
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1" >
+ <widget class="QSpinBox" name="bufferSpinBox" >
+ <property name="maximum" >
+ <number>10000</number>
+ </property>
+ <property name="minimum" >
+ <number>200</number>
+ </property>
+ <property name="value" >
+ <number>500</number>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0" >
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Buffer time (ms):</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="text" >
+ <string>Period time (ms):</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>188</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </widget>
+ </item>
+ <item row="1" column="0" >
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" >
+ <size>
+ <width>191</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2" >
+ <widget class="QPushButton" name="cancelButton" >
+ <property name="text" >
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1" >
+ <widget class="QPushButton" name="okButton" >
+ <property name="text" >
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cancelButton</sender>
+ <signal>clicked()</signal>
+ <receiver>SettingsDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel" >
+ <x>338</x>
+ <y>283</y>
+ </hint>
+ <hint type="destinationlabel" >
+ <x>164</x>
+ <y>294</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/plugins/Output/alsa/translations/alsa_plugin_cs.ts b/src/plugins/Output/alsa/translations/alsa_plugin_cs.ts
new file mode 100644
index 000000000..3108490ec
--- /dev/null
+++ b/src/plugins/Output/alsa/translations/alsa_plugin_cs.ts
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="pl">
+<defaultcodec></defaultcodec>
+<context>
+ <name>OutputALSAFactory</name>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="30"/>
+ <source>ALSA Plugin</source>
+ <translation>Plugin ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="47"/>
+ <source>About ALSA Output Plugin</source>
+ <translation>O pluginu ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="48"/>
+ <source>Qmmp ALSA Output Plugin</source>
+ <translation>Výstupní plugin Qmmp ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="49"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Autor: Ilja Kotov &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="../settingsdialog.ui" line="13"/>
+ <source>ALSA Plugin Settings</source>
+ <translation>Nastavení pluginu ALSA</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="29"/>
+ <source>Device Settings</source>
+ <translation>Nastavení zařízení</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="41"/>
+ <source>Audio device</source>
+ <translation>Zvukové zařízení</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="58"/>
+ <source>Mixer</source>
+ <translation>Mixér</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="73"/>
+ <source>Mixer card:</source>
+ <translation>Zvuková karta:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="83"/>
+ <source>Mixer device:</source>
+ <translation>Ovládání hlasitosti:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="100"/>
+ <source>Advanced Settings</source>
+ <translation>Pokročilá nastavení</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="112"/>
+ <source>Soundcard</source>
+ <translation>Zvuková karta</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="176"/>
+ <source>Buffer time (ms):</source>
+ <translation>Velikost bufferu (ms):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="186"/>
+ <source>Period time (ms):</source>
+ <translation type="unfinished">Délka periody (ms):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="229"/>
+ <source>Cancel</source>
+ <translation>Zrušit</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="236"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/plugins/Output/alsa/translations/alsa_plugin_ru.qm b/src/plugins/Output/alsa/translations/alsa_plugin_ru.qm
new file mode 100644
index 000000000..78ed38962
--- /dev/null
+++ b/src/plugins/Output/alsa/translations/alsa_plugin_ru.qm
Binary files differ
diff --git a/src/plugins/Output/alsa/translations/alsa_plugin_ru.ts b/src/plugins/Output/alsa/translations/alsa_plugin_ru.ts
new file mode 100644
index 000000000..2c7296750
--- /dev/null
+++ b/src/plugins/Output/alsa/translations/alsa_plugin_ru.ts
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS><TS version="1.1" language="ru">
+<context>
+ <name>OutputALSAFactory</name>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="30"/>
+ <source>ALSA Plugin</source>
+ <translation>Модуль ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="47"/>
+ <source>About ALSA Output Plugin</source>
+ <translation>О модуле вывода ALSA</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="48"/>
+ <source>Qmmp ALSA Output Plugin</source>
+ <translation>Модуль вывода ALSA для Qmmp</translation>
+ </message>
+ <message>
+ <location filename="../outputalsafactory.cpp" line="49"/>
+ <source>Writen by: Ilya Kotov &lt;forkotov02@hotmail.ru&gt;</source>
+ <translation>Разработчик: Илья Котов &lt;forkotov02@hotmail.ru&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>SettingsDialog</name>
+ <message>
+ <location filename="../settingsdialog.ui" line="29"/>
+ <source>Device Settings</source>
+ <translation>Параметры устройства</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="41"/>
+ <source>Audio device</source>
+ <translation>Аудио устройство</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="58"/>
+ <source>Mixer</source>
+ <translation>Микшер</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="73"/>
+ <source>Mixer card:</source>
+ <translation>Карта микшера:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="83"/>
+ <source>Mixer device:</source>
+ <translation>Устройство микшера:</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="100"/>
+ <source>Advanced Settings</source>
+ <translation>Дополнительные настройки</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="112"/>
+ <source>Soundcard</source>
+ <translation>Звуковая карта</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="176"/>
+ <source>Buffer time (ms):</source>
+ <translation>Время буферизации (мс):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="186"/>
+ <source>Period time (ms):</source>
+ <translation>Время периода (мс):</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="229"/>
+ <source>Cancel</source>
+ <translation>Отмена</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="236"/>
+ <source>OK</source>
+ <translation>Применить</translation>
+ </message>
+ <message>
+ <location filename="../settingsdialog.ui" line="13"/>
+ <source>ALSA Plugin Settings</source>
+ <translation>Настройки модуля ALSA</translation>
+ </message>
+</context>
+</TS>
diff --git a/src/plugins/Output/alsa/translations/translations.qrc b/src/plugins/Output/alsa/translations/translations.qrc
new file mode 100644
index 000000000..beac1cd17
--- /dev/null
+++ b/src/plugins/Output/alsa/translations/translations.qrc
@@ -0,0 +1,6 @@
+<!DOCTYPE RCC>
+<RCC version="1.0">
+ <qresource>
+ <file>alsa_plugin_ru.qm</file>
+ </qresource>
+</RCC>