From 06d1877811fa6aa97dddc0e03bcde4e766928c87 Mon Sep 17 00:00:00 2001 From: vovanec Date: Thu, 7 Feb 2008 13:36:34 +0000 Subject: new directory structure git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@232 90c681e8-e032-0410-971d-27865f9a5e38 --- src/plugins/Output/alsa/CMakeLists.txt | 65 +++ src/plugins/Output/alsa/alsa.pro | 37 ++ src/plugins/Output/alsa/outputalsa.cpp | 539 +++++++++++++++++++++ src/plugins/Output/alsa/outputalsa.h | 85 ++++ src/plugins/Output/alsa/outputalsafactory.cpp | 63 +++ src/plugins/Output/alsa/outputalsafactory.h | 48 ++ src/plugins/Output/alsa/settingsdialog.cpp | 237 +++++++++ src/plugins/Output/alsa/settingsdialog.h | 59 +++ src/plugins/Output/alsa/settingsdialog.ui | 261 ++++++++++ .../Output/alsa/translations/alsa_plugin_cs.ts | 90 ++++ .../Output/alsa/translations/alsa_plugin_ru.qm | Bin 0 -> 1580 bytes .../Output/alsa/translations/alsa_plugin_ru.ts | 89 ++++ .../Output/alsa/translations/translations.qrc | 6 + 13 files changed, 1579 insertions(+) create mode 100644 src/plugins/Output/alsa/CMakeLists.txt create mode 100644 src/plugins/Output/alsa/alsa.pro create mode 100644 src/plugins/Output/alsa/outputalsa.cpp create mode 100644 src/plugins/Output/alsa/outputalsa.h create mode 100644 src/plugins/Output/alsa/outputalsafactory.cpp create mode 100644 src/plugins/Output/alsa/outputalsafactory.h create mode 100644 src/plugins/Output/alsa/settingsdialog.cpp create mode 100644 src/plugins/Output/alsa/settingsdialog.h create mode 100644 src/plugins/Output/alsa/settingsdialog.ui create mode 100644 src/plugins/Output/alsa/translations/alsa_plugin_cs.ts create mode 100644 src/plugins/Output/alsa/translations/alsa_plugin_ru.qm create mode 100644 src/plugins/Output/alsa/translations/alsa_plugin_ru.ts create mode 100644 src/plugins/Output/alsa/translations/translations.qrc (limited to 'src/plugins/Output/alsa') 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 +#include +extern "C" { +#include +} +#if defined( Q_OS_WIN32 ) +#include +#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 + +#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 ")); +} + +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 +#include +#include +#include + +#include +#include + + +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 +#include + +extern "C" +{ +#include +} + +#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 + +extern "C"{ +#include +} +//#include + +#include "ui_settingsdialog.h" + +/** + @author Ilya Kotov +*/ +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 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 @@ + + SettingsDialog + + + + 0 + 0 + 403 + 300 + + + + ALSA Plugin Settings + + + + 9 + + + 6 + + + + + 0 + + + + Device Settings + + + + 9 + + + 6 + + + + + Audio device + + + + + 11 + 47 + 338 + 22 + + + + + + + + + Mixer + + + + 9 + + + 6 + + + + + + + + Mixer card: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Mixer device: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + + + Advanced Settings + + + + 9 + + + 6 + + + + + Soundcard + + + + 9 + + + 6 + + + + + Qt::Vertical + + + + 20 + 111 + + + + + + + + Qt::Horizontal + + + + 188 + 20 + + + + + + + + 5000 + + + 20 + + + 100 + + + + + + + 10000 + + + 200 + + + 500 + + + + + + + Buffer time (ms): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Period time (ms): + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Horizontal + + + + 188 + 20 + + + + + + + + + + + + + + + Qt::Horizontal + + + + 191 + 20 + + + + + + + + Cancel + + + + + + + OK + + + + + + + + + cancelButton + clicked() + SettingsDialog + reject() + + + 338 + 283 + + + 164 + 294 + + + + + 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 @@ + + + + + OutputALSAFactory + + + ALSA Plugin + Plugin ALSA + + + + About ALSA Output Plugin + O pluginu ALSA + + + + Qmmp ALSA Output Plugin + Výstupní plugin Qmmp ALSA + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Autor: Ilja Kotov <forkotov02@hotmail.ru> + + + + SettingsDialog + + + ALSA Plugin Settings + Nastavení pluginu ALSA + + + + Device Settings + Nastavení zařízení + + + + Audio device + Zvukové zařízení + + + + Mixer + Mixér + + + + Mixer card: + Zvuková karta: + + + + Mixer device: + Ovládání hlasitosti: + + + + Advanced Settings + Pokročilá nastavení + + + + Soundcard + Zvuková karta + + + + Buffer time (ms): + Velikost bufferu (ms): + + + + Period time (ms): + Délka periody (ms): + + + + Cancel + Zrušit + + + + OK + OK + + + 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 Binary files /dev/null and b/src/plugins/Output/alsa/translations/alsa_plugin_ru.qm 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 @@ + + + + OutputALSAFactory + + + ALSA Plugin + Модуль ALSA + + + + About ALSA Output Plugin + О модуле вывода ALSA + + + + Qmmp ALSA Output Plugin + Модуль вывода ALSA для Qmmp + + + + Writen by: Ilya Kotov <forkotov02@hotmail.ru> + Разработчик: Илья Котов <forkotov02@hotmail.ru> + + + + SettingsDialog + + + Device Settings + Параметры устройства + + + + Audio device + Аудио устройство + + + + Mixer + Микшер + + + + Mixer card: + Карта микшера: + + + + Mixer device: + Устройство микшера: + + + + Advanced Settings + Дополнительные настройки + + + + Soundcard + Звуковая карта + + + + Buffer time (ms): + Время буферизации (мс): + + + + Period time (ms): + Время периода (мс): + + + + Cancel + Отмена + + + + OK + Применить + + + + ALSA Plugin Settings + Настройки модуля ALSA + + + 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 @@ + + + + alsa_plugin_ru.qm + + -- cgit v1.2.3-13-gbd6f