diff options
| author | vovanec <vovanec@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-02-07 13:36:34 +0000 |
|---|---|---|
| committer | vovanec <vovanec@90c681e8-e032-0410-971d-27865f9a5e38> | 2008-02-07 13:36:34 +0000 |
| commit | 06d1877811fa6aa97dddc0e03bcde4e766928c87 (patch) | |
| tree | c25462d0e58c3d58c728664440412bf4f16a49ec /src/plugins | |
| parent | 3f6b60f23c44a8ba8dd97ca6f41a16e2af7ef2f7 (diff) | |
| download | qmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.tar.gz qmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.tar.bz2 qmmp-06d1877811fa6aa97dddc0e03bcde4e766928c87.zip | |
new directory structure
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@232 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src/plugins')
203 files changed, 22381 insertions, 0 deletions
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt new file mode 100644 index 000000000..42382b5cd --- /dev/null +++ b/src/plugins/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(Input) +add_subdirectory(Output) +add_subdirectory(Visual) +add_subdirectory(Effect)
\ No newline at end of file diff --git a/src/plugins/Effect/CMakeLists.txt b/src/plugins/Effect/CMakeLists.txt new file mode 100644 index 000000000..a777010b9 --- /dev/null +++ b/src/plugins/Effect/CMakeLists.txt @@ -0,0 +1,8 @@ +SET(USE_SRC TRUE CACHE BOOL "enable/disable SRC plugin") + +IF(USE_SRC) +MESSAGE( STATUS "SRC ON") +add_subdirectory(srconverter) +ELSE(USE_SRC) +MESSAGE( STATUS "SRC OFF") +ENDIF(USE_SRC) diff --git a/src/plugins/Effect/Effect.pro b/src/plugins/Effect/Effect.pro new file mode 100644 index 000000000..b03c8a598 --- /dev/null +++ b/src/plugins/Effect/Effect.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += srconverter + diff --git a/src/plugins/Effect/srconverter/CMakeLists.txt b/src/plugins/Effect/srconverter/CMakeLists.txt new file mode 100644 index 000000000..ef95abf79 --- /dev/null +++ b/src/plugins/Effect/srconverter/CMakeLists.txt @@ -0,0 +1,76 @@ +project(libsrconverter) + +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}/../../../) + +PKGCONFIG(samplerate SAMPLERATE_INCLUDE_DIR SAMPLERATE_LINK_DIR SAMPLERATE_LINK_FLAGS SAMPLERATE_CFLAGS) + +IF(NOT SAMPLERATE_LINK_FLAGS) + SET(SAMPLERATE_LINK_FLAGS -lsamplerate) +ENDIF(NOT SAMPLERATE_LINK_FLAGS) + +include_directories(${SAMPLERATE_INCLUDE_DIR}) +link_directories(${SAMPLERATE_LINK_DIR}) + +ADD_DEFINITIONS(${SAMPLERATE_CFLAGS}) + +SET(libsrconverter_SRCS + srconverter.cpp + settingsdialog.cpp + effectsrconverterfactory.cpp +) + +SET(libsrconverter_MOC_HDRS + srconverter.h + settingsdialog.h + effectsrconverterfactory.h +) + +#SET(libsrconverter_RCCS translations/translations.qrc) + +#QT4_ADD_RESOURCES(libsrconverter_RCC_SRCS ${libsrconverter_RCCS}) + +QT4_WRAP_CPP(libsrconverter_MOC_SRCS ${libsrconverter_MOC_HDRS}) + +# user interface + + +SET(libsrconverter_UIS + settingsdialog.ui +) + +QT4_WRAP_UI(libsrconverter_UIS_H ${libsrconverter_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(srconverter SHARED ${libsrconverter_SRCS} ${libsrconverter_MOC_SRCS} ${libsrconverter_UIS_H} + ${libsrconverter_RCC_SRCS}) +target_link_libraries(srconverter ${QT_LIBRARIES} -lqmmp ${SAMPLERATE_LINK_FLAGS}) +install(TARGETS srconverter DESTINATION ${LIB_DIR}/qmmp/Effect) + diff --git a/src/plugins/Effect/srconverter/effectsrconverterfactory.cpp b/src/plugins/Effect/srconverter/effectsrconverterfactory.cpp new file mode 100644 index 000000000..bd4f93c1b --- /dev/null +++ b/src/plugins/Effect/srconverter/effectsrconverterfactory.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * 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 "effectsrconverterfactory.h" +#include "srconverter.h" + +const EffectProperties EffectSRConverterFactory::properties() const +{ + EffectProperties properties; + properties.name = tr("SRC Plugin"); + return properties; +}; + +Effect *EffectSRConverterFactory::create(QObject *parent) +{ + return new SRConverter(parent); +}; + +void EffectSRConverterFactory::showSettings(QWidget *parent) +{ + SettingsDialog *s = new SettingsDialog(parent); + s ->show(); +}; + +void EffectSRConverterFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Sample Rate Converter Plugin"), + tr("Qmmp Sample Rate Converter Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +}; + +QTranslator *EffectSRConverterFactory::createTranslator(QObject *parent) +{ + return 0; +}; + +Q_EXPORT_PLUGIN(EffectSRConverterFactory) diff --git a/src/plugins/Effect/srconverter/effectsrconverterfactory.h b/src/plugins/Effect/srconverter/effectsrconverterfactory.h new file mode 100644 index 000000000..4112a3af5 --- /dev/null +++ b/src/plugins/Effect/srconverter/effectsrconverterfactory.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * 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 EFFECTSRCONVERTERFACTORY_H +#define EFFECTSRCONVERTERFACTORY_H + + +#include <QObject> + +#include <effectfactory.h> +#include <effect.h> + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class EffectSRConverterFactory : public QObject, public EffectFactory +{ +Q_OBJECT +Q_INTERFACES(EffectFactory); + +public: + const EffectProperties properties() const; + Effect *create(QObject *parent); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + + +#endif diff --git a/src/plugins/Effect/srconverter/settingsdialog.cpp b/src/plugins/Effect/srconverter/settingsdialog.cpp new file mode 100644 index 000000000..d4e017bba --- /dev/null +++ b/src/plugins/Effect/srconverter/settingsdialog.cpp @@ -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. * + ***************************************************************************/ + +#include <QSettings> +#include <QDir> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, TRUE); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + ui.srSpinBox->setValue(settings.value("SRC/sample_rate",48000).toInt()); + ui.engineComboBox->setCurrentIndex(settings.value("SRC/engine", 0).toInt()); + connect (ui.okButton, SIGNAL(clicked()),SLOT(writeSettings())); +} + + +SettingsDialog::~SettingsDialog() +{ +} + +void SettingsDialog::writeSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.setValue("SRC/sample_rate",ui.srSpinBox->value()); + settings.setValue("SRC/engine", ui.engineComboBox->currentIndex()); + accept(); +} diff --git a/src/plugins/Effect/srconverter/settingsdialog.h b/src/plugins/Effect/srconverter/settingsdialog.h new file mode 100644 index 000000000..b7c466477 --- /dev/null +++ b/src/plugins/Effect/srconverter/settingsdialog.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * 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 SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include <QDialog> + +#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 writeSettings(); + +private: + Ui::SettingsDialog ui; + +}; + +#endif diff --git a/src/plugins/Effect/srconverter/settingsdialog.ui b/src/plugins/Effect/srconverter/settingsdialog.ui new file mode 100644 index 000000000..e837c9cf0 --- /dev/null +++ b/src/plugins/Effect/srconverter/settingsdialog.ui @@ -0,0 +1,124 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>357</width> + <height>107</height> + </rect> + </property> + <property name="windowTitle" > + <string>Sample Rate Converter Plugin Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Sample Rate (Hz):</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QSpinBox" name="srSpinBox" > + <property name="maximum" > + <number>96000</number> + </property> + <property name="singleStep" > + <number>100</number> + </property> + <property name="value" > + <number>48000</number> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Interpolation Engine:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="engineComboBox" > + <item> + <property name="text" > + <string>Best Sinc Interpolation</string> + </property> + </item> + <item> + <property name="text" > + <string>Medium Sinc Interpolation</string> + </property> + </item> + <item> + <property name="text" > + <string>Fastest Sinc Interpolation</string> + </property> + </item> + <item> + <property name="text" > + <string>ZOH Interpolation</string> + </property> + </item> + <item> + <property name="text" > + <string>Linear Interpolation</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <layout class="QHBoxLayout" > + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>&OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>&Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>302</x> + <y>88</y> + </hint> + <hint type="destinationlabel" > + <x>42</x> + <y>69</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Effect/srconverter/srconverter.cpp b/src/plugins/Effect/srconverter/srconverter.cpp new file mode 100644 index 000000000..0e733c8c4 --- /dev/null +++ b/src/plugins/Effect/srconverter/srconverter.cpp @@ -0,0 +1,125 @@ +/*************************************************************************** + * 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 <QSettings> +#include <QDir> +#include <math.h> + +#include "srconverter.h" + +SRConverter::SRConverter(QObject* parent) : Effect(parent) +{ + m_isSrcAlloc = FALSE; + int converter_type_array[] = {SRC_SINC_BEST_QUALITY, SRC_SINC_MEDIUM_QUALITY, SRC_SINC_FASTEST, + SRC_ZERO_ORDER_HOLD, SRC_LINEAR}; + m_srcIn = 0; + m_srcOut = 0; + m_src_state = 0; + m_srcError = 0; + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + m_overSamplingFs = settings.value("SRC/sample_rate",48000).toInt(); + m_converter_type = converter_type_array[settings.value("SRC/engine", 0).toInt()]; +} + +SRConverter::~SRConverter() +{ + src_reset (m_src_state) ; + freeSRC(); + m_src_data.data_in = 0; + m_src_data.data_out = 0; + m_src_data.end_of_input = 0; + m_src_data.input_frames = 0; + m_src_data.output_frames = 0; + if (m_isSrcAlloc) + { + free(m_srcIn); + free(m_srcOut); + free(m_wOut); + m_isSrcAlloc = FALSE; + } +} + +const ulong SRConverter::process(char *in_data, const ulong size, char **out_data) +{ + if (m_isSrcAlloc) + { + free(m_srcIn); + free(m_srcOut); + free(m_wOut); + m_isSrcAlloc = FALSE; + } + ulong wbytes = 0; + + if (m_src_state && size > 0) + { + int lrLength = size/2; + int overLrLength= (int)floor(lrLength*(m_src_data.src_ratio+1)); + m_srcIn = (float*) malloc(sizeof(float)*lrLength); + m_srcOut = (float*) malloc(sizeof(float)*overLrLength); + m_wOut = (short int*) malloc(sizeof(short int)*overLrLength); + src_short_to_float_array((short int*)in_data, m_srcIn, lrLength); + m_isSrcAlloc = TRUE; + m_src_data.data_in = m_srcIn; + m_src_data.data_out = m_srcOut; + m_src_data.end_of_input = 0; + m_src_data.input_frames = lrLength/2; + m_src_data.output_frames = overLrLength/2; + if ((m_srcError = src_process(m_src_state, &m_src_data)) > 0) + { + qWarning("SRConverter: src_process(): %s\n", src_strerror(m_srcError)); + } + else + { + src_float_to_short_array(m_srcOut, m_wOut, m_src_data.output_frames_gen*2); + wbytes = m_src_data.output_frames_gen*4; + *out_data = new char[wbytes]; + memcpy(*out_data, (char*) m_wOut, wbytes); + } + } + return wbytes; +} + +void SRConverter::configure(ulong freq, int chan, int res) +{ + Effect::configure(freq, chan, res); + freeSRC(); + uint rate = freq; + { + m_src_state = src_new(m_converter_type, 2, &m_srcError); + if (m_src_state) + { + m_src_data.src_ratio = (float)m_overSamplingFs/(float)rate; + rate = m_overSamplingFs; + } + else + qDebug("SRConverter: src_new(): %s", src_strerror(m_srcError)); + } +} + +const ulong SRConverter::frequency() +{ + return m_overSamplingFs; +} + +void SRConverter::freeSRC() +{ + if (m_src_state != NULL) + m_src_state = src_delete(m_src_state); +} diff --git a/src/plugins/Effect/srconverter/srconverter.h b/src/plugins/Effect/srconverter/srconverter.h new file mode 100644 index 000000000..8c6dcb6d3 --- /dev/null +++ b/src/plugins/Effect/srconverter/srconverter.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * 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 SRCONVERTER_H +#define SRCONVERTER_H + +#include <effect.h> + +extern "C" +{ +#include <samplerate.h> +} + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class SRConverter : public Effect +{ + Q_OBJECT +public: + SRConverter(QObject *parent = 0); + + virtual ~SRConverter(); + + const ulong process(char *in_data, const ulong size, char **out_data); + void configure(ulong freq, int chan, int res); + const ulong frequency(); + +private: + void freeSRC(); + SRC_STATE *m_src_state; + SRC_DATA m_src_data; + int m_overSamplingFs; + int m_srcError; + int m_converter_type; + bool m_isSrcAlloc; + float *m_srcIn, *m_srcOut; + short *m_wOut; + ulong m_freq; +}; + +#endif diff --git a/src/plugins/Effect/srconverter/srconverter.pro b/src/plugins/Effect/srconverter/srconverter.pro new file mode 100644 index 000000000..af20c1e92 --- /dev/null +++ b/src/plugins/Effect/srconverter/srconverter.pro @@ -0,0 +1,34 @@ +include(../../plugins.pri) + +HEADERS += srconverter.h \ + effectsrconverterfactory.h \ + settingsdialog.h + +SOURCES += srconverter.cpp \ + effectsrconverterfactory.cpp \ + settingsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Effect/srconverter +QMAKE_CLEAN =$$PLUGINS_PREFIX/Effect/libsrconverter.so +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig + +PKGCONFIG += samplerate +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -I/usr/include + +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty(LIB_DIR){ + LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Effect +INSTALLS += target + +FORMS += settingsdialog.ui + diff --git a/src/plugins/General/General.pro b/src/plugins/General/General.pro new file mode 100644 index 000000000..11f567452 --- /dev/null +++ b/src/plugins/General/General.pro @@ -0,0 +1,4 @@ +SUBDIRS += statusicon \ + scrobbler \ + dbuscontrol +TEMPLATE = subdirs diff --git a/src/plugins/General/dbuscontrol/dbusadaptor.cpp b/src/plugins/General/dbuscontrol/dbusadaptor.cpp new file mode 100644 index 000000000..77ace9208 --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbusadaptor.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * Copyright (C) 2008 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 "dbusadaptor.h" + +DBUSAdaptor::DBUSAdaptor(QObject *parent) + : QDBusAbstractAdaptor(parent) +{ + setAutoRelaySignals(TRUE); +} + +DBUSAdaptor::~DBUSAdaptor() +{ +} + +void DBUSAdaptor::play() +{ + QMetaObject::invokeMethod(parent(), "play"); +} + +void DBUSAdaptor::stop() +{ + QMetaObject::invokeMethod(parent(), "stop"); +} + +void DBUSAdaptor::next() +{ + QMetaObject::invokeMethod(parent(), "next"); +} + +void DBUSAdaptor::previous() +{ + QMetaObject::invokeMethod(parent(), "previous"); +} + +void DBUSAdaptor::pause() +{ + QMetaObject::invokeMethod(parent(), "pause"); +} + +void DBUSAdaptor::exit() +{ + QMetaObject::invokeMethod(parent(), "exit"); +} + + diff --git a/src/plugins/General/dbuscontrol/dbusadaptor.h b/src/plugins/General/dbuscontrol/dbusadaptor.h new file mode 100644 index 000000000..43294e753 --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbusadaptor.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2008 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 DBUSADAPTOR_H +#define DBUSADAPTOR_H + +#include <QtDBus> + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class DBUSAdaptor : public QDBusAbstractAdaptor +{ +Q_OBJECT +Q_CLASSINFO("D-Bus Interface", "org.qmmp.dbus") +public: + DBUSAdaptor(QObject *parent = 0); + + ~DBUSAdaptor(); + +public slots: + void play(); + void stop(); + void next(); + void previous(); + void pause(); + void exit(); + +}; + +#endif diff --git a/src/plugins/General/dbuscontrol/dbuscontrol.cpp b/src/plugins/General/dbuscontrol/dbuscontrol.cpp new file mode 100644 index 000000000..1133cec7a --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbuscontrol.cpp @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright (C) 2008 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 "dbusadaptor.h" +#include "dbuscontrol.h" + +DBUSControl::DBUSControl(QObject *parent) + : General(parent) +{ + new DBUSAdaptor(this); + QDBusConnection connection = QDBusConnection::sessionBus(); + connection.registerObject("/Qmmp", this); + connection.registerService("org.qmmp.dbus"); +} + + +DBUSControl::~DBUSControl() +{} + +void DBUSControl::setState(const uint &state) +{ + switch ((uint) state) + { + case General::Playing: + { + //m_tray->setIcon(QIcon(":/tray_play.png")); + break; + } + case General::Paused: + { + //m_tray->setIcon(QIcon(":/tray_pause.png")); + break; + } + case General::Stopped: + { + //m_tray->setIcon(QIcon(":/tray_stop.png")); + break; + } + } +} + +void DBUSControl::setSongInfo(const SongInfo &song) +{ +} diff --git a/src/plugins/General/dbuscontrol/dbuscontrol.h b/src/plugins/General/dbuscontrol/dbuscontrol.h new file mode 100644 index 000000000..2d5f3c91a --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbuscontrol.h @@ -0,0 +1,43 @@ +/*************************************************************************** + * Copyright (C) 2008 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 STATUSICON_H +#define STATUSICON_H + + +#include <qmmpui/general.h> + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class DBUSControl : public General +{ +Q_OBJECT +public: + DBUSControl(QObject *parent = 0); + + ~DBUSControl(); + + void setState(const uint& state); + void setSongInfo(const SongInfo &song); + +}; + +#endif diff --git a/src/plugins/General/dbuscontrol/dbuscontrol.pro b/src/plugins/General/dbuscontrol/dbuscontrol.pro new file mode 100644 index 000000000..4ad8e047b --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbuscontrol.pro @@ -0,0 +1,39 @@ +include(../../plugins.pri) + +CONFIG += release \ +warn_on \ +plugin \ + lib \ + qdbus + +TARGET=$$PLUGINS_PREFIX/General/dbuscontrol +QMAKE_CLEAN =$$PLUGINS_PREFIX/General/libdbuscontrol.so + +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib + +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty(LIB_DIR){ + LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/General +INSTALLS += target +#FORMS += settingsdialog.ui + +#RESOURCES += images/images.qrc + + +HEADERS += dbuscontrolfactory.h \ + dbuscontrol.h \ + dbusadaptor.h + +SOURCES += dbuscontrolfactory.cpp \ + dbuscontrol.cpp \ + dbusadaptor.cpp + +INCLUDEPATH += ../../../../src + +LIBS += -lqmmpui + diff --git a/src/plugins/General/dbuscontrol/dbuscontrolfactory.cpp b/src/plugins/General/dbuscontrol/dbuscontrolfactory.cpp new file mode 100644 index 000000000..922962c67 --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbuscontrolfactory.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2008 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 "dbuscontrol.h" +#include "dbuscontrolfactory.h" + +const GeneralProperties DBUSControlFactory::properties() const +{ + GeneralProperties properties; + properties.name = tr("D-Bus Plugin"); + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +General *DBUSControlFactory::create(QObject *parent) +{ + return new DBUSControl(parent); +} + +void DBUSControlFactory::showSettings(QWidget *) +{} + +void DBUSControlFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About D-Bus Plugin"), + tr("Qmmp D-Bus Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *DBUSControlFactory::createTranslator(QObject *parent) +{ + return 0; +} + +Q_EXPORT_PLUGIN(DBUSControlFactory) diff --git a/src/plugins/General/dbuscontrol/dbuscontrolfactory.h b/src/plugins/General/dbuscontrol/dbuscontrolfactory.h new file mode 100644 index 000000000..98121edec --- /dev/null +++ b/src/plugins/General/dbuscontrol/dbuscontrolfactory.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2008 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 STATUSICONFACTORY_H +#define STATUSICONFACTORY_H + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +#include <QObject> +#include <QTranslator> + +#include <qmmpui/general.h> +#include <qmmpui/generalfactory.h> + +class DBUSControlFactory : public QObject, public GeneralFactory +{ +Q_OBJECT +Q_INTERFACES(GeneralFactory); +public: + const GeneralProperties properties() const; + General *create(QObject *parent); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); + +}; + +#endif diff --git a/src/plugins/General/scrobbler/scrobbler.cpp b/src/plugins/General/scrobbler/scrobbler.cpp new file mode 100644 index 000000000..7713161b7 --- /dev/null +++ b/src/plugins/General/scrobbler/scrobbler.cpp @@ -0,0 +1,259 @@ +/*************************************************************************** + * Copyright (C) 2008 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 <QMenu> +#include <QHttp> +#include <QByteArray> +#include <QCryptographicHash> +#include <QUrl> +#include <QTime> +#include <QSettings> +#include <QDir> + +#include "scrobbler.h" + +#define SCROBBLER_HS_URL "post.audioscrobbler.com" +#define PROTOCOL_VER "1.2" +#define CLIENT_ID "qmm" +#define CLIENT_VER "0.1" + +Scrobbler::Scrobbler(QObject *parent) + : General(parent) +{ + m_http = new QHttp(this); + m_http->setHost(SCROBBLER_HS_URL, 80); + m_state = General::Stopped; + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Scrobbler"); + m_login = settings.value("login").toString(); + m_passw = settings.value("password").toString(); + settings.endGroup(); + m_disabled = m_login.isEmpty() || m_passw.isEmpty(); + m_passw = QString(QCryptographicHash::hash(m_passw.toAscii(), QCryptographicHash::Md5).toHex()); + connect(m_http, SIGNAL(requestFinished (int, bool)), SLOT(processResponse(int, bool))); + connect(m_http, SIGNAL(readyRead (const QHttpResponseHeader&)), + SLOT(readResponse(const QHttpResponseHeader&))); + m_time = new QTime(); + m_submitedSongs = 0; + m_handshakeid = 0; + m_submitid = 0; + if (!m_disabled) + handshake(); + //TODO proxy support +} + + +Scrobbler::~Scrobbler() +{ + delete m_time; +} + +void Scrobbler::setState(const uint &state) +{ + m_state = state; + if (m_disabled) + return; + switch ((uint) state) + { + case General::Playing: + { + m_start_ts = time(NULL); + m_time->restart(); + if (!isReady() && m_handshakeid == 0) + handshake(); + break; + } + case General::Paused: + { + break; + } + case General::Stopped: + { + if (!m_song.isEmpty() + && ((m_time->elapsed ()/1000 > 240) + || (m_time->elapsed ()/1000 > int(m_song.length()/2))) + && (m_time->elapsed ()/1000 > 60)) + { + m_songCache << m_song; + m_timeStamps << m_start_ts; + } + + m_song.clear(); + if (m_songCache.isEmpty()) + break; + + if (m_http->error() != QHttp::NoError) + m_http->clearPendingRequests (); + + if (isReady() && m_submitid == 0) + { + submit(); + } + break; + } + } +} + +void Scrobbler::setSongInfo(const SongInfo &song) +{ + if (m_state == General::Playing + && !song.title().isEmpty() //skip empty tags + && !song.artist().isEmpty() + && !song.isStream() //skip stream + && !song.artist().contains("&") //skip tags with special symbols + && !song.title().contains("&") + && !song.album().contains("&") + && !song.artist().contains("=") + && !song.title().contains("=") + && !song.album().contains("=")) + { + m_song = song; + } +} + +void Scrobbler::processResponse(int id, bool error) +{ + if (error) + { + qWarning("Scrobbler: %s", qPrintable(m_http->errorString ())); + //TODO hard failure handling + + if (id == m_submitid) + m_submitid = 0; + else if (id == m_handshakeid) + m_handshakeid = 0; + return; + } + QString str(m_array); + QStringList strlist = str.split("\n"); + + if (id == m_handshakeid) + { + m_handshakeid = 0; + if (!strlist[0].contains("OK") || strlist.size() < 4) + { + qWarning("Scrobbler: handshake phase error: %s", qPrintable(strlist[0])); + //TODO badtime handling + return; + } + if (strlist.size() > 3) //process handshake response + { + qDebug("Scrobbler: reading handshake response"); + qDebug("Scrobbler: Session ID: %s",qPrintable(strlist[1])); + qDebug("Scrobbler: Now-Playing URL: %s",qPrintable(strlist[2])); + qDebug("Scrobbler: Submission URL: %s",qPrintable(strlist[3])); + m_submitUrl = strlist[3]; + m_session = strlist[1]; + return; + } + } + else if (id == m_submitid) + { + m_submitid = 0; + if (!strlist[0].contains("OK")) + { + qWarning("Scrobbler: submit error: %s", qPrintable(strlist[0])); + //TODO badsession handling + return; + } + qWarning("Scrobbler: submited %d song(s)", m_submitedSongs); + while (m_submitedSongs) + { + m_submitedSongs--; + m_timeStamps.removeFirst (); + m_songCache.removeFirst (); + } + } + m_array.clear(); +} + +void Scrobbler::readResponse(const QHttpResponseHeader &header) +{ + if (header.statusCode () != 200) + { + qWarning("Scrobbler: error: %s",qPrintable(header.reasonPhrase ())); + //TODO Failure Handling + return; + } + m_array = m_http->readAll(); +} + +void Scrobbler::handshake() +{ + qDebug("Scrobbler::handshake()"); + time_t ts = time(NULL); + qDebug("Scrobbler: current time stamp %ld",ts); + QString auth_tmp = QString("%1%2").arg(m_passw).arg(ts); + QByteArray auth = QCryptographicHash::hash(auth_tmp.toAscii (), QCryptographicHash::Md5); + auth = auth.toHex(); + + QString url = QString("%1?hs=true&p=%2&c=%3&v=%4&u=%5&t=%6&a=%7") + .arg("/") + .arg(PROTOCOL_VER) + .arg(CLIENT_ID) + .arg(CLIENT_VER) + .arg(m_login) + .arg(ts) + .arg(QString(auth)); + + qDebug("Scrobbler: request url: %s",qPrintable(url)); + m_http->setHost(SCROBBLER_HS_URL, 80); + m_handshakeid = m_http->get(url); +} + +void Scrobbler::submit() +{ + qDebug("Scrobbler::submit()"); + if (m_songCache.isEmpty()) + return; + m_submitedSongs = m_songCache.size(); + QString body = QString("s=%1").arg(m_session); + for (int i = 0; i < qMin(m_songCache.size(), 25); ++i) + { + SongInfo info = m_songCache[i]; + body += QString("&a[%9]=%1&t[%9]=%2&i[%9]=%3&o[%9]=%4&r[%9]=%5&l[%9]=%6&b[%9]=%7&n[%9]=%8&m[%9]=") + .arg(info.artist()) + .arg(info.title()) + .arg( m_timeStamps[i]) + .arg("P") + .arg("") + .arg(info.length()) + .arg(info.album()) + .arg(info.track()) + .arg(i); + } + QUrl url(m_submitUrl); + m_http->setHost(url.host(), url.port()); + QHttpRequestHeader header("POST", url.path()); + header.setContentType("application/x-www-form-urlencoded"); + header.setValue("User-Agent","iScrobbler/1.5.1qmmp-plugins/0.2"); + header.setValue("Host",url.host()); + header.setValue("Accept", "*/*"); + header.setContentLength(QUrl::toPercentEncoding(body,":/[]&=").size()); + qDebug("Scrobbler: submit request header"); + qDebug(qPrintable(header.toString())); + qDebug("*****************************"); + m_submitid = m_http->request(header, QUrl::toPercentEncoding(body,":/[]&=")); +} + +bool Scrobbler::isReady() +{ + return !m_submitUrl.isEmpty() && !m_session.isEmpty(); +} diff --git a/src/plugins/General/scrobbler/scrobbler.h b/src/plugins/General/scrobbler/scrobbler.h new file mode 100644 index 000000000..25faa13fd --- /dev/null +++ b/src/plugins/General/scrobbler/scrobbler.h @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008 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 SCROBBLER_H +#define SCROBBLER_H + +#include <QHttpResponseHeader> +#include <qmmpui/general.h> +#include <time.h> + +class QHttp; +class QTime; + + + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class Scrobbler : public General +{ +Q_OBJECT +public: + Scrobbler(QObject *parent = 0); + + ~Scrobbler(); + + void setState(const uint& state); + void setSongInfo(const SongInfo &song); + +private slots: + void processResponse(int, bool); + void processResponseHeader(const QHttpResponseHeader &); + void readResponse(const QHttpResponseHeader&); + +private: + void handshake(); + void submit(); + bool isReady(); + time_t m_start_ts; + SongInfo m_song; + QHttp *m_http; + uint m_state; + QString m_login; + QString m_passw; + QString m_submitUrl; + QString m_session; + QList <time_t> m_timeStamps; + QList <SongInfo> m_songCache; + QTime* m_time; + int m_submitedSongs; + int m_handshakeid; + int m_submitid; + QByteArray m_array; + bool m_disabled; + +}; + +#endif diff --git a/src/plugins/General/scrobbler/scrobbler.pro b/src/plugins/General/scrobbler/scrobbler.pro new file mode 100644 index 000000000..787cb9f37 --- /dev/null +++ b/src/plugins/General/scrobbler/scrobbler.pro @@ -0,0 +1,41 @@ +include(../../plugins.pri) + +CONFIG += release \ + warn_on \ + plugin + +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib + +TARGET=$$PLUGINS_PREFIX/General/scrobbler +QMAKE_CLEAN =$$PLUGINS_PREFIX/General/libscrobbler.so + + +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty(LIB_DIR){ + LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/General +INSTALLS += target +#FORMS += settingsdialog.ui + +#RESOURCES += images/images.qrc + + +HEADERS += scrobblerfactory.h \ + scrobbler.h \ + settingsdialog.h +SOURCES += scrobblerfactory.cpp \ + scrobbler.cpp \ + settingsdialog.cpp +QT += network + + +INCLUDEPATH += ../../../ + +LIBS += -lqmmpui + +FORMS += settingsdialog.ui + diff --git a/src/plugins/General/scrobbler/scrobblerfactory.cpp b/src/plugins/General/scrobbler/scrobblerfactory.cpp new file mode 100644 index 000000000..77bbb0f2b --- /dev/null +++ b/src/plugins/General/scrobbler/scrobblerfactory.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2008 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 "scrobbler.h" +#include "settingsdialog.h" +#include "scrobblerfactory.h" + +const GeneralProperties ScrobblerFactory::properties() const +{ + GeneralProperties properties; + properties.name = tr("Scrobbler Plugin"); + properties.hasAbout = TRUE; + properties.hasSettings = TRUE; + return properties; +} + +General *ScrobblerFactory::create(QObject *parent) +{ + return new Scrobbler(parent); +} + +void ScrobblerFactory::showSettings(QWidget *parent) +{ + SettingsDialog *dialog = new SettingsDialog(parent); + dialog->show(); +} + +void ScrobblerFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Scrobbler Plugin"), + tr("Qmmp AudioScrobbler Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *ScrobblerFactory::createTranslator(QObject *parent) +{ + return 0; +} + +Q_EXPORT_PLUGIN(ScrobblerFactory) diff --git a/src/plugins/General/scrobbler/scrobblerfactory.h b/src/plugins/General/scrobbler/scrobblerfactory.h new file mode 100644 index 000000000..240749714 --- /dev/null +++ b/src/plugins/General/scrobbler/scrobblerfactory.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2008 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 SCROBBLERFACTORY_H +#define SCROBBLERFACTORY_H + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +#include <QObject> +#include <QTranslator> + +#include <qmmpui/general.h> +#include <qmmpui/generalfactory.h> + +class ScrobblerFactory : public QObject, public GeneralFactory +{ +Q_OBJECT +Q_INTERFACES(GeneralFactory); +public: + const GeneralProperties properties() const; + General *create(QObject *parent); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); + +}; + +#endif diff --git a/src/plugins/General/scrobbler/settingsdialog.cpp b/src/plugins/General/scrobbler/settingsdialog.cpp new file mode 100644 index 000000000..8dc2dfe22 --- /dev/null +++ b/src/plugins/General/scrobbler/settingsdialog.cpp @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright (C) 2008 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> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Scrobbler"); + ui.userLineEdit->setText(settings.value("login").toString()); + ui.passwordLineEdit->setText(settings.value("password").toString()); + settings.endGroup(); + connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings())); +} + + +SettingsDialog::~SettingsDialog() +{} + +void SettingsDialog::writeSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Scrobbler"); + settings.setValue("login",ui.userLineEdit->text()); + settings.setValue("password",ui.passwordLineEdit->text()); + settings.endGroup(); + accept(); +} diff --git a/src/plugins/General/scrobbler/settingsdialog.h b/src/plugins/General/scrobbler/settingsdialog.h new file mode 100644 index 000000000..0d5b21d52 --- /dev/null +++ b/src/plugins/General/scrobbler/settingsdialog.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2008 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> + + +#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 writeSettings(); + +private: + Ui::SettingsDialog ui; + +}; + +#endif diff --git a/src/plugins/General/scrobbler/settingsdialog.ui b/src/plugins/General/scrobbler/settingsdialog.ui new file mode 100644 index 000000000..ec446bba2 --- /dev/null +++ b/src/plugins/General/scrobbler/settingsdialog.ui @@ -0,0 +1,92 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>250</width> + <height>123</height> + </rect> + </property> + <property name="windowTitle" > + <string>Scrobbler Plugin Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>User name:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="userLineEdit" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Password:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="passwordLineEdit" > + <property name="echoMode" > + <enum>QLineEdit::Password</enum> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <layout class="QHBoxLayout" > + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>61</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="canselButton" > + <property name="text" > + <string>Cansel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>canselButton</sender> + <signal>clicked()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>204</x> + <y>90</y> + </hint> + <hint type="destinationlabel" > + <x>26</x> + <y>102</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/General/statusicon/images/images.qrc b/src/plugins/General/statusicon/images/images.qrc new file mode 100644 index 000000000..b072b1d73 --- /dev/null +++ b/src/plugins/General/statusicon/images/images.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>tray_play.png</file> + <file>tray_pause.png</file> + <file>tray_stop.png</file> + </qresource> +</RCC> diff --git a/src/plugins/General/statusicon/images/tray_pause.png b/src/plugins/General/statusicon/images/tray_pause.png Binary files differnew file mode 100644 index 000000000..dfed3deb1 --- /dev/null +++ b/src/plugins/General/statusicon/images/tray_pause.png diff --git a/src/plugins/General/statusicon/images/tray_play.png b/src/plugins/General/statusicon/images/tray_play.png Binary files differnew file mode 100644 index 000000000..4d0f9099c --- /dev/null +++ b/src/plugins/General/statusicon/images/tray_play.png diff --git a/src/plugins/General/statusicon/images/tray_stop.png b/src/plugins/General/statusicon/images/tray_stop.png Binary files differnew file mode 100644 index 000000000..9e894d9eb --- /dev/null +++ b/src/plugins/General/statusicon/images/tray_stop.png diff --git a/src/plugins/General/statusicon/settingsdialog.cpp b/src/plugins/General/statusicon/settingsdialog.cpp new file mode 100644 index 000000000..7cb6bb1e5 --- /dev/null +++ b/src/plugins/General/statusicon/settingsdialog.cpp @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright (C) 2008 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> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Tray"); + ui.messageCheckBox->setChecked(settings.value("show_message",TRUE).toBool()); + ui.messageDelaySpinBox->setValue(settings.value("message_delay", 2000).toInt()); + ui.toolTipCheckBox->setChecked(settings.value("show_tooltip",FALSE).toBool()); + ui.hideToTrayRadioButton->setChecked(settings.value("hide_on_close", FALSE).toBool()); + settings.endGroup(); + connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings())); +} + + +SettingsDialog::~SettingsDialog() +{} + +void SettingsDialog::writeSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Tray"); + settings.setValue ("show_message", ui.messageCheckBox->isChecked()); + settings.setValue ("message_delay", ui.messageDelaySpinBox->value()); + settings.setValue ("show_tooltip", ui.toolTipCheckBox->isChecked()); + settings.setValue ("hide_on_close", ui.hideToTrayRadioButton->isChecked()); + settings.endGroup(); + accept(); +} diff --git a/src/plugins/General/statusicon/settingsdialog.h b/src/plugins/General/statusicon/settingsdialog.h new file mode 100644 index 000000000..0d5b21d52 --- /dev/null +++ b/src/plugins/General/statusicon/settingsdialog.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright (C) 2008 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> + + +#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 writeSettings(); + +private: + Ui::SettingsDialog ui; + +}; + +#endif diff --git a/src/plugins/General/statusicon/settingsdialog.ui b/src/plugins/General/statusicon/settingsdialog.ui new file mode 100644 index 000000000..126fcce7a --- /dev/null +++ b/src/plugins/General/statusicon/settingsdialog.ui @@ -0,0 +1,173 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>267</width> + <height>285</height> + </rect> + </property> + <property name="windowTitle" > + <string>Status Icon Plugin Settings</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="groupBox_4" > + <property name="title" > + <string>Tray Icon</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QCheckBox" name="messageCheckBox" > + <property name="text" > + <string>Show message</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QCheckBox" name="toolTipCheckBox" > + <property name="text" > + <string>Show tooltip</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Message delay, ms:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QSpinBox" name="messageDelaySpinBox" > + <property name="minimum" > + <number>100</number> + </property> + <property name="maximum" > + <number>10000</number> + </property> + <property name="singleStep" > + <number>100</number> + </property> + <property name="value" > + <number>1000</number> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="closeGroupBox" > + <property name="title" > + <string>Action On Close</string> + </property> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <item> + <widget class="QRadioButton" name="hideToTrayRadioButton" > + <property name="text" > + <string>Hide to tray</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="quitRadioButton" > + <property name="text" > + <string>Quit</string> + </property> + <property name="checked" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>131</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>336</x> + <y>210</y> + </hint> + <hint type="destinationlabel" > + <x>179</x> + <y>224</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/General/statusicon/statusicon.cpp b/src/plugins/General/statusicon/statusicon.cpp new file mode 100644 index 000000000..59225d028 --- /dev/null +++ b/src/plugins/General/statusicon/statusicon.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + * Copyright (C) 2008 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 <QMenu> +#include <QSettings> +#include <QDir> +#include <QTimer> +#include <QCoreApplication> + +#include "statusicon.h" + +StatusIcon::StatusIcon(QObject *parent) + : General(parent) +{ + m_tray = new QSystemTrayIcon(this); + connect(m_tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason))); + m_tray->setIcon ( QIcon(":/tray_stop.png")); + m_tray->show(); + QMenu *menu = new QMenu(qobject_cast<QWidget *>(parent)); + menu->addAction(tr("Play"), this, SLOT(play())); + menu->addAction(tr("Pause"), this, SLOT(pause())); + menu->addAction(tr("Stop"), this, SLOT(stop())); + menu->addAction(tr("Next"), this, SLOT(next())); + menu->addAction(tr("Previous"), this, SLOT(previous())); + menu->addSeparator(); + menu->addAction(tr("Exit"), this, SLOT(exit())); + m_tray->setContextMenu(menu); + + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("Tray"); + m_showMessage = settings.value("show_message",TRUE).toBool(); + m_messageDelay = settings.value("message_delay", 2000).toInt(); + m_showTooltip = settings.value("show_tooltip",FALSE).toBool(); + m_hideToTray = settings.value("hide_on_close", FALSE).toBool(); + settings.endGroup(); + m_enabled = FALSE; + QTimer::singleShot(200, this, SLOT(enable())); + +} + + +StatusIcon::~StatusIcon() +{} + +void StatusIcon::setState(const uint &state) +{ + switch ((uint) state) + { + case General::Playing: + { + m_tray->setIcon(QIcon(":/tray_play.png")); + break; + } + case General::Paused: + { + m_tray->setIcon(QIcon(":/tray_pause.png")); + break; + } + case General::Stopped: + { + m_tray->setIcon(QIcon(":/tray_stop.png")); + break; + } + } +} + +void StatusIcon::setSongInfo(const SongInfo &song) +{ + if(!m_enabled) + return; + QString message = song.artist() + " - " +song.title(); + if (song.artist().isEmpty()) + message = song.title(); + if (song.title().isEmpty()) + message = song.artist(); + if (m_showMessage) + m_tray->showMessage (tr("Now Playing"), message, + QSystemTrayIcon::Information, m_messageDelay); + if (m_showTooltip) + m_tray->setToolTip(message); +} + +void StatusIcon::trayActivated(QSystemTrayIcon::ActivationReason reason) +{ + if (reason == QSystemTrayIcon::Trigger) + toggleVisibility(); +} + +void StatusIcon::enable() +{ + m_enabled = TRUE; +} diff --git a/src/plugins/General/statusicon/statusicon.h b/src/plugins/General/statusicon/statusicon.h new file mode 100644 index 000000000..fae20422d --- /dev/null +++ b/src/plugins/General/statusicon/statusicon.h @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright (C) 2008 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 STATUSICON_H +#define STATUSICON_H + +#include <QSystemTrayIcon> + +#include <qmmpui/general.h> + + + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class StatusIcon : public General +{ +Q_OBJECT +public: + StatusIcon(QObject *parent = 0); + + ~StatusIcon(); + + void setState(const uint& state); + void setSongInfo(const SongInfo &song); + +private slots: + void trayActivated(QSystemTrayIcon::ActivationReason); + void enable(); + +private: + QSystemTrayIcon *m_tray; + bool m_showMessage; + bool m_showTooltip; + bool m_hideToTray; + bool m_enabled; + int m_messageDelay; + +}; + +#endif diff --git a/src/plugins/General/statusicon/statusicon.pro b/src/plugins/General/statusicon/statusicon.pro new file mode 100644 index 000000000..01408d1c8 --- /dev/null +++ b/src/plugins/General/statusicon/statusicon.pro @@ -0,0 +1,36 @@ +include(../../plugins.pri) + +INCLUDEPATH += ../../../../src +CONFIG += release \ +warn_on \ +plugin + +TARGET=$$PLUGINS_PREFIX/General/statusicon +QMAKE_CLEAN =$$PLUGINS_PREFIX/General/libstatusicon.so + + +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmpui + +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty(LIB_DIR){ + LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/General +INSTALLS += target +#FORMS += settingsdialog.ui + +RESOURCES += images/images.qrc + + +HEADERS += statusiconfactory.h \ +statusicon.h \ + settingsdialog.h +SOURCES += statusiconfactory.cpp \ +statusicon.cpp \ + settingsdialog.cpp +FORMS += settingsdialog.ui + diff --git a/src/plugins/General/statusicon/statusiconfactory.cpp b/src/plugins/General/statusicon/statusiconfactory.cpp new file mode 100644 index 000000000..1d3beaa0b --- /dev/null +++ b/src/plugins/General/statusicon/statusiconfactory.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2008 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 "statusicon.h" +#include "settingsdialog.h" +#include "statusiconfactory.h" + +const GeneralProperties StatusIconFactory::properties() const +{ + GeneralProperties properties; + properties.name = tr("Status Icon Plugin"); + properties.hasAbout = TRUE; + properties.hasSettings = TRUE; + return properties; +} + +General *StatusIconFactory::create(QObject *parent) +{ + return new StatusIcon(parent); +} + +void StatusIconFactory::showSettings(QWidget *parent) +{ + SettingsDialog *s = new SettingsDialog(parent); + s->show(); +} + +void StatusIconFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Scrobbler Plugin"), + tr("Qmmp Status Icon Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *StatusIconFactory::createTranslator(QObject *parent) +{ + return 0; +} + +Q_EXPORT_PLUGIN(StatusIconFactory) diff --git a/src/plugins/General/statusicon/statusiconfactory.h b/src/plugins/General/statusicon/statusiconfactory.h new file mode 100644 index 000000000..b09fb1ef1 --- /dev/null +++ b/src/plugins/General/statusicon/statusiconfactory.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2008 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 STATUSICONFACTORY_H +#define STATUSICONFACTORY_H + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +#include <QObject> +#include <QTranslator> + +#include <qmmpui/general.h> +#include <qmmpui/generalfactory.h> + +class StatusIconFactory : public QObject, public GeneralFactory +{ +Q_OBJECT +Q_INTERFACES(GeneralFactory); +public: + const GeneralProperties properties() const; + General *create(QObject *parent); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); + +}; + +#endif diff --git a/src/plugins/Input/CMakeLists.txt b/src/plugins/Input/CMakeLists.txt new file mode 100644 index 000000000..8114bc092 --- /dev/null +++ b/src/plugins/Input/CMakeLists.txt @@ -0,0 +1,49 @@ + +SET(USE_MAD TRUE CACHE BOOL "enable/disable mad plugin") +SET(USE_FLAC TRUE CACHE BOOL "enable/disable flac plugin") +SET(USE_VORBIS TRUE CACHE BOOL "enable/disable ogg vorbis plugin") +SET(USE_FFMPEG TRUE CACHE BOOL "enable/disable ffmpeg plugin") +SET(USE_MPC TRUE CACHE BOOL "enable/disable mpc plugin") +SET(USE_SNDFILE TRUE CACHE BOOL "enable/disable sndfile plugin") + +IF(USE_MAD) +MESSAGE( STATUS "MAD ON") +add_subdirectory(mad) +ELSE(USE_MAD) +MESSAGE( STATUS "MAD OFF") +ENDIF(USE_MAD) + +IF(USE_FLAC) +MESSAGE( STATUS "FLAC ON") +add_subdirectory(flac) +ELSE(USE_FLAC) +MESSAGE( STATUS "FLAC OFF") +ENDIF(USE_FLAC) + +IF(USE_VORBIS) +MESSAGE( STATUS "VORBIS ON") +add_subdirectory(vorbis) +ELSE(USE_VORBIS) +MESSAGE( STATUS "VORBIS OFF") +ENDIF(USE_VORBIS) + +IF(USE_FFMPEG) +MESSAGE( STATUS "FFMPEG ON") +add_subdirectory(ffmpeg) +ELSE(USE_FFMPEG) +MESSAGE( STATUS "FFMPEG OFF") +ENDIF(USE_FFMPEG) + +IF(USE_MPC) +MESSAGE( STATUS "MPC ON") +add_subdirectory(mpc) +ELSE(USE_MPC) +MESSAGE( STATUS "MPC OFF") +ENDIF(USE_MPC) + +IF(USE_SNDFILE) +MESSAGE( STATUS "SNDFILE ON") +add_subdirectory(sndfile) +ELSE(USE_SNDFILE) +MESSAGE( STATUS "SNDFILE OFF") +ENDIF(USE_SNDFILE) diff --git a/src/plugins/Input/Input.pro b/src/plugins/Input/Input.pro new file mode 100644 index 000000000..e180229cc --- /dev/null +++ b/src/plugins/Input/Input.pro @@ -0,0 +1,26 @@ +include(../../../qmmp.pri) + +SUBDIRS += mad vorbis sndfile +TEMPLATE = subdirs + +contains(CONFIG, MUSEPACK_PLUGIN){ + SUBDIRS += mpc + message(***************************) + message(* Musepack plugin enabled *) + message(***************************) +} + +contains(CONFIG, FLAC_PLUGIN){ + SUBDIRS += flac + message(***********************) + message(* FLAC plugin enabled *) + message(***********************) +} + +contains(CONFIG, FFMPEG_PLUGIN){ + SUBDIRS += ffmpeg + message(*************************) + message(* FFMPEG plugin enabled *) + message(*************************) +} + diff --git a/src/plugins/Input/ffmpeg/CMakeLists.txt b/src/plugins/Input/ffmpeg/CMakeLists.txt new file mode 100644 index 000000000..1ca0d4b7a --- /dev/null +++ b/src/plugins/Input/ffmpeg/CMakeLists.txt @@ -0,0 +1,94 @@ +project(libffmpeg) + +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) +# fixes ffmpeg defines +ADD_DEFINITIONS(-D__STDC_CONSTANT_MACROS) + +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}/../../../) + +# libffmpeg and taglib +PKGCONFIG(libavcodec LIBAVCODEC_INCLUDE_DIR LIBAVCODEC_LINK_DIR LIBAVCODEC_LINK_FLAGS LIBAVCODEC_CFLAGS) +PKGCONFIG(libavformat LIBAVFORMAT_INCLUDE_DIR LIBAVFORMAT_LINK_DIR LIBAVFORMAT_LINK_FLAGS LIBAVFORMAT_CFLAGS) +PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS) + +IF(NOT LIBAVCODEC_LINK_FLAGS) + SET(LIBAVCODEC_LINK_FLAGS -lavcodec) +ENDIF(NOT LIBAVCODEC_LINK_FLAGS) + +IF(NOT LIBAVFORMAT_LINK_FLAGS) + SET(LIBAVFORMAT_LINK_FLAGS -lavformat) +ENDIF(NOT LIBAVFORMAT_LINK_FLAGS) + +IF(NOT TAGLIB_LINK_FLAGS) + SET(TAGLIB_LINK_FLAGS -ltag) + SET(TAGLIB_INCLUDE_DIR /usr/include/taglib) + SET(TAGLIB_CFLAGS -I/usr/include/taglib) +ENDIF(NOT TAGLIB_LINK_FLAGS) + +include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR}) +link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR}) + +ADD_DEFINITIONS(${LIBAVCODEC_CFLAGS}) +ADD_DEFINITIONS(${LIBAVFORMAT_CFLAGS}) +ADD_DEFINITIONS(${TAGLIB_CFLAGS}) + + +SET(libffmpeg_SRCS + decoder_ffmpeg.cpp + decoderffmpegfactory.cpp + detailsdialog.cpp +) + +SET(libffmpeg_MOC_HDRS + decoderffmpegfactory.h + decoder_ffmpeg.h + detailsdialog.h +) + +SET(libffmpeg_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libffmpeg_RCC_SRCS ${libffmpeg_RCCS}) + +QT4_WRAP_CPP(libffmpeg_MOC_SRCS ${libffmpeg_MOC_HDRS}) + +# user interface + + +SET(libffmpeg_UIS + detailsdialog.ui +) + +QT4_WRAP_UI(libffmpeg_UIS_H ${libffmpeg_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(ffmpeg SHARED ${libffmpeg_SRCS} ${libffmpeg_MOC_SRCS} ${libffmpeg_UIS_H} + ${libffmpeg_RCC_SRCS}) +target_link_libraries(ffmpeg ${QT_LIBRARIES} -lqmmp ${LIBAVCODEC_LINK_FLAGS} ${LIBAVFORMAT_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS}) +install(TARGETS ffmpeg DESTINATION ${LIB_DIR}/qmmp/Input PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) + diff --git a/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp b/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp new file mode 100644 index 000000000..095f818e7 --- /dev/null +++ b/src/plugins/Input/ffmpeg/decoder_ffmpeg.cpp @@ -0,0 +1,342 @@ +/*************************************************************************** + * 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 <QFile> + +#include "constants.h" +#include "buffer.h" +#include "output.h" +#include "recycler.h" + +#include "decoder_ffmpeg.h" + +// Decoder class + +DecoderFFmpeg::DecoderFFmpeg(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + inited = FALSE; + user_stop = FALSE; + stat = 0; + output_buf = 0; + output_bytes = 0; + output_at = 0; + bks = 0; + done = FALSE; + finish = FALSE; + freq = 0; + bitrate = 0; + seekTime = -1.0; + totalTime = 0.0; + chan = 0; + output_size = 0; + ic = 0; + wma_outbuf = 0; +} + + +DecoderFFmpeg::~DecoderFFmpeg() +{ + deinit(); + if (wma_outbuf) + { + delete [] wma_outbuf; + wma_outbuf = 0; + } + if (output_buf) + delete [] output_buf; + output_buf = 0; + + if (ic) + av_close_input_file(ic); +} + + +void DecoderFFmpeg::stop() +{ + user_stop = TRUE; +} + + +void DecoderFFmpeg::flush(bool final) +{ + ulong min = final ? 0 : bks; + + while ((! done && ! finish) && output_bytes > min) + { + output()->recycler()->mutex()->lock (); + + while ((! done && ! finish) && output()->recycler()->full()) + { + mutex()->unlock(); + + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock (); + done = user_stop; + } + + if (user_stop || finish) + { + inited = FALSE; + done = TRUE; + } + else + { + output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan); + output_size += bks; + output_at = output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + + +bool DecoderFFmpeg::initialize() +{ + bks = blockSize(); + inited = user_stop = done = finish = FALSE; + freq = bitrate = 0; + stat = chan = 0; + output_size = 0; + seekTime = -1.0; + totalTime = 0.0; + + + if (! input()) + { + error("DecoderFFmpeg: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()) + { + error("DecoderFFmpeg: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + QString filename = qobject_cast<QFile*>(input())->fileName (); + input()->close(); + avcodec_init(); + avcodec_register_all(); + av_register_all(); + + AVCodec *codec; + if (av_open_input_file(&ic, filename.toLocal8Bit(), NULL,0, NULL) < 0) + { + qDebug("DecoderFFmpeg: cannot open input file"); + return FALSE; + } + for (wma_idx = 0; wma_idx < ic->nb_streams; wma_idx++) + { + c = ic->streams[wma_idx]->codec; + if (c->codec_type == CODEC_TYPE_AUDIO) break; + } + + av_find_stream_info(ic); + + codec = avcodec_find_decoder(c->codec_id); + + if (!codec) return FALSE; + if (avcodec_open(c, codec) < 0) + return FALSE; + + totalTime = ic->duration/AV_TIME_BASE; + + configure(c->sample_rate, c->channels, 16, c->bit_rate); + + bitrate = c->bit_rate; + chan = c->channels; + wma_outbuf = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*sizeof(int16_t)]; + inited = TRUE; + qDebug("DecoderFFmpeg: initialize succes"); + return TRUE; +} + + +double DecoderFFmpeg::lengthInSeconds() +{ + if (! inited) + return 0; + + return totalTime; +} + + +void DecoderFFmpeg::seek(double pos) +{ + seekTime = pos; +} + + +void DecoderFFmpeg::deinit() +{ + inited = user_stop = done = finish = FALSE; + freq = bitrate = 0; + stat = chan = 0; + output_size = 0; +} + +void DecoderFFmpeg::run() +{ +// mpc_uint32_t vbrAcc = 0; +// mpc_uint32_t vbrUpd = 0; + uint8_t *inbuf_ptr; + int out_size, size; + AVPacket pkt; + + mutex()->lock (); + + if (! inited) + { + mutex()->unlock(); + + return; + } + stat = DecoderState::Decoding; + mutex()->unlock(); + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + while (! done && ! finish) + { + mutex()->lock (); + // decode + + if (seekTime >= 0.0) + { + int64_t timestamp; + timestamp = int64_t(seekTime)*AV_TIME_BASE; + if (ic->start_time != AV_NOPTS_VALUE) + timestamp += ic->start_time; + av_seek_frame(ic, -1, timestamp, AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(c); + seekTime = -1.0; + } + + int l = 0; + if (av_read_frame(ic, &pkt) < 0) + { + finish = TRUE; + goto end; + } + size = pkt.size; + inbuf_ptr = pkt.data; + + out_size = 0; + + while (size > 0) + { + out_size = AVCODEC_MAX_AUDIO_FRAME_SIZE*sizeof(int16_t); + l = avcodec_decode_audio2(c, (int16_t *)(wma_outbuf), &out_size, inbuf_ptr, size); + + if(l < 0) + goto end; + ffmpeg_out(out_size); + size -= l; + inbuf_ptr += l; + if (pkt.data) + av_free_packet(&pkt); + } + bitrate = c->bit_rate/1024; +end: + if (finish) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock (); + // end of stream + while (! output()->recycler()->empty() && ! user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock (); + } + output()->recycler()->mutex()->unlock(); + } + + done = TRUE; + if (! user_stop) + { + finish = TRUE; + } + } + + mutex()->unlock(); + + } + + mutex()->lock (); + + if (finish) + stat = DecoderState::Finished; + else if (user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + deinit(); +} + +void DecoderFFmpeg::ffmpeg_out(int size) +{ + if (size == 0) + return; + int at = 0; + int to_copy = 0; + while (size > 0 && !user_stop) + { + to_copy = qMin(int(globalBufferSize - output_at), int(size) ); + memmove ( (char *) (output_buf + output_at), wma_outbuf + at, to_copy); + at += to_copy; + size -= to_copy; + output_at += to_copy; + output_bytes += to_copy; + if (output()) + flush(); + } +} diff --git a/src/plugins/Input/ffmpeg/decoder_ffmpeg.h b/src/plugins/Input/ffmpeg/decoder_ffmpeg.h new file mode 100644 index 000000000..956e5f32b --- /dev/null +++ b/src/plugins/Input/ffmpeg/decoder_ffmpeg.h @@ -0,0 +1,78 @@ +/*************************************************************************** + * 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 __decoder_ffmeg_h +#define __decoder_ffmeg_h + +extern "C"{ +#include <ffmpeg/avformat.h> +#include <ffmpeg/avcodec.h> +} +#include "decoder.h" + +class DecoderFFmpeg : public Decoder +{ +public: + DecoderFFmpeg(QObject *, DecoderFactory *, QIODevice *, Output *); + virtual ~DecoderFFmpeg(); + + // Standard Decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + // Equalizer + bool isEQSupported() const { return FALSE; } + void setEQEnabled(bool) { ; } + void setEQGain(int) { ; } + void setEQBands(int[10]) { ; } + + +private: + // thread run function + void run(); + // helper functions + void flush(bool = FALSE); + void deinit(); + void ffmpeg_out(int size); + + bool inited, user_stop; + int stat; + + // output buffer + char *output_buf; + ulong output_bytes, output_at; + + AVFormatContext *ic; + AVCodecContext *c; + uint wma_st_buff, wma_idx, wma_idx2; + uint8_t *wma_outbuf; + + unsigned int bks; + bool done, finish; + long freq, bitrate; + int chan; + unsigned long output_size; + double totalTime, seekTime; +}; + + +#endif // __decoder_ffmpeg_h diff --git a/src/plugins/Input/ffmpeg/decoderffmpegfactory.cpp b/src/plugins/Input/ffmpeg/decoderffmpegfactory.cpp new file mode 100644 index 000000000..789635a15 --- /dev/null +++ b/src/plugins/Input/ffmpeg/decoderffmpegfactory.cpp @@ -0,0 +1,93 @@ +#include <QtGui> + +extern "C"{ +#include <ffmpeg/avformat.h> +#include <ffmpeg/avcodec.h> +} + +#include "detailsdialog.h" +#include "decoder_ffmpeg.h" +#include "decoderffmpegfactory.h" + + +// DecoderFFmpegFactory + +bool DecoderFFmpegFactory::supports(const QString &source) const +{ + + return (source.right(4).toLower() == ".wma" || source.right(4).toLower() == ".wav"); +} + +bool DecoderFFmpegFactory::canDecode(QIODevice *) const +{ + return FALSE; +} + +const DecoderProperties DecoderFFmpegFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("WMA Files"); + properties.filter = "*.wma *.wav"; + properties.description = tr("WMA Files"); + //properties.contentType = ""; + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +Decoder *DecoderFFmpegFactory::create(QObject *parent, QIODevice *input, + Output *output) +{ + return new DecoderFFmpeg(parent, this, input, output); +} + +FileTag *DecoderFFmpegFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + avcodec_init(); + avcodec_register_all(); + av_register_all(); + AVFormatContext *in; + + if (av_open_input_file(&in, source.toLocal8Bit(), NULL,0, NULL) < 0) + return ftag; + av_find_stream_info(in); + ftag->setValue(FileTag::ALBUM, QString::fromUtf8(in->album).trimmed()); + ftag->setValue(FileTag::ARTIST, QString::fromUtf8(in->author).trimmed()); + ftag->setValue(FileTag::COMMENT, QString::fromUtf8(in->comment).trimmed()); + ftag->setValue(FileTag::GENRE, QString::fromUtf8(in->genre).trimmed()); + ftag->setValue(FileTag::TITLE, QString::fromUtf8(in->title).trimmed()); + ftag->setValue(FileTag::YEAR, in->year); + ftag->setValue(FileTag::TRACK, in->track); + ftag->setValue(FileTag::LENGTH ,int(in->duration/AV_TIME_BASE)); + av_close_input_file(in); + return ftag; +} + +QObject* DecoderFFmpegFactory::showDetails(QWidget *parent, const QString &path) +{ + DetailsDialog *d = new DetailsDialog(parent, path); + d -> show(); + return d; +} + +void DecoderFFmpegFactory::showSettings(QWidget *) +{} + +void DecoderFFmpegFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About FFmpeg Audio Plugin"), + tr("Qmmp FFmpeg Audio Plugin")+"\n"+ + tr("Suppored formats: WMA")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *DecoderFFmpegFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/ffmpeg_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderFFmpegFactory) diff --git a/src/plugins/Input/ffmpeg/decoderffmpegfactory.h b/src/plugins/Input/ffmpeg/decoderffmpegfactory.h new file mode 100644 index 000000000..16b6de1dd --- /dev/null +++ b/src/plugins/Input/ffmpeg/decoderffmpegfactory.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 DECODERFFMPEGFACTORY_H +#define DECODERFFMPEGFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> +#include <filetag.h> + + + + +class DecoderFFmpegFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/ffmpeg/detailsdialog.cpp b/src/plugins/Input/ffmpeg/detailsdialog.cpp new file mode 100644 index 000000000..076cd6872 --- /dev/null +++ b/src/plugins/Input/ffmpeg/detailsdialog.cpp @@ -0,0 +1,103 @@ +/*************************************************************************** + * 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. * + ***************************************************************************/ + +extern "C" +{ +#include <ffmpeg/avformat.h> +#include <ffmpeg/avcodec.h> +} +#include <QFile> + +#include "detailsdialog.h" + +DetailsDialog::DetailsDialog(QWidget *parent, const QString &path) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_path = path; + setWindowTitle (path.section('/',-1)); + path.section('/',-1); + + ui.pathLineEdit->setText(m_path); + if(QFile::exists(m_path)) + loadInfo(); +} + + +DetailsDialog::~DetailsDialog() +{} + +void DetailsDialog::loadInfo() +{ + AVFormatContext *in; + avcodec_init(); + avcodec_register_all(); + av_register_all(); + if (av_open_input_file(&in, m_path.toLocal8Bit(), NULL,0, NULL) < 0) + return; + av_find_stream_info(in); + QString string = QString::fromUtf8(in->title).trimmed(); + ui.titleLineEdit->setText(string); + string = QString::fromUtf8(in->author).trimmed(); + ui.artistLineEdit->setText(string); + string = QString::fromUtf8(in->album).trimmed(); + ui.albumLineEdit->setText(string); + string = QString::fromUtf8(in->comment).trimmed(); + ui.commentLineEdit->setText(string); + string = QString("%1").arg(in->year); + ui.yearLineEdit->setText(string); + string = QString("%1").arg(in->track); + ui.trackLineEdit->setText(string); + string = QString::fromUtf8(in->genre).trimmed(); + ui.genreLineEdit->setText(string); + + QString text; + text = QString("%1").arg(int(in->duration/AV_TIME_BASE)/60); + text +=":"+QString("%1").arg(int(in->duration/AV_TIME_BASE)%60,2,10,QChar('0')); + ui.lengthLabel->setText(text); + + + text = QString("%1").arg(in->file_size/1024)+" "+tr("KB"); + ui.fileSizeLabel->setText(text); + text = QString("%1").arg(in->bit_rate/1000); + ui.bitrateLabel->setText(text+" "+tr("kbps")); + + AVCodecContext *c = 0; + uint wma_idx; + + for (wma_idx = 0; wma_idx < in->nb_streams; wma_idx++) + { + c = in->streams[wma_idx]->codec; + if (c->codec_type == CODEC_TYPE_AUDIO) break; + } + + if (c) + { + text = QString("%1").arg(c->sample_rate); + ui.sampleRateLabel->setText(text+" "+tr("Hz")); + text = QString("%1").arg(c->channels); + ui.channelsLabel->setText(text); + } + + av_close_input_file(in); +} + + diff --git a/src/plugins/Input/ffmpeg/detailsdialog.h b/src/plugins/Input/ffmpeg/detailsdialog.h new file mode 100644 index 000000000..258a1bd21 --- /dev/null +++ b/src/plugins/Input/ffmpeg/detailsdialog.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * 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 DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include <QDialog> + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class DetailsDialog : public QDialog +{ +Q_OBJECT +public: + DetailsDialog(QWidget *parent = 0, const QString &path = 0); + + ~DetailsDialog(); + +private: + void loadInfo(); + Ui::DetailsDialog ui; + QString m_path; + +}; + +#endif diff --git a/src/plugins/Input/ffmpeg/detailsdialog.ui b/src/plugins/Input/ffmpeg/detailsdialog.ui new file mode 100644 index 000000000..70ed57052 --- /dev/null +++ b/src/plugins/Input/ffmpeg/detailsdialog.ui @@ -0,0 +1,332 @@ +<ui version="4.0" > + <class>DetailsDialog</class> + <widget class="QDialog" name="DetailsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>449</width> + <height>375</height> + </rect> + </property> + <property name="windowTitle" > + <string>Details</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item rowspan="2" row="1" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox" > + <property name="minimumSize" > + <size> + <width>175</width> + <height>16</height> + </size> + </property> + <property name="title" > + <string>ASF Info</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="4" column="1" > + <widget class="QLabel" name="bitrateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="channelsLabel" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Bitrate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_10" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Channels:</string> + </property> + <property name="textFormat" > + <enum>Qt::PlainText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="sampleRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Sample rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="lengthLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Length:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="fileSizeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>74</width> + <height>151</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </item> + <item row="1" column="2" colspan="2" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>WMA Tag</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="1" colspan="2" > + <widget class="QPushButton" name="pushButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Save</string> + </property> + </widget> + </item> + <item row="4" column="3" > + <widget class="QLineEdit" name="trackLineEdit" /> + </item> + <item row="4" column="2" > + <widget class="QLabel" name="label_26" > + <property name="text" > + <string>Track number:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="yearLineEdit" /> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_25" > + <property name="text" > + <string>Year:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_27" > + <property name="text" > + <string>Genre:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_24" > + <property name="text" > + <string>Comment:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_23" > + <property name="text" > + <string>Album:</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_22" > + <property name="text" > + <string>Artist:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_21" > + <property name="text" > + <string>Title:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="titleLineEdit" /> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLineEdit" name="artistLineEdit" /> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLineEdit" name="albumLineEdit" /> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLineEdit" name="commentLineEdit" /> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLineEdit" name="genreLineEdit" /> + </item> + </layout> + </widget> + </item> + <item row="2" column="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="pushButton_3" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_28" > + <property name="text" > + <string>File path:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="pathLineEdit" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_3</sender> + <signal>clicked()</signal> + <receiver>DetailsDialog</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>623</x> + <y>353</y> + </hint> + <hint type="destinationlabel" > + <x>539</x> + <y>352</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/ffmpeg/ffmpeg.pro b/src/plugins/Input/ffmpeg/ffmpeg.pro new file mode 100644 index 000000000..7de7c7d5b --- /dev/null +++ b/src/plugins/Input/ffmpeg/ffmpeg.pro @@ -0,0 +1,35 @@ +include(../../plugins.pri) + +FORMS += detailsdialog.ui +HEADERS += decoderffmpegfactory.h \ + detailsdialog.h \ + decoder_ffmpeg.h +SOURCES += detailsdialog.cpp \ + decoder_ffmpeg.cpp \ + decoderffmpegfactory.cpp + + +QMAKE_CLEAN = ../libffmpeg.so + +TARGET=$$PLUGINS_PREFIX/Input/ffmpeg +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libffmpeg.so + + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -I/usr/include +DEFINES += __STDC_CONSTANT_MACROS +PKGCONFIG += libavcodec libavformat +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target diff --git a/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_cs.ts b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_cs.ts new file mode 100644 index 000000000..8f686a145 --- /dev/null +++ b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_cs.ts @@ -0,0 +1,150 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="pl"> +<defaultcodec></defaultcodec> +<context> + <name>DecoderFFmpegFactory</name> + <message> + <location filename="../decoderffmpegfactory.cpp" line="33"/> + <source>WMA Files</source> + <translation>Soubory WMA</translation> + </message> + <message> + <location filename="../decoderffmpegfactory.cpp" line="61"/> + <source>About FFmpeg Audio Plugin</source> + <translation>O pluginu FFmpeg</translation> + </message> + <message> + <location filename="../decoderffmpegfactory.cpp" line="62"/> + <source>Qmmp FFmpeg Audio Plugin</source> + <translation>Vstupní plugin Qmmp FFmpeg</translation> + </message> + <message> + <location filename="../decoderffmpegfactory.cpp" line="63"/> + <source>Suppored formats: WMA</source> + <translation>Podporované formáty: WMA</translation> + </message> + <message> + <location filename="../decoderffmpegfactory.cpp" line="64"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Autor: Ilja Kotov <forkotov02@hotmail.ru></translation> + </message> + <message> + <location filename="../decoderffmpegfactory.cpp" line="19"/> + <source>FFmpeg Plugin</source> + <translation>Plugin FFmpeg</translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="81"/> + <source>kbps</source> + <translation>kbps</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="95"/> + <source>Hz</source> + <translation>Hz</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="78"/> + <source>KB</source> + <translation>KB</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Podrobnosti</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="70"/> + <source>File size:</source> + <translation>Velikost souboru:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="130"/> + <source>-</source> + <translation>-</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="103"/> + <source>Sample rate:</source> + <translation>Vzorkovací frakvence:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="176"/> + <source>Save</source> + <translation>Uložit</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="186"/> + <source>Track number:</source> + <translation>Číslo stopy:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="199"/> + <source>Year:</source> + <translation>Rok:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="209"/> + <source>Genre:</source> + <translation>Žánr:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="219"/> + <source>Comment:</source> + <translation>Komentář:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="229"/> + <source>Album:</source> + <translation>Album:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="239"/> + <source>Artist:</source> + <translation>Umělec:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="249"/> + <source>Title:</source> + <translation>Název:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="290"/> + <source>Close</source> + <translation>Zavřít</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="297"/> + <source>File path:</source> + <translation>Cesta k souboru:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="120"/> + <source>Length:</source> + <translation>Délka:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="83"/> + <source>Channels:</source> + <translation>Počet kanálů:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="60"/> + <source>Bitrate:</source> + <translation>Datový tok:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>ASF Info</source> + <translation>Informace ASF</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="161"/> + <source>WMA Tag</source> + <translation>WMA tag</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..68ceb6f8b --- /dev/null +++ b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.qm diff --git a/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts new file mode 100644 index 000000000..a65f434f2 --- /dev/null +++ b/src/plugins/Input/ffmpeg/translations/ffmpeg_plugin_ru.ts @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>DecoderFFmpegFactory</name> + <message> + <location filename="../decoderffmegfactory.cpp" line="19"/> + <source>FFmpeg Plugin</source> + <translation>Модуль FFmpeg</translation> + </message> + <message> + <location filename="../decoderffmegfactory.cpp" line="33"/> + <source>WMA Files</source> + <translation>Файлы WMA</translation> + </message> + <message> + <location filename="../decoderffmegfactory.cpp" line="61"/> + <source>About FFmpeg Audio Plugin</source> + <translation>Об аудио-модуле FFmpeg</translation> + </message> + <message> + <location filename="../decoderffmegfactory.cpp" line="62"/> + <source>Qmmp FFmpeg Audio Plugin</source> + <translation>Аудио-модуль FFmpeg для Qmmp</translation> + </message> + <message> + <location filename="../decoderffmegfactory.cpp" line="63"/> + <source>Suppored formats: WMA</source> + <translation>Поддерживаемые форматы: WMA</translation> + </message> + <message> + <location filename="../decoderffmegfactory.cpp" line="64"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="78"/> + <source>KB</source> + <translation>Кб</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="95"/> + <source>Hz</source> + <translation>Гц</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>ASF Info</source> + <translation>Информация ASF</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="130"/> + <source>-</source> + <translation></translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="60"/> + <source>Bitrate:</source> + <translation>Битовая частота:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="70"/> + <source>File size:</source> + <translation>Размер файла:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="83"/> + <source>Channels:</source> + <translation>Каналов:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="103"/> + <source>Sample rate:</source> + <translation>Дискретизация:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="120"/> + <source>Length:</source> + <translation>Длительность:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="161"/> + <source>WMA Tag</source> + <translation>WMA-тег</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="176"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="186"/> + <source>Track number:</source> + <translation>Номер трека:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="199"/> + <source>Year:</source> + <translation>Год:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="209"/> + <source>Genre:</source> + <translation>Жанр:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="219"/> + <source>Comment:</source> + <translation>Комментарий:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="229"/> + <source>Album:</source> + <translation>Альбом:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="239"/> + <source>Artist:</source> + <translation>Исполнитель:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="249"/> + <source>Title:</source> + <translation>Название:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="290"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="297"/> + <source>File path:</source> + <translation>Путь к файлу:</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="81"/> + <source>kbps</source> + <translation>Кб/с</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Информация</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/ffmpeg/translations/translations.qrc b/src/plugins/Input/ffmpeg/translations/translations.qrc new file mode 100644 index 000000000..5e15f321d --- /dev/null +++ b/src/plugins/Input/ffmpeg/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>ffmpeg_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Input/flac/CMakeLists.txt b/src/plugins/Input/flac/CMakeLists.txt new file mode 100644 index 000000000..24041608c --- /dev/null +++ b/src/plugins/Input/flac/CMakeLists.txt @@ -0,0 +1,86 @@ +project(libflac) + +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}/../../../) + +# libflac and taglib +PKGCONFIG(flac FLAC_INCLUDE_DIR FLAC_LINK_DIR FLAC_LINK_FLAGS FLAC_CFLAGS) +PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS) + +IF(NOT FLAC_LINK_FLAGS) + SET(FLAC_LINK_FLAGS -lFLAC) +ENDIF(NOT FLAC_LINK_FLAGS) + +IF(NOT TAGLIB_LINK_FLAGS) + SET(TAGLIB_LINK_FLAGS -ltag) + SET(TAGLIB_INCLUDE_DIR /usr/include/taglib) + SET(TAGLIB_CFLAGS -I/usr/include/taglib) +ENDIF(NOT TAGLIB_LINK_FLAGS) + +include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR}) +link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR}) + +ADD_DEFINITIONS(${FLAC_CFLAGS}) +ADD_DEFINITIONS(${TAGLIB_CFLAGS}) + + +SET(libflac_SRCS + decoder_flac.cpp + decoderflacfactory.cpp + detailsdialog.cpp +) + +SET(libflac_MOC_HDRS + decoderflacfactory.h + decoder_flac.h + detailsdialog.h +) + +SET(libflac_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libflac_RCC_SRCS ${libflac_RCCS}) + +QT4_WRAP_CPP(libflac_MOC_SRCS ${libflac_MOC_HDRS}) + +# user interface + + +SET(libflac_UIS + detailsdialog.ui +) + +QT4_WRAP_UI(libflac_UIS_H ${libflac_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(flac SHARED ${libflac_SRCS} ${libflac_MOC_SRCS} ${libflac_UIS_H} + ${libflac_RCC_SRCS}) +target_link_libraries(flac ${QT_LIBRARIES} -lqmmp ${FLAC_LINK_FLAGS} ${FLAC_CFLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS}) +install(TARGETS flac DESTINATION ${LIB_DIR}/qmmp/Input PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) + diff --git a/src/plugins/Input/flac/decoder_flac.cpp b/src/plugins/Input/flac/decoder_flac.cpp new file mode 100644 index 000000000..b2a895bca --- /dev/null +++ b/src/plugins/Input/flac/decoder_flac.cpp @@ -0,0 +1,567 @@ +/*************************************************************************** + * 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 "constants.h" +#include "buffer.h" +#include "output.h" +#include "recycler.h" + +#include <QObject> +#include <QIODevice> +#include <FLAC/all.h> +#include "decoder_flac.h" + + + +static size_t pack_pcm_signed (FLAC__byte *data, + const FLAC__int32 * const input[], + unsigned wide_samples, + unsigned channels, unsigned bps) +{ + FLAC__byte * const start = data; + FLAC__int32 sample; + const FLAC__int32 *input_; + unsigned samples, channel; + unsigned bytes_per_sample; + unsigned incr; + + if (bps == 24) + bps = 32; /* we encode to 32-bit words */ + bytes_per_sample = bps / 8; + incr = bytes_per_sample * channels; + + for (channel = 0; channel < channels; channel++) + { + samples = wide_samples; + data = start + bytes_per_sample * channel; + input_ = input[channel]; + + while (samples--) + { + sample = *input_++; + + switch (bps) + { + case 8: + data[0] = sample; + break; + case 16: + data[1] = (FLAC__byte)(sample >> 8); + data[0] = (FLAC__byte)sample; + break; + case 32: + data[3] = (FLAC__byte)(sample >> 16); + data[2] = (FLAC__byte)(sample >> 8); + data[1] = (FLAC__byte)sample; + data[0] = 0; + break; + } + + data += incr; + } + } + + return wide_samples * channels * bytes_per_sample; +} + +static int flac_decode (void *void_data, char *buf, int buf_len) /*, + struct sound_params *sound_params)*/ +{ + //struct flac_data *data = (struct flac_data *)void_data; + DecoderFLAC *dflac = (DecoderFLAC *) void_data; + unsigned to_copy; + int bytes_per_sample; + FLAC__uint64 decode_position; + + bytes_per_sample = dflac->data()->bits_per_sample / 8; + + /*switch (bytes_per_sample) { + case 1: + sound_params->fmt = SFMT_S8; + break; + case 2: + sound_params->fmt = SFMT_S16 | SFMT_LE; + break; + case 3: + sound_params->fmt = SFMT_S32 | SFMT_LE; + break; + } + + sound_params->rate = data->sample_rate; + sound_params->channels = data->channels;*/ + + //decoder_error_clear (&data->error); + + if (!dflac->data()->sample_buffer_fill) + { + + if (FLAC__stream_decoder_get_state(dflac->data()->decoder) + == FLAC__STREAM_DECODER_END_OF_STREAM) + { + return 0; + } + + if (!FLAC__stream_decoder_process_single( + dflac->data()->decoder)) + { + return 0; + } + + /* Count the bitrate */ + if (!FLAC__stream_decoder_get_decode_position( + dflac->data()->decoder, &decode_position)) + decode_position = 0; + if (decode_position > dflac->data()->last_decode_position) + { + int bytes_per_sec = bytes_per_sample * dflac->data()->sample_rate + * dflac->data()->channels; + + dflac->data()->bitrate = int(((float)decode_position - + dflac->data()->last_decode_position) * 8.0 * + bytes_per_sec / + dflac->data()->sample_buffer_fill / 1000); + } + + dflac->data()->last_decode_position = decode_position; + } + + to_copy = qMin((unsigned)buf_len, dflac->data()->sample_buffer_fill); + memcpy (buf, dflac->data()->sample_buffer, to_copy); + memmove (dflac->data()->sample_buffer, + dflac->data()->sample_buffer + to_copy, + dflac->data()->sample_buffer_fill - to_copy); + dflac->data()->sample_buffer_fill -= to_copy; + return to_copy; +} + + +static FLAC__StreamDecoderReadStatus +flac_callback_read (const FLAC__StreamDecoder *, FLAC__byte buffer[], + size_t *bytes, void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + qint64 res; + + res = dflac->input()->read((char *)buffer, *bytes); + + if (res > 0) + { + *bytes = res; + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; + } + if (res == 0) + { + *bytes = res; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + +} + +static FLAC__StreamDecoderWriteStatus +flac_callback_write (const FLAC__StreamDecoder *, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + const unsigned wide_samples = frame->header.blocksize; + + if (dflac->data()->abort) + return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT; + + + dflac->data()->sample_buffer_fill = pack_pcm_signed ( + dflac->data()->sample_buffer, + buffer, wide_samples, + dflac->data()->channels, + dflac->data()->bits_per_sample); + + return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; +} + +static FLAC__StreamDecoderTellStatus +flac_callback_tell (const FLAC__StreamDecoder *, FLAC__uint64 *offset, void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + *offset = dflac->input()->pos (); + return FLAC__STREAM_DECODER_TELL_STATUS_OK; +} + +static FLAC__StreamDecoderSeekStatus +flac_callback_seek (const FLAC__StreamDecoder *, FLAC__uint64 offset, void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + + return dflac->input()->seek(offset) + ? FLAC__STREAM_DECODER_SEEK_STATUS_OK + : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; +} + +static FLAC__StreamDecoderLengthStatus +flac_callback_length (const FLAC__StreamDecoder *, FLAC__uint64 *stream_length, void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + *stream_length = dflac->input()->size(); + return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; +} + +static void +flac_callback_metadata (const FLAC__StreamDecoder *, const FLAC__StreamMetadata *metadata, void *client_data) +{ + DecoderFLAC *dflac = (DecoderFLAC *) client_data; + + if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) + { + qDebug ("DecoderFLAC: getting metadata info"); + + dflac->data()->total_samples = + (unsigned)(metadata->data.stream_info.total_samples + & 0xffffffff); + dflac->data()->bits_per_sample = + metadata->data.stream_info.bits_per_sample; + dflac->data()->channels = metadata->data.stream_info.channels; + dflac->data()->sample_rate = metadata->data.stream_info.sample_rate; + dflac->data()->length = dflac->data()->total_samples / dflac->data()->sample_rate; + } +} + +static FLAC__bool +flac_callback_eof (const FLAC__StreamDecoder *, void *) +{ + return FALSE; +} + +static void +flac_callback_error (const FLAC__StreamDecoder *, FLAC__StreamDecoderErrorStatus status, void *client_data) +{} + +// Decoder class + +DecoderFLAC::DecoderFLAC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + inited = FALSE; + user_stop = FALSE; + stat = 0; + output_buf = 0; + output_bytes = 0; + output_at = 0; + bks = 0; + done = FALSE; + finish = FALSE; + len = 0; + freq = 0; + bitrate = 0; + seekTime = -1.0; + totalTime = 0.0; + chan = 0; + output_size = 0; + m_data = 0; + + + + +} + + +DecoderFLAC::~DecoderFLAC() +{ + deinit(); + if (data()) + { + if (data()->decoder) + FLAC__stream_decoder_delete (data()->decoder); + delete data(); + m_data = 0; + } + + if (output_buf) + delete [] output_buf; + output_buf = 0; +} + + +void DecoderFLAC::stop() +{ + user_stop = TRUE; +} + + +void DecoderFLAC::flush(bool final) +{ + //qDebug("DecoderFLAC: flush()"); + ulong min = final ? 0 : bks; + + while ((! done && ! finish) && output_bytes > min) + { + output()->recycler()->mutex()->lock(); + + while ((! done && ! finish) && output()->recycler()->full()) + { + mutex()->unlock(); + + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock(); + done = user_stop; + } + + if (user_stop || finish) + { + inited = FALSE; + done = TRUE; + } + else + { + output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan); + output_size += bks; + output_at = output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + + +bool DecoderFLAC::initialize() +{ + bks = blockSize(); + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; + seekTime = -1.0; + totalTime = 0.0; + + + if (! input()) + { + error("DecoderFLAC: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()->isOpen()) + { + if (! input()->open(QIODevice::ReadOnly)) + { + + return FALSE; + } + } + + + if (! input()) + { + error("DecoderFLAC: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()->isOpen()) + { + if (! input()->open(QIODevice::ReadOnly)) + { + return FALSE; + } + } + if (!m_data) + { + m_data = new flac_data; + m_data->decoder = NULL; + } + + m_data->bitrate = -1; + m_data->abort = 0; + m_data->sample_buffer_fill = 0; + m_data->last_decode_position = 0; + if (!m_data->decoder) + { + qDebug("DecoderFLAC: creating FLAC__StreamDecoder"); + m_data->decoder = FLAC__stream_decoder_new (); + } + qDebug("DecoderFLAC: setting callbacks"); + if (FLAC__stream_decoder_init_stream( + m_data->decoder, + flac_callback_read, + flac_callback_seek, + flac_callback_tell, + flac_callback_length, + flac_callback_eof, + flac_callback_write, + flac_callback_metadata, + flac_callback_error, + this) != FLAC__STREAM_DECODER_INIT_STATUS_OK) + { + data()->ok = 0; + return FALSE; + } + + if (!FLAC__stream_decoder_process_until_end_of_metadata( + data()->decoder)) + { + data()->ok = 0; + return FALSE; + } + chan = data()->channels; + configure(data()->sample_rate, data()->channels, 16, bitrate); + totalTime = data()->length; + + inited = TRUE; + qDebug("DecoderFLAC: initialize succes"); + return TRUE; +} + + +double DecoderFLAC::lengthInSeconds() +{ + if (! inited) + return 0; + + return totalTime; +} + + +void DecoderFLAC::seek(double pos) +{ + seekTime = pos; +} + + +void DecoderFLAC::deinit() +{ + if(data()) + FLAC__stream_decoder_finish (data()->decoder); + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; +} + +void DecoderFLAC::run() +{ + mutex()->lock (); + + if (! inited) + { + mutex()->unlock(); + + return; + } + stat = DecoderState::Decoding; + mutex()->unlock(); + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + while (! done && ! finish) + { + mutex()->lock (); + // decode + + if (seekTime >= 0.0) + { + FLAC__uint64 target_sample; + + target_sample = (FLAC__uint64)((seekTime/(double)data()->length) * + (double)data()->total_samples); + + FLAC__stream_decoder_seek_absolute(data()->decoder, + target_sample); + seekTime = -1.0; + } + len = flac_decode (this, (char *) (output_buf + output_at), bks); + + if (len > 0) + { + bitrate = data()->bitrate; + output_at += len; + output_bytes += len; + + if (output()) + flush(); + + } + else if (len == 0) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock (); + // end of stream + while (! output()->recycler()->empty() && ! user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock (); + } + output()->recycler()->mutex()->unlock(); + } + + done = TRUE; + if (! user_stop) + { + finish = TRUE; + } + } + else + { + // error in read + error("DecoderFLAC: Error while decoding stream, File appears to be " + "corrupted"); + + finish = TRUE; + } + + mutex()->unlock(); + } + + mutex()->lock (); + + if (finish) + stat = DecoderState::Finished; + else if (user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + deinit(); +} diff --git a/src/plugins/Input/flac/decoder_flac.h b/src/plugins/Input/flac/decoder_flac.h new file mode 100644 index 000000000..4c85176ed --- /dev/null +++ b/src/plugins/Input/flac/decoder_flac.h @@ -0,0 +1,124 @@ +/*************************************************************************** + * 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 __decoder_flac_h +#define __decoder_flac_h + +class DecoderOgg; + +#include "decoder.h" + +#include <FLAC/all.h> + +#define MAX_SUPPORTED_CHANNELS 2 + +#define SAMPLES_PER_WRITE 512 +#define SAMPLE_BUFFER_SIZE ((FLAC__MAX_BLOCK_SIZE + SAMPLES_PER_WRITE) * MAX_SUPPORTED_CHANNELS * (32/8)) + +struct flac_data +{ + //FLAC__SeekableStreamDecoder *decoder; + FLAC__StreamDecoder *decoder; + struct io_stream *stream; + int bitrate; + int abort; /* abort playing (due to an error) */ + + unsigned length; + unsigned total_samples; + + FLAC__byte sample_buffer[SAMPLE_BUFFER_SIZE]; + unsigned sample_buffer_fill; + + /* sound parameters */ + unsigned bits_per_sample; + unsigned sample_rate; + unsigned channels; + + FLAC__uint64 last_decode_position; + + int ok; /* was this stream successfully opened? */ + //struct decoder_error error; +}; + +class DecoderFLAC : public Decoder +{ +public: + DecoderFLAC(QObject *, DecoderFactory *, QIODevice *, Output *); + virtual ~DecoderFLAC(); + + // Standard Decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + // Equalizer + bool isEQSupported() const + { + return FALSE; + } + void setEQEnabled(bool) + { + ; + } + void setEQGain(int) + { + ; + } + void setEQBands(int[10]) + { + ; + } + + struct flac_data *data() + { + return m_data; + } + + +private: + // thread run function + void run(); + struct flac_data *m_data; + // helper functions + void flush(bool = FALSE); + void deinit(); + + bool inited, user_stop; + int stat; + + // output buffer + char *output_buf; + ulong output_bytes, output_at; + + // FLAC Decoder + //FLAC__SeekableStreamDecoder *m_flacDecoder; + FLAC__StreamDecoder *m_flacDecoder; + + unsigned int bks; + bool done, finish; + long len, freq, bitrate; + int chan; + unsigned long output_size; + double totalTime, seekTime; +}; + + +#endif // __decoder_flac_h diff --git a/src/plugins/Input/flac/decoderflacfactory.cpp b/src/plugins/Input/flac/decoderflacfactory.cpp new file mode 100644 index 000000000..5abb60b39 --- /dev/null +++ b/src/plugins/Input/flac/decoderflacfactory.cpp @@ -0,0 +1,95 @@ +#include <QtGui> +#include <taglib/tag.h> +#include <taglib/fileref.h> + +#include "detailsdialog.h" +#include "decoder_flac.h" +#include "decoderflacfactory.h" + + +// DecoderFLACFactory + +bool DecoderFLACFactory::supports(const QString &source) const +{ + + return (source.right(5).toLower() == ".flac"); +} + +bool DecoderFLACFactory::canDecode(QIODevice *input) const +{ + return FALSE; +} + +const DecoderProperties DecoderFLACFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("FLAC Plugin"); + properties.filter = "*.flac"; + properties.description = tr("FLAC Files"); + //properties.contentType = ; + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +Decoder *DecoderFLACFactory::create(QObject *parent, QIODevice *input, + Output *output) +{ + return new DecoderFLAC(parent, this, input, output); +} + +FileTag *DecoderFLACFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + + TagLib::FileRef fileRef(source.toLocal8Bit ()); + TagLib::Tag *tag = fileRef.tag(); + + if (tag && !tag->isEmpty()) + { + ftag->setValue(FileTag::ALBUM, + QString::fromUtf8(tag->album().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::ARTIST, + QString::fromUtf8(tag->artist().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::COMMENT, + QString::fromUtf8(tag->comment().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::GENRE, + QString::fromUtf8(tag->genre().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::TITLE, + QString::fromUtf8(tag->title().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::YEAR, tag->year()); + ftag->setValue(FileTag::TRACK, tag->track()); + } + + if (fileRef.audioProperties()) + ftag->setValue(FileTag::LENGTH, fileRef.audioProperties()->length()); + + return ftag; +} + +QObject* DecoderFLACFactory::showDetails(QWidget *parent, const QString &path) +{ + DetailsDialog *d = new DetailsDialog(parent, path); + d -> show(); + return d; +} + +void DecoderFLACFactory::showSettings(QWidget *) +{} + +void DecoderFLACFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About FLAC Audio Plugin"), + tr("Qmmp FLAC Audio Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *DecoderFLACFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/flac_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderFLACFactory) diff --git a/src/plugins/Input/flac/decoderflacfactory.h b/src/plugins/Input/flac/decoderflacfactory.h new file mode 100644 index 000000000..586bc3b33 --- /dev/null +++ b/src/plugins/Input/flac/decoderflacfactory.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 DECODERFLACFACTORY_H +#define DECODERFLACFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> +#include <filetag.h> + + + + +class DecoderFLACFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/flac/detailsdialog.cpp b/src/plugins/Input/flac/detailsdialog.cpp new file mode 100644 index 000000000..2826b6cdb --- /dev/null +++ b/src/plugins/Input/flac/detailsdialog.cpp @@ -0,0 +1,118 @@ +/*************************************************************************** + * 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 <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/flacfile.h> + +#include <QFile> +#include <QFileInfo> + +#include "detailsdialog.h" + +#define QStringToTString_qt4(s) TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8) + +DetailsDialog::DetailsDialog(QWidget *parent, const QString &path) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_path = path; + setWindowTitle (path.section('/',-1)); + path.section('/',-1); + ui.pathLineEdit->setText(m_path); + if (QFile::exists(m_path)) + { + loadFLACInfo(); + loadTag(); + } +} + + +DetailsDialog::~DetailsDialog() +{} + +void DetailsDialog::loadFLACInfo() +{ + TagLib::FLAC::File f (m_path.toLocal8Bit()); + //l.label + //ui. f.audioProperties()->level(); + QString text; + text = QString("%1").arg(f.audioProperties()->length()/60); + text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0')); + ui.lengthLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->sampleRate()); + ui.sampleRateLabel->setText(text+" "+tr("Hz")); + text = QString("%1").arg(f.audioProperties()->channels()); + ui.channelsLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->bitrate()); + ui.bitrateLabel->setText(text+" "+tr("kbps")); + text = QString("%1").arg(f.audioProperties()->sampleWidth()); + ui.sampleWidthLabel->setText(text+" "+tr("bits")); + text = QString("%1 "+tr("KB")).arg(f.length()/1024); + ui.fileSizeLabel->setText(text); + +} + +void DetailsDialog::loadTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + if (f.tag()) + { //TODO: load codec name from config + + TagLib::String title = f.tag()->title(); + TagLib::String artist = f.tag()->artist(); + TagLib::String album = f.tag()->album(); + TagLib::String comment = f.tag()->comment(); + TagLib::String genre = f.tag()->genre(); + QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed(); + ui.titleLineEdit->setText(string); + string = QString::fromUtf8(artist.toCString(TRUE)).trimmed(); + ui.artistLineEdit->setText(string); + string = QString::fromUtf8(album.toCString(TRUE)).trimmed(); + ui.albumLineEdit->setText(string); + string = QString::fromUtf8(comment.toCString(TRUE)).trimmed(); + ui.commentLineEdit->setText(string); + string = QString("%1").arg(f.tag()->year()); + ui.yearLineEdit->setText(string); + string = QString("%1").arg(f.tag()->track()); + ui.trackLineEdit->setText(string); + string = QString::fromUtf8(genre.toCString(TRUE)).trimmed(); + ui.genreLineEdit->setText(string); + } + QFileInfo info(m_path); + ui.saveButton->setEnabled(info.isWritable()); + connect(ui.saveButton, SIGNAL(clicked()), SLOT(saveTag())); +} + +void DetailsDialog::saveTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + f.tag()->setTitle(QStringToTString_qt4(ui.titleLineEdit->text())); + f.tag()->setArtist(QStringToTString_qt4(ui.artistLineEdit->text())); + f.tag()->setAlbum(QStringToTString_qt4(ui.albumLineEdit->text())); + f.tag()->setComment(QStringToTString_qt4(ui.commentLineEdit->text())); + f.tag()->setGenre(QStringToTString_qt4(ui.genreLineEdit->text())); + f.tag()->setYear(ui.yearLineEdit->text().toUInt()); + f.tag()->setTrack(ui.trackLineEdit->text().toUInt()); + + f.save(); +} diff --git a/src/plugins/Input/flac/detailsdialog.h b/src/plugins/Input/flac/detailsdialog.h new file mode 100644 index 000000000..80c17544c --- /dev/null +++ b/src/plugins/Input/flac/detailsdialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * 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 DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include <QDialog> + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class DetailsDialog : public QDialog +{ +Q_OBJECT +public: + DetailsDialog(QWidget *parent = 0, const QString &path = 0); + + ~DetailsDialog(); + +private slots: + void saveTag(); + +private: + void loadFLACInfo(); + void loadTag(); + Ui::DetailsDialog ui; + QString m_path; + +}; + +#endif diff --git a/src/plugins/Input/flac/detailsdialog.ui b/src/plugins/Input/flac/detailsdialog.ui new file mode 100644 index 000000000..5ea739953 --- /dev/null +++ b/src/plugins/Input/flac/detailsdialog.ui @@ -0,0 +1,349 @@ +<ui version="4.0" > + <class>DetailsDialog</class> + <widget class="QDialog" name="DetailsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>449</width> + <height>375</height> + </rect> + </property> + <property name="windowTitle" > + <string>Details</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item rowspan="2" row="1" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox" > + <property name="minimumSize" > + <size> + <width>175</width> + <height>16</height> + </size> + </property> + <property name="title" > + <string>FLAC Info</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>74</width> + <height>151</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" colspan="2" > + <widget class="QLabel" name="fileSizeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Length:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <widget class="QLabel" name="lengthLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Sample rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2" > + <widget class="QLabel" name="sampleRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_10" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Channels:</string> + </property> + <property name="textFormat" > + <enum>Qt::PlainText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Bitrate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" colspan="2" > + <widget class="QLabel" name="channelsLabel" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="4" column="1" colspan="2" > + <widget class="QLabel" name="bitrateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Sample width:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLabel" name="sampleWidthLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="pathLineEdit" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_28" > + <property name="text" > + <string>File path:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="pushButton_3" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2" colspan="2" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>FLAC Tag</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="1" colspan="2" > + <widget class="QPushButton" name="saveButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Save</string> + </property> + </widget> + </item> + <item row="4" column="3" > + <widget class="QLineEdit" name="trackLineEdit" /> + </item> + <item row="4" column="2" > + <widget class="QLabel" name="label_26" > + <property name="text" > + <string>Track number:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="yearLineEdit" /> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_25" > + <property name="text" > + <string>Year:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_27" > + <property name="text" > + <string>Genre:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_24" > + <property name="text" > + <string>Comment:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_23" > + <property name="text" > + <string>Album:</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_22" > + <property name="text" > + <string>Artist:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_21" > + <property name="text" > + <string>Title:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="titleLineEdit" /> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLineEdit" name="artistLineEdit" /> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLineEdit" name="albumLineEdit" /> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLineEdit" name="commentLineEdit" /> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLineEdit" name="genreLineEdit" /> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_3</sender> + <signal>clicked()</signal> + <receiver>DetailsDialog</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>623</x> + <y>353</y> + </hint> + <hint type="destinationlabel" > + <x>539</x> + <y>352</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/flac/flac.pro b/src/plugins/Input/flac/flac.pro new file mode 100644 index 000000000..4ab46c296 --- /dev/null +++ b/src/plugins/Input/flac/flac.pro @@ -0,0 +1,36 @@ +# ???? ?????? ? KDevelop ?????????? qmake. +# ------------------------------------------- +# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/flac +# ???? - ??????????: + +include(../../plugins.pri) + +FORMS += detailsdialog.ui +HEADERS += decoderflacfactory.h \ + decoder_flac.h \ + detailsdialog.h +SOURCES += decoder_flac.cpp \ + decoderflacfactory.cpp \ + detailsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Input/flac +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libflac.so + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -I/usr/include +PKGCONFIG += taglib flac +#TRANSLATIONS = translations/flac_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target + diff --git a/src/plugins/Input/flac/translations/flac_plugin_ru.qm b/src/plugins/Input/flac/translations/flac_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..fbd97d091 --- /dev/null +++ b/src/plugins/Input/flac/translations/flac_plugin_ru.qm diff --git a/src/plugins/Input/flac/translations/flac_plugin_ru.ts b/src/plugins/Input/flac/translations/flac_plugin_ru.ts new file mode 100644 index 000000000..a1e9b98b7 --- /dev/null +++ b/src/plugins/Input/flac/translations/flac_plugin_ru.ts @@ -0,0 +1,154 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>DecoderFLACFactory</name> + <message> + <location filename="../decoderflacfactory.cpp" line="21"/> + <source>FLAC Plugin</source> + <translation>Модуль FLAC</translation> + </message> + <message> + <location filename="../decoderflacfactory.cpp" line="35"/> + <source>FLAC Files</source> + <translation>Файлы FLAC</translation> + </message> + <message> + <location filename="../decoderflacfactory.cpp" line="63"/> + <source>About FLAC Audio Plugin</source> + <translation>Об аудио-модуле FLAC</translation> + </message> + <message> + <location filename="../decoderflacfactory.cpp" line="64"/> + <source>Qmmp FLAC Audio Plugin</source> + <translation>Аудио-модуль FLAC для Qmmp</translation> + </message> + <message> + <location filename="../decoderflacfactory.cpp" line="65"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="54"/> + <source>Hz</source> + <translation>Гц</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="60"/> + <source>bits</source> + <translation>бит</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>FLAC Info</source> + <translation>Информация FLAC</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="160"/> + <source>-</source> + <translation></translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="63"/> + <source>Length:</source> + <translation>Длительность:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="80"/> + <source>Sample rate:</source> + <translation>Частота сэмплов:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="100"/> + <source>Channels:</source> + <translation>Каналов:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="113"/> + <source>File size:</source> + <translation>Размер файла:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="123"/> + <source>Bitrate:</source> + <translation>Битовая частота:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="150"/> + <source>Sample width:</source> + <translation>Ширина кадра:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="177"/> + <source>File path:</source> + <translation>Путь к файлу:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="187"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="215"/> + <source>FLAC Tag</source> + <translation>FLAC-тег</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="230"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="240"/> + <source>Track number:</source> + <translation>Номер трека:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="253"/> + <source>Year:</source> + <translation>Год:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="263"/> + <source>Genre:</source> + <translation>Жанр:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="273"/> + <source>Comment:</source> + <translation>Комментарий:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="283"/> + <source>Album:</source> + <translation>Альбом:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="293"/> + <source>Artist:</source> + <translation>Исполнитель:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="303"/> + <source>Title:</source> + <translation>Название:</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="58"/> + <source>kbps</source> + <translation>Кб/с</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="61"/> + <source>KB</source> + <translation>Кб</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Информация</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/flac/translations/translations.qrc b/src/plugins/Input/flac/translations/translations.qrc new file mode 100644 index 000000000..cd630dfce --- /dev/null +++ b/src/plugins/Input/flac/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>flac_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Input/mad/CMakeLists.txt b/src/plugins/Input/mad/CMakeLists.txt new file mode 100644 index 000000000..3e4dfa9d0 --- /dev/null +++ b/src/plugins/Input/mad/CMakeLists.txt @@ -0,0 +1,90 @@ +project(libmad) + +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}/../../../) + +# libmad and taglib +PKGCONFIG(mad MAD_INCLUDE_DIR MAD_LINK_DIR MAD_LINK_FLAGS MAD_CFLAGS) +PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS) + +IF(NOT MAD_LINK_FLAGS) + MESSAGE("Can not find mad.pc") + SET(MAD_LINK_FLAGS -lmad) +ENDIF(NOT MAD_LINK_FLAGS) + +IF(NOT TAGLIB_LINK_FLAGS) + SET(TAGLIB_LINK_FLAGS -ltag) + SET(TAGLIB_INCLUDE_DIR /usr/include/taglib) + SET(TAGLIB_CFLAGS -I/usr/include/taglib) +ENDIF(NOT TAGLIB_LINK_FLAGS) + +include_directories(${MAD_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR}) +link_directories(${MAD_LINK_DIR} ${TAGLIB_LINK_DIR}) + +#ADD_DEFINITIONS(${MAD_CFLAGS}) +ADD_DEFINITIONS(${TAGLIB_CFLAGS}) + + +SET(libmad_SRCS + decoder_mad.cpp + decodermadfactory.cpp + detailsdialog.cpp + settingsdialog.cpp +) + +SET(libmad_MOC_HDRS + settingsdialog.h + decodermadfactory.h + decoder_mad.h + detailsdialog.h +) + +SET(libmad_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libmad_RCC_SRCS ${libmad_RCCS}) + +QT4_WRAP_CPP(libmad_MOC_SRCS ${libmad_MOC_HDRS}) + +# user interface + + +SET(libmad_UIS + detailsdialog.ui + settingsdialog.ui +) + +QT4_WRAP_UI(libmad_UIS_H ${libmad_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(mad SHARED ${libmad_SRCS} ${libmad_MOC_SRCS} ${libmad_UIS_H} + ${libmad_RCC_SRCS}) + +target_link_libraries(mad ${QT_LIBRARIES} -lqmmp ${MAD_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS}) +install(TARGETS mad DESTINATION ${LIB_DIR}/qmmp/Input PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) diff --git a/src/plugins/Input/mad/decoder_mad.cpp b/src/plugins/Input/mad/decoder_mad.cpp new file mode 100644 index 000000000..6b4c2569a --- /dev/null +++ b/src/plugins/Input/mad/decoder_mad.cpp @@ -0,0 +1,572 @@ +#include <QtGui> + +#include "decoder_mad.h" +#include "constants.h" +#include "buffer.h" +#include "output.h" + +#include <math.h> +#include <stdio.h> + +# define XING_MAGIC (('X' << 24) | ('i' << 16) | ('n' << 8) | 'g') + + +DecoderMAD::DecoderMAD(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + inited = false; + user_stop = false; + done = false; + finish = false; + derror = false; + eof = false; + useeq = false; + totalTime = 0.; + seekTime = -1.; + channels = 0; + bks = 0; + bitrate = 0; + freq = 0; + len = 0; + input_buf = 0; + input_bytes = 0; + output_buf = 0; + output_bytes = 0; + output_at = 0; + output_size = 0; +} + +DecoderMAD::~DecoderMAD() +{ + wait(); + deinit(); + mutex()->lock(); + if (input_buf) + { + qDebug("DecoderMAD: deleting input_buf"); + delete [] input_buf; + } + input_buf = 0; + + if (output_buf) + { + qDebug("DecoderMAD: deleting output_buf"); + delete [] output_buf; + } + output_buf = 0; + mutex()->unlock(); +} + +bool DecoderMAD::initialize() +{ + bks = blockSize(); + + inited = false; + user_stop = false; + done = false; + finish = false; + derror = false; + eof = false; + totalTime = 0.; + seekTime = -1.; + channels = 0; + bitrate = 0; + freq = 0; + len = 0; + input_bytes = 0; + output_bytes = 0; + output_at = 0; + output_size = 0; + + if (! input()) + { + error("DecoderMAD: cannot initialize. No input."); + return FALSE; + } + + if (! input_buf) + input_buf = new char[globalBufferSize]; + + if (! output_buf) + output_buf = new char[globalBufferSize]; + + if (! input()->isOpen()) + { + if (! input()->open(QIODevice::ReadOnly)) + { + error("DecoderMAD: Failed to open input. Error " + + QString::number(input()->isOpen()) + "."); + return FALSE; + } + } + + mad_stream_init(&stream); + mad_frame_init(&frame); + mad_synth_init(&synth); + + if (! findHeader()) + { + qDebug("DecoderMAD: Cannot find a valid MPEG header."); + return FALSE; + } + configure(freq, channels, 16, bitrate); + + inited = TRUE; + return TRUE; +} + + +void DecoderMAD::deinit() +{ + if(!inited) + return; + + mad_synth_finish(&synth); + mad_frame_finish(&frame); + mad_stream_finish(&stream); + + inited = false; + user_stop = false; + done = false; + finish = false; + derror = false; + eof = false; + useeq = false; + totalTime = 0.; + seekTime = -1.; + channels = 0; + bks = 0; + bitrate = 0; + freq = 0; + len = 0; + input_bytes = 0; + output_bytes = 0; + output_at = 0; + output_size = 0; +} + +bool DecoderMAD::findXingHeader(struct mad_bitptr ptr, unsigned int bitlen) +{ + if (bitlen < 64 || mad_bit_read(&ptr, 32) != XING_MAGIC) + goto fail; + + xing.flags = mad_bit_read(&ptr, 32); + bitlen -= 64; + + if (xing.flags & XING_FRAMES) + { + if (bitlen < 32) + goto fail; + + xing.frames = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing.flags & XING_BYTES) + { + if (bitlen < 32) + goto fail; + + xing.bytes = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + if (xing.flags & XING_TOC) + { + int i; + + if (bitlen < 800) + goto fail; + + for (i = 0; i < 100; ++i) + xing.toc[i] = mad_bit_read(&ptr, 8); + + bitlen -= 800; + } + + if (xing.flags & XING_SCALE) + { + if (bitlen < 32) + goto fail; + + xing.scale = mad_bit_read(&ptr, 32); + bitlen -= 32; + } + + return true; + +fail: + xing.flags = 0; + xing.frames = 0; + xing.bytes = 0; + xing.scale = 0; + return false; +} + +bool DecoderMAD::findHeader() +{ + bool result = false; + int count = 0; + + while (1) + { + if (input_bytes < globalBufferSize) + { + int bytes = input()->read(input_buf + input_bytes, + globalBufferSize - input_bytes); + if (bytes <= 0) + { + if (bytes == -1) + result = false; + ; + break; + } + input_bytes += bytes; + } + + mad_stream_buffer(&stream, (unsigned char *) input_buf, input_bytes); + + bool done = false; + while (! done) + { + if (mad_frame_decode(&frame, &stream) != -1) + done = true; + else if (!MAD_RECOVERABLE(stream.error)) + { + qWarning("DecoderMAD: Can't decode frame"); + break; + } + + count++; + } + + findXingHeader(stream.anc_ptr, stream.anc_bitlen); + result = done; + if ((stream.error != MAD_ERROR_BUFLEN)) + break; + + input_bytes = &input_buf[input_bytes] - (char *) stream.next_frame; + memmove(input_buf, stream.next_frame, input_bytes); + } + + if (result && count) + { + freq = frame.header.samplerate; + channels = MAD_NCHANNELS(&frame.header); + bitrate = frame.header.bitrate / 1000; + calcLength(&frame.header); + } + + return result; +} + +void DecoderMAD::calcLength(struct mad_header *header) +{ + if (! input() || input()->isSequential()) + return; + + totalTime = 0.; + if (xing.flags & XING_FRAMES) + { + mad_timer_t timer; + + timer = header->duration; + mad_timer_multiply(&timer, xing.frames); + + totalTime = double(mad_timer_count(timer, MAD_UNITS_MILLISECONDS)) / 1000.; + } + else if (header->bitrate > 0) + totalTime = input()->size() * 8 / header->bitrate; +} + +double DecoderMAD::lengthInSeconds() +{ + if (! inited) + return 0.; + return totalTime; +} + +void DecoderMAD::seek(double pos) +{ + seekTime = pos; +} + +void DecoderMAD::stop() +{ + user_stop = TRUE; +} + +void DecoderMAD::flush(bool final) +{ + ulong min = final ? 0 : bks; + + while ((! done && ! finish) && output_bytes > min) + { + output()->recycler()->mutex()->lock(); + + while ((! done && ! finish) && output()->recycler()->full()) + { + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock(); + done = user_stop; + } + + if (user_stop || finish) + { + inited = FALSE; + done = TRUE; + } + else + { + output_bytes -= produceSound(output_buf, output_bytes, bitrate, channels); + output_size += bks; + output_at = output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + +void DecoderMAD::run() +{ + mutex()->lock(); + + if (! inited) + { + mutex()->unlock(); + return; + } + + DecoderState::Type stat = DecoderState::Decoding; + + mutex()->unlock(); + + dispatch(stat); + + while (! done && ! finish && ! derror) + { + mutex()->lock(); + + if (seekTime >= 0.0) + { + long seek_pos = long(seekTime * input()->size() / totalTime); + input()->seek(seek_pos); + output_size = long(seekTime) * long(freq * channels * 16 / 2); + input_bytes = 0; + output_at = 0; + output_bytes = 0; + eof = false; + } + + finish = eof; + + if (! eof) + { + if (stream.next_frame && seekTime == -1.) + { + input_bytes = &input_buf[input_bytes] - (char *) stream.next_frame; + memmove(input_buf, stream.next_frame, input_bytes); + } + + if (input_bytes < globalBufferSize) + { + int len = input()->read((char *) input_buf + input_bytes, + globalBufferSize - input_bytes); + + if (len == 0) + { + eof = true; + } + else if (len < 0) + { + derror = true; + break; + } + + input_bytes += len; + } + + mad_stream_buffer(&stream, (unsigned char *) input_buf, input_bytes); + } + + seekTime = -1.; + + mutex()->unlock(); + + // decode + while (! done && ! finish && ! derror) + { + if (mad_frame_decode(&frame, &stream) == -1) + { + if (stream.error == MAD_ERROR_BUFLEN) + break; + + // error in decoding + if (! MAD_RECOVERABLE(stream.error)) + { + derror = true; + break; + } + continue; + } + + mutex()->lock(); + + if (seekTime >= 0.) + { + mutex()->unlock(); + break; + } + + if (useeq) + { + unsigned int nch, ch, ns, s, sb; + + nch = MAD_NCHANNELS(&frame.header); + ns = MAD_NSBSAMPLES(&frame.header); + + for (ch = 0; ch < nch; ++ch) + for (s = 0; s < ns; ++s) + for (sb = 0; sb < 32; ++sb) + frame.sbsample[ch][s][sb] = + mad_f_mul(frame.sbsample[ch][s][sb], eqbands[sb]); + } + + mad_synth_frame(&synth, &frame); + madOutput(); + mutex()->unlock(); + } + } + + mutex()->lock(); + + if (! user_stop && eof) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock(); + // end of stream + while (! output()->recycler()->empty() && ! user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock(); + } + output()->recycler()->mutex()->unlock(); + } + + done = TRUE; + if (! user_stop) + finish = TRUE; + } + + if (finish) + stat = DecoderState::Finished; + else if (user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + dispatch(stat); + + if (input()) + input()->close(); + deinit(); + +} + +static inline signed int scale(mad_fixed_t sample) +{ + /* round */ + sample += (1L << (MAD_F_FRACBITS - 16)); + + /* clip */ + if (sample >= MAD_F_ONE) + sample = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + sample = -MAD_F_ONE; + + /* quantize */ + return sample >> (MAD_F_FRACBITS + 1 - 16); +} + +static inline signed long fix_sample(unsigned int bits, mad_fixed_t sample) +{ + mad_fixed_t quantized, check; + // clip + quantized = sample; + check = (sample >> MAD_F_FRACBITS) + 1; + if (check & ~1) + { + if (sample >= MAD_F_ONE) + quantized = MAD_F_ONE - 1; + else if (sample < -MAD_F_ONE) + quantized = -MAD_F_ONE; + } + // quantize + quantized &= ~((1L << (MAD_F_FRACBITS + 1 - bits)) - 1); + // scale + return quantized >> (MAD_F_FRACBITS + 1 - bits); +} + +enum mad_flow DecoderMAD::madOutput() +{ + unsigned int samples, channels; + mad_fixed_t const *left, *right; + + samples = synth.pcm.length; + channels = synth.pcm.channels; + left = synth.pcm.samples[0]; + right = synth.pcm.samples[1]; + + + bitrate = frame.header.bitrate / 1000; + done = user_stop; + + while (samples-- && !user_stop) + { + signed int sample; + + if (output_bytes + 4096 > globalBufferSize) + flush(); + + sample = fix_sample(16, *left++); + *(output_buf + output_at++) = ((sample >> 0) & 0xff); + *(output_buf + output_at++) = ((sample >> 8) & 0xff); + output_bytes += 2; + + if (channels == 2) + { + sample = fix_sample(16, *right++); + *(output_buf + output_at++) = ((sample >> 0) & 0xff); + *(output_buf + output_at++) = ((sample >> 8) & 0xff); + output_bytes += 2; + } + } + + if (done || finish) + { + return MAD_FLOW_STOP; + } + + return MAD_FLOW_CONTINUE; +} + +enum mad_flow DecoderMAD::madError(struct mad_stream *stream, + struct mad_frame *) +{ + if (MAD_RECOVERABLE(stream->error)) + return MAD_FLOW_CONTINUE; + qFatal("MADERROR!\n"); + return MAD_FLOW_STOP; +} diff --git a/src/plugins/Input/mad/decoder_mad.h b/src/plugins/Input/mad/decoder_mad.h new file mode 100644 index 000000000..ecbb160cb --- /dev/null +++ b/src/plugins/Input/mad/decoder_mad.h @@ -0,0 +1,95 @@ +// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com> +// +// Use, modification and distribution is allowed without limitation, +// warranty, or liability of any kind. +// + +#ifndef __decoder_mad_h +#define __decoder_mad_h + +class DecoderMAD; + +#include "decoder.h" +#include "decodermadfactory.h" + +extern "C" { +#include <mad.h> +} + + +class DecoderMAD : public Decoder +{ +public: + DecoderMAD(QObject *parent = 0, DecoderFactory *d = 0, + QIODevice *i = 0, Output *o = 0); + virtual ~DecoderMAD(); + + // standard decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + // Equalizer + //bool isEQSupported() const { return TRUE; } + //void setEQEnabled(bool); + //void setEQ(const EqPreset &); + + static const int maxDecodeRetries; + static const int maxFrameSize; + static const int maxFrameCheck; + static const int initialFrameSize; + + +private: + // thread run function + void run(); + + enum mad_flow madOutput(); + enum mad_flow madError(struct mad_stream *, struct mad_frame *); + + // helper functions + void flush(bool = FALSE); + void deinit(); + bool findHeader(); + bool findXingHeader(struct mad_bitptr, unsigned int); + void calcLength(struct mad_header *); + + bool inited, user_stop, done, finish, derror, eof, useeq; + double totalTime, seekTime; + int channels; + long bitrate, freq, len; + unsigned int bks; + mad_fixed_t eqbands[32]; + + // file input buffer + char *input_buf; + unsigned long input_bytes; + + // output buffer + char *output_buf; + unsigned long output_bytes, output_at, output_size; + + // MAD decoder + struct { + int flags; + unsigned long frames; + unsigned long bytes; + unsigned char toc[100]; + long scale; + } xing; + + enum { + XING_FRAMES = 0x0001, + XING_BYTES = 0x0002, + XING_TOC = 0x0004, + XING_SCALE = 0x0008 + }; + + struct mad_stream stream; + struct mad_frame frame; + struct mad_synth synth; +}; + + +#endif // __decoder_mad_h diff --git a/src/plugins/Input/mad/decodermadfactory.cpp b/src/plugins/Input/mad/decodermadfactory.cpp new file mode 100644 index 000000000..8bb04aca1 --- /dev/null +++ b/src/plugins/Input/mad/decodermadfactory.cpp @@ -0,0 +1,188 @@ +#include <QtGui> +#include <QDialog> +#include <QMessageBox> +#include <mad.h> +#include <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/id3v1tag.h> +#include <taglib/id3v2tag.h> +#include <taglib/apetag.h> +#include <taglib/tfile.h> +#include <taglib/mpegfile.h> + +#include "detailsdialog.h" +#include "settingsdialog.h" +#include "decoder_mad.h" +#include "decodermadfactory.h" + +// DecoderMADFactory + +bool DecoderMADFactory::supports(const QString &source) const +{ + QString ext = source.right(4).toLower(); + return ext == ".mp1" || ext == ".mp2" || ext == ".mp3"; +} + +bool DecoderMADFactory::canDecode(QIODevice *input) const +{ + char buf[16 * 512]; + + if (input->peek(buf,sizeof(buf)) == sizeof(buf)) + { + struct mad_stream stream; + struct mad_header header; + int dec_res; + + mad_stream_init (&stream); + mad_header_init (&header); + mad_stream_buffer (&stream, (unsigned char *) buf, sizeof(buf)); + stream.error = MAD_ERROR_NONE; + + while ((dec_res = mad_header_decode(&header, &stream)) == -1 + && MAD_RECOVERABLE(stream.error)) + ; + return dec_res != -1 ? TRUE: FALSE; + } + return FALSE; +} + +const DecoderProperties DecoderMADFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("MPEG Plugin"); + properties.filter = "*.mp1 *.mp2 *.mp3"; + properties.description = tr("MPEG Files"); + properties.contentType = "audio/mp3;audio/mpeg"; + properties.hasAbout = TRUE; + properties.hasSettings = TRUE; + return properties; +} + +Decoder *DecoderMADFactory::create(QObject *parent, QIODevice *input, Output *output) +{ + return new DecoderMAD(parent, this, input, output); +} + +FileTag *DecoderMADFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + TagLib::Tag *tag = 0; + TagLib::MPEG::File fileRef(source.toLocal8Bit ()); + + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("MAD"); + QTextCodec *codec_v1 = + QTextCodec::codecForName(settings.value("ID3v1_encoding","UTF-8" ) + .toByteArray ()); + QTextCodec *codec_v2 = + QTextCodec::codecForName(settings.value("ID3v2_encoding","UTF-8" ) + .toByteArray ()); + if (!codec_v1) + codec_v1 = QTextCodec::codecForName ("UTF-8"); + if (!codec_v2) + codec_v2 = QTextCodec::codecForName ("UTF-8"); + + QTextCodec *codec = 0; + + uint tag_array[3]; + tag_array[0] = settings.value("tag_1", SettingsDialog::ID3v2).toInt(); + tag_array[1] = settings.value("tag_2", SettingsDialog::Disabled).toInt(); + tag_array[2] = settings.value("tag_3", SettingsDialog::Disabled).toInt(); + + + for (int i = 0; i < 3; ++i) + { + switch ((uint) tag_array[i]) + { + case SettingsDialog::ID3v1: + { + codec = codec_v1; + tag = fileRef.ID3v1Tag(); + break; + } + case SettingsDialog::ID3v2: + { + codec = codec_v2; + tag = fileRef.ID3v2Tag(); + break; + } + case SettingsDialog::APE: + { + codec = QTextCodec::codecForName ("UTF-8"); + tag = fileRef.APETag(); + break; + } + case SettingsDialog::Disabled: + { + break; + } + } + if(tag && !tag->isEmpty()) + break; + } + settings.endGroup(); + + if (tag && codec) + { + bool utf = codec->name ().contains("UTF"); + TagLib::String album = tag->album(); + TagLib::String artist = tag->artist(); + TagLib::String comment = tag->comment(); + TagLib::String genre = tag->genre(); + TagLib::String title = tag->title(); + + ftag->setValue(FileTag::ALBUM, + codec->toUnicode(album.toCString(utf)).trimmed()); + ftag->setValue(FileTag::ARTIST, + codec->toUnicode(artist.toCString(utf)).trimmed()); + ftag->setValue(FileTag::COMMENT, + codec->toUnicode(comment.toCString(utf)).trimmed()); + ftag->setValue(FileTag::GENRE, + codec->toUnicode(genre.toCString(utf)).trimmed()); + ftag->setValue(FileTag::TITLE, + codec->toUnicode(title.toCString(utf)).trimmed()); + ftag->setValue(FileTag::YEAR, + tag->year()); + ftag->setValue(FileTag::TRACK, + tag->track()); + } + if (fileRef.audioProperties()) + ftag->setValue(FileTag::LENGTH,fileRef.audioProperties()->length()); + return ftag; +} + +QObject* DecoderMADFactory::showDetails(QWidget *parent, const QString &path) +{ + DetailsDialog *d = new DetailsDialog(parent, path); + d -> show(); + return d; +} + +void DecoderMADFactory::showSettings(QWidget *parent) +{ + SettingsDialog *s = new SettingsDialog(parent); + s -> show(); +} + +void DecoderMADFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About MPEG Audio Plugin"), + tr("Qmmp MPEG Audio Plugin")+"\n"+ + tr("Compiled against libmad version:")+" "+ + QString("%1.%2.%3%4").arg(MAD_VERSION_MAJOR) + .arg(MAD_VERSION_MINOR) + .arg(MAD_VERSION_PATCH).arg(MAD_VERSION_EXTRA)+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")+"\n"+ + tr("Source code based on mq3 progect") + ); +} + +QTranslator *DecoderMADFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/mad_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderMADFactory) diff --git a/src/plugins/Input/mad/decodermadfactory.h b/src/plugins/Input/mad/decodermadfactory.h new file mode 100644 index 000000000..2c7da8e47 --- /dev/null +++ b/src/plugins/Input/mad/decodermadfactory.h @@ -0,0 +1,53 @@ +/*************************************************************************** + * 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 DECODERMADFACTORY_H +#define DECODERMADFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> + + + + +class DecoderMADFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/mad/detailsdialog.cpp b/src/plugins/Input/mad/detailsdialog.cpp new file mode 100644 index 000000000..af9b466cd --- /dev/null +++ b/src/plugins/Input/mad/detailsdialog.cpp @@ -0,0 +1,294 @@ +/*************************************************************************** + * 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 <QTextCodec> +#include <QSettings> +#include <QDir> +#include <QFile> +#include <QFileInfo> + +#include <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/id3v1tag.h> +#include <taglib/id3v2tag.h> +#include <taglib/apetag.h> +#include <taglib/tfile.h> +#include <taglib/mpegfile.h> +#include <taglib/mpegheader.h> +#include <taglib/mpegproperties.h> + +#include "detailsdialog.h" + +DetailsDialog::DetailsDialog(QWidget *parent, const QString &path) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_path = path; + setWindowTitle (path.section('/',-1)); + ui.pathLineEdit->setText(m_path); + + if (!QFile::exists(m_path)) + return; + + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("MAD"); + m_codec_v1 = + QTextCodec::codecForName(settings.value("ID3v1_encoding","UTF-8" ) + .toByteArray ()); + m_codec_v2 = + QTextCodec::codecForName(settings.value("ID3v2_encoding","UTF-8" ) + .toByteArray ()); + if (!m_codec_v1) + m_codec_v1 = QTextCodec::codecForName ("UTF-8"); + if (!m_codec_v2) + m_codec_v2 = QTextCodec::codecForName ("UTF-8"); + + QString tag_name = settings.value("current_tag","ID3v2").toString (); + + if (tag_name == "ID3v1") + ui.id3v1RadioButton->setChecked(TRUE); + else if (tag_name == "ID3v2") + ui.id3v2RadioButton->setChecked(TRUE); + else if (tag_name == "APE") + ui.apeRadioButton->setChecked(TRUE); + else + ui.id3v2RadioButton->setChecked(TRUE); + + settings.endGroup(); + + loadMPEGInfo(); + QFileInfo info(m_path); + m_rw = info.isWritable(); + loadTag(); + connect(ui.saveButton, SIGNAL(clicked()), SLOT(save())); + connect(ui.createButton, SIGNAL(clicked()), SLOT(create())); + connect(ui.deleteButton, SIGNAL(clicked()), SLOT(deleteTag())); + connect(ui.id3v1RadioButton, SIGNAL(clicked()), SLOT(loadTag())); + connect(ui.id3v2RadioButton, SIGNAL(clicked()), SLOT(loadTag())); + connect(ui.apeRadioButton, SIGNAL(clicked()), SLOT(loadTag())); +} + + +DetailsDialog::~DetailsDialog() +{} + +void DetailsDialog::loadMPEGInfo() +{ + TagLib::MPEG::File f (m_path.toLocal8Bit()); + //l.label + //ui. f.audioProperties()->level(); + QString text; + text = QString("%1").arg(f.audioProperties()->layer()); + ui.levelLabel->setText("MPEG layer "+text); //TODO: add MPEG version + text = QString("%1").arg(f.audioProperties()->bitrate()); + ui.bitRateLabel->setText(text+" "+tr("kbps")); + text = QString("%1").arg(f.audioProperties()->sampleRate()); + ui.sampleRateLabel->setText(text+" "+tr("Hz")); + switch (f.audioProperties()->channelMode()) + { + case TagLib::MPEG::Header::Stereo: + ui.modeLabel->setText("Stereo"); + break; + case TagLib::MPEG::Header::JointStereo: + ui.modeLabel->setText("Joint stereo"); + break; + case TagLib::MPEG::Header::DualChannel: + ui.modeLabel->setText("Dual channel"); + break; + case TagLib::MPEG::Header::SingleChannel: + ui.modeLabel->setText("Single channel"); + break; + } + text = QString("%1 "+tr("KB")).arg(f.length()/1024); + ui.fileSizeLabel->setText(text); + /*if (f.audioProperties()->protectionEnabled()) + ui.errProtectionLabel->setText(tr("Yes")); + else + ui.errProtectionLabel->setText(tr("No"));*/ + if (f.audioProperties()->isCopyrighted()) + ui.copyrightLabel->setText(tr("Yes")); + else + ui.copyrightLabel->setText(tr("No")); + if (f.audioProperties()->isOriginal()) + ui.originalLabel->setText(tr("Yes")); + else + ui.originalLabel->setText(tr("No")); +} + +void DetailsDialog::loadTag() +{ + TagLib::MPEG::File f (m_path.toLocal8Bit()); + QTextCodec *codec = QTextCodec::codecForName ("UTF-8"); + TagLib::Tag *tag = 0; + + if (selectedTag() == TagLib::MPEG::File::ID3v1) + { + tag = f.ID3v1Tag(); + codec = m_codec_v1; + ui.tagGroupBox->setTitle(tr("ID3v1 Tag")); + } + else if (selectedTag() == TagLib::MPEG::File::ID3v2) + { + tag = f.ID3v2Tag(); + codec = m_codec_v2; + ui.tagGroupBox->setTitle(tr("ID3v2 Tag")); + } + else if (selectedTag() == TagLib::MPEG::File::APE) + { + ui.tagGroupBox->setTitle(tr("APE Tag")); + tag = f.APETag(); + } + ui.saveButton->setEnabled(tag && m_rw); + ui.createButton->setEnabled(!tag && m_rw); + ui.deleteButton->setEnabled(tag && m_rw); + ui.tagsWidget->setEnabled(tag); + //clear old values + ui.titleLineEdit->clear(); + ui.artistLineEdit->clear(); + ui.albumLineEdit->clear(); + ui.commentLineEdit->clear(); + ui.yearLineEdit->clear(); + ui.trackLineEdit->clear(); + ui.genreLineEdit->clear(); + + if (tag) + { + bool utf = codec->name().contains("UTF"); + TagLib::String title = tag->title(); + TagLib::String artist = tag->artist(); + TagLib::String album = tag->album(); + TagLib::String comment = tag->comment(); + TagLib::String genre = tag->genre(); + QString string = codec->toUnicode(title.toCString(utf)).trimmed(); + ui.titleLineEdit->setText(string); + string = codec->toUnicode(artist.toCString(utf)).trimmed(); + ui.artistLineEdit->setText(string); + string = codec->toUnicode(album.toCString(utf)).trimmed(); + ui.albumLineEdit->setText(string); + string = codec->toUnicode(comment.toCString(utf)).trimmed(); + ui.commentLineEdit->setText(string); + string = QString("%1").arg(tag->year()); + ui.yearLineEdit->setText(string); + string = QString("%1").arg(tag->track()); + ui.trackLineEdit->setText(string); + string = codec->toUnicode(genre.toCString(utf)).trimmed(); + ui.genreLineEdit->setText(string); + } +} + +void DetailsDialog::save() +{ + TagLib::MPEG::File* f = new TagLib::MPEG::File(m_path.toLocal8Bit()); + TagLib::String::Type type = TagLib::String::Latin1; + + QTextCodec *codec = 0; + TagLib::Tag *tag = 0; + + if (selectedTag() == TagLib::MPEG::File::ID3v1) + { + codec = m_codec_v1; + tag = f->ID3v1Tag(TRUE); + if (codec->name().contains("UTF")) + { + delete f; + loadTag(); + } + } + if (selectedTag() == TagLib::MPEG::File::ID3v2) + { + codec = m_codec_v2; + tag = f->ID3v2Tag(TRUE); + if (codec->name().contains("UTF")) + { + TagLib::ID3v2::FrameFactory *factory = TagLib::ID3v2::FrameFactory::instance(); + factory->setDefaultTextEncoding(TagLib::String::UTF8); + f->setID3v2FrameFactory(factory); + type = TagLib::String::UTF8; + } + } + if (selectedTag() == TagLib::MPEG::File::APE) + { + codec = QTextCodec::codecForName ("UTF-8"); + tag = f->APETag(TRUE); + type = TagLib::String::UTF8; + } + + tag->setTitle(TagLib::String(codec->fromUnicode(ui.titleLineEdit->text()).constData(), type)); + tag->setArtist(TagLib::String(codec->fromUnicode(ui.artistLineEdit->text()).constData(), type)); + tag->setAlbum(TagLib::String(codec->fromUnicode(ui.albumLineEdit->text()).constData(), type)); + tag->setComment(TagLib::String(codec->fromUnicode(ui.commentLineEdit->text()).constData(), type)); + tag->setGenre(TagLib::String(codec->fromUnicode(ui.genreLineEdit->text()).constData(), type)); + tag->setYear(ui.yearLineEdit->text().toUInt()); + tag->setTrack(ui.trackLineEdit->text().toUInt()); + + f->save(selectedTag(), FALSE); + delete f; + loadTag(); +} + +void DetailsDialog::create() +{ + TagLib::MPEG::File *f = new TagLib::MPEG::File (m_path.toLocal8Bit()); + TagLib::Tag *tag = 0; + if (selectedTag() == TagLib::MPEG::File::ID3v1) + tag = f->ID3v1Tag(TRUE); + else if (selectedTag() == TagLib::MPEG::File::ID3v2) + tag = f->ID3v2Tag(TRUE); + else if (selectedTag() == TagLib::MPEG::File::APE) + tag = f->APETag(TRUE); + + f->save(selectedTag(), FALSE); + delete f; + loadTag(); + ui.tagsWidget->setEnabled(TRUE); + ui.saveButton->setEnabled(m_rw); +} + +void DetailsDialog::deleteTag() +{ + TagLib::MPEG::File *f = new TagLib::MPEG::File (m_path.toLocal8Bit()); + f->strip(selectedTag()); + delete f; + loadTag(); +} + +uint DetailsDialog::selectedTag() +{ + if (ui.id3v1RadioButton->isChecked()) + return TagLib::MPEG::File::ID3v1; + else if (ui.id3v2RadioButton->isChecked()) + return TagLib::MPEG::File::ID3v2; + else if (ui.apeRadioButton->isChecked()) + return TagLib::MPEG::File::APE; + return TagLib::MPEG::File::ID3v2; +} + +void DetailsDialog::closeEvent (QCloseEvent *) +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("MAD"); + if (ui.id3v1RadioButton->isChecked()) + settings.setValue("current_tag","ID3v1"); + else if (ui.id3v2RadioButton->isChecked()) + settings.setValue("current_tag","ID3v2"); + else if (ui.apeRadioButton->isChecked()) + settings.setValue("current_tag","APE"); + settings.endGroup(); +} diff --git a/src/plugins/Input/mad/detailsdialog.h b/src/plugins/Input/mad/detailsdialog.h new file mode 100644 index 000000000..bc92724a3 --- /dev/null +++ b/src/plugins/Input/mad/detailsdialog.h @@ -0,0 +1,63 @@ +/*************************************************************************** + * 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 DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include <QDialog> + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ + +class QTextCodec; + +class DetailsDialog : public QDialog +{ + Q_OBJECT +public: + DetailsDialog(QWidget *parent = 0, const QString &path = 0); + + ~DetailsDialog(); + +protected: + virtual void closeEvent (QCloseEvent *); + +private slots: + void save(); + void create(); + void deleteTag(); + void loadTag(); + +private: + void loadMPEGInfo(); + uint selectedTag(); + //void loadTag(); + //void loadID3v2Tag(); + Ui::DetailsDialog ui; + QString m_path; + QTextCodec *m_codec_v1; + QTextCodec *m_codec_v2; + bool m_rw; + +}; + +#endif diff --git a/src/plugins/Input/mad/detailsdialog.ui b/src/plugins/Input/mad/detailsdialog.ui new file mode 100644 index 000000000..6f9a00c31 --- /dev/null +++ b/src/plugins/Input/mad/detailsdialog.ui @@ -0,0 +1,392 @@ +<ui version="4.0" > + <class>DetailsDialog</class> + <widget class="QDialog" name="DetailsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>593</width> + <height>402</height> + </rect> + </property> + <property name="windowTitle" > + <string>Details</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="3" > + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_28" > + <property name="text" > + <string>File path:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="pathLineEdit" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item rowspan="2" row="1" column="0" > + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="groupBox_3" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Tag Choice</string> + </property> + <layout class="QHBoxLayout" > + <item> + <widget class="QRadioButton" name="id3v1RadioButton" > + <property name="text" > + <string>ID3v1</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="id3v2RadioButton" > + <property name="text" > + <string>ID3v2</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="apeRadioButton" > + <property name="text" > + <string>APE</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="minimumSize" > + <size> + <width>200</width> + <height>16</height> + </size> + </property> + <property name="title" > + <string>MPEG Info</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>MPEG level:</string> + </property> + <property name="textFormat" > + <enum>Qt::AutoText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="levelLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Bit rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="bitRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Sample rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="sampleRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="fileSizeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_6" > + <property name="text" > + <string>Mode:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLabel" name="modeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_8" > + <property name="text" > + <string>Copyright:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1" > + <widget class="QLabel" name="copyrightLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="6" column="0" > + <widget class="QLabel" name="label_9" > + <property name="text" > + <string>Original:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="6" column="1" > + <widget class="QLabel" name="originalLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </item> + <item row="1" column="1" colspan="2" > + <widget class="QGroupBox" name="tagGroupBox" > + <property name="title" > + <string>ID3v1 Tag</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QWidget" native="1" name="tagsWidget" > + <property name="enabled" > + <bool>true</bool> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_21" > + <property name="text" > + <string>Title:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="titleLineEdit" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_22" > + <property name="text" > + <string>Artist:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLineEdit" name="artistLineEdit" /> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_23" > + <property name="text" > + <string>Album:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLineEdit" name="albumLineEdit" /> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_24" > + <property name="text" > + <string>Comment:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLineEdit" name="commentLineEdit" /> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_25" > + <property name="text" > + <string>Year:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="yearLineEdit" /> + </item> + <item row="4" column="2" > + <widget class="QLabel" name="label_26" > + <property name="text" > + <string>Track number:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="3" > + <widget class="QLineEdit" name="trackLineEdit" /> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_27" > + <property name="text" > + <string>Genre:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1" colspan="3" > + <widget class="QLineEdit" name="genreLineEdit" /> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QPushButton" name="createButton" > + <property name="text" > + <string>Create</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="deleteButton" > + <property name="text" > + <string>Delete</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="saveButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Save</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="2" column="1" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="2" > + <widget class="QPushButton" name="pushButton_3" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_3</sender> + <signal>clicked()</signal> + <receiver>DetailsDialog</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>623</x> + <y>353</y> + </hint> + <hint type="destinationlabel" > + <x>539</x> + <y>352</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/mad/mad.pro b/src/plugins/Input/mad/mad.pro new file mode 100644 index 000000000..7a320b370 --- /dev/null +++ b/src/plugins/Input/mad/mad.pro @@ -0,0 +1,39 @@ +# ???? ?????? ? KDevelop ?????????? qmake. +# ------------------------------------------- +# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/mad +# ???? - ??????????: + +include(../../plugins.pri) + + +FORMS += detailsdialog.ui \ + settingsdialog.ui +HEADERS += decodermadfactory.h \ + decoder_mad.h \ + detailsdialog.h \ + settingsdialog.h +SOURCES += decoder_mad.cpp \ + decodermadfactory.cpp \ + detailsdialog.cpp \ + settingsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Input/mad +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libmad.so + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp +PKGCONFIG += taglib mad +#TRANSLATIONS = translations/mad_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target diff --git a/src/plugins/Input/mad/settingsdialog.cpp b/src/plugins/Input/mad/settingsdialog.cpp new file mode 100644 index 000000000..667e8598a --- /dev/null +++ b/src/plugins/Input/mad/settingsdialog.cpp @@ -0,0 +1,107 @@ +/*************************************************************************** + * 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 <QTextCodec> +#include <QSettings> +#include <QDir> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + findCodecs(); + foreach (QTextCodec *codec, codecs) + { + ui.id3v1EncComboBox->addItem(codec->name()); + ui.id3v2EncComboBox->addItem(codec->name()); + } + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("MAD"); + int pos = ui.id3v1EncComboBox->findText + (settings.value("ID3v1_encoding","UTF-8").toString()); + ui.id3v1EncComboBox->setCurrentIndex(pos); + pos = ui.id3v2EncComboBox->findText + (settings.value("ID3v2_encoding","UTF-8").toString()); + ui.id3v2EncComboBox->setCurrentIndex(pos); + + ui.firstTagComboBox->setCurrentIndex(settings.value("tag_1", ID3v2).toInt()); + ui.secondTagComboBox->setCurrentIndex(settings.value("tag_2", Disabled).toInt()); + ui.thirdTagComboBox->setCurrentIndex(settings.value("tag_3", Disabled).toInt()); + + settings.endGroup(); + connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings())); +} + + +SettingsDialog::~SettingsDialog() +{} + +void SettingsDialog::writeSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("MAD"); + settings.setValue("ID3v1_encoding", ui.id3v1EncComboBox->currentText()); + settings.setValue("ID3v2_encoding", ui.id3v2EncComboBox->currentText()); + settings.setValue("tag_1", ui.firstTagComboBox->currentIndex()); + settings.setValue("tag_2", ui.secondTagComboBox->currentIndex()); + settings.setValue("tag_3", ui.thirdTagComboBox->currentIndex()); + settings.endGroup(); + accept(); +} + +void SettingsDialog::findCodecs() +{ + QMap<QString, QTextCodec *> codecMap; + QRegExp iso8859RegExp("ISO[- ]8859-([0-9]+).*"); + + foreach (int mib, QTextCodec::availableMibs()) + { + QTextCodec *codec = QTextCodec::codecForMib(mib); + + QString sortKey = codec->name().toUpper(); + int rank; + + if (sortKey.startsWith("UTF-8")) + { + rank = 1; + } + else if (sortKey.startsWith("UTF-16")) + { + rank = 2; + } + else if (iso8859RegExp.exactMatch(sortKey)) + { + if (iso8859RegExp.cap(1).size() == 1) + rank = 3; + else + rank = 4; + } + else + { + rank = 5; + } + sortKey.prepend(QChar('0' + rank)); + + codecMap.insert(sortKey, codec); + } + codecs = codecMap.values(); +} diff --git a/src/plugins/Input/mad/settingsdialog.h b/src/plugins/Input/mad/settingsdialog.h new file mode 100644 index 000000000..2ad1ed188 --- /dev/null +++ b/src/plugins/Input/mad/settingsdialog.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * 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> + + +#include "ui_settingsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class SettingsDialog : public QDialog +{ +Q_OBJECT +public: + SettingsDialog(QWidget *parent = 0); + + ~SettingsDialog(); + + enum TagType {ID3v1 = 0, ID3v2, APE, Disabled}; + +private slots: + void writeSettings(); + +private: + void findCodecs(); + + Ui::SettingsDialog ui; + QList<QTextCodec *> codecs; + +}; + +#endif diff --git a/src/plugins/Input/mad/settingsdialog.ui b/src/plugins/Input/mad/settingsdialog.ui new file mode 100644 index 000000000..3ace8fcec --- /dev/null +++ b/src/plugins/Input/mad/settingsdialog.ui @@ -0,0 +1,306 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>242</width> + <height>303</height> + </rect> + </property> + <property name="windowTitle" > + <string>MPEG Plugin Settings</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Tag Priority</string> + </property> + <layout class="QVBoxLayout" > + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_15_2" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>First:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="firstTagComboBox" > + <property name="currentIndex" > + <number>0</number> + </property> + <item> + <property name="text" > + <string>ID3v1</string> + </property> + </item> + <item> + <property name="text" > + <string>ID3v2</string> + </property> + </item> + <item> + <property name="text" > + <string>APE</string> + </property> + </item> + <item> + <property name="text" > + <string>Disabled</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_15_3" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Second:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="secondTagComboBox" > + <property name="currentIndex" > + <number>0</number> + </property> + <item> + <property name="text" > + <string>ID3v1</string> + </property> + </item> + <item> + <property name="text" > + <string>ID3v2</string> + </property> + </item> + <item> + <property name="text" > + <string>APE</string> + </property> + </item> + <item> + <property name="text" > + <string>Disabled</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QLabel" name="label_15_4" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Third:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="thirdTagComboBox" > + <property name="currentIndex" > + <number>0</number> + </property> + <item> + <property name="text" > + <string>ID3v1</string> + </property> + </item> + <item> + <property name="text" > + <string>ID3v2</string> + </property> + </item> + <item> + <property name="text" > + <string>APE</string> + </property> + </item> + <item> + <property name="text" > + <string>Disabled</string> + </property> + </item> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Encodings</string> + </property> + <layout class="QVBoxLayout" > + <item> + <layout class="QHBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_17_2_2" > + <property name="text" > + <string>ID3v1 encoding:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="id3v1EncComboBox" /> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_18_2_2" > + <property name="text" > + <string>ID3v2 encoding:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="id3v2EncComboBox" /> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>131</width> + <height>31</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="okButton" > + <property name="text" > + <string>OK</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="cancelButton" > + <property name="text" > + <string>Cancel</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>336</x> + <y>210</y> + </hint> + <hint type="destinationlabel" > + <x>179</x> + <y>224</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/mad/translations/mad_plugin_ru.qm b/src/plugins/Input/mad/translations/mad_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..16bea746b --- /dev/null +++ b/src/plugins/Input/mad/translations/mad_plugin_ru.qm diff --git a/src/plugins/Input/mad/translations/mad_plugin_ru.ts b/src/plugins/Input/mad/translations/mad_plugin_ru.ts new file mode 100644 index 000000000..6406f6045 --- /dev/null +++ b/src/plugins/Input/mad/translations/mad_plugin_ru.ts @@ -0,0 +1,242 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>DecoderMADFactory</name> + <message> + <location filename="../decodermadfactory.cpp" line="27"/> + <source>MPEG Plugin</source> + <translation>Модуль MPEG</translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="39"/> + <source>MPEG Files</source> + <translation>Файлы MPEG</translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="68"/> + <source>About MPEG Audio Plugin</source> + <translation>Об аудио-модуле MPEG</translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="69"/> + <source>Qmmp MPEG Audio Plugin</source> + <translation>Аудио-модуль MPEG для Qmmp</translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="70"/> + <source>Compiled against libmad version:</source> + <translation>Собрано с версией libmad:</translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="74"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></translation> + </message> + <message> + <location filename="../decodermadfactory.cpp" line="76"/> + <source>Source code based on mq3 progect</source> + <translation>Исходный код основан на проекте mq3</translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="79"/> + <source>Hz</source> + <translation>Гц</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="106"/> + <source>Yes</source> + <translation>Есть</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="108"/> + <source>No</source> + <translation>Нет</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="192"/> + <source>ID3v1 Tag</source> + <translation>ID3v1-тег</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="442"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="351"/> + <source>Track number:</source> + <translation>Номер трека:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="364"/> + <source>Year:</source> + <translation>Год:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="374"/> + <source>Genre:</source> + <translation>Жанр:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="384"/> + <source>Comment:</source> + <translation>Комментарий:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="394"/> + <source>Album:</source> + <translation>Альбом:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="404"/> + <source>Artist:</source> + <translation>Исполнитель:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="414"/> + <source>Title:</source> + <translation>Название:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="321"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="336"/> + <source>ID3v2 Tag</source> + <translation>ID3v2-тег</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>MPEG Info</source> + <translation>Информация MPEG</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="164"/> + <source>-</source> + <translation></translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="171"/> + <source>Original:</source> + <translation>Оригинальный:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="147"/> + <source>Copyright:</source> + <translation>Авторские права:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="137"/> + <source>Mode:</source> + <translation>Режим:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="59"/> + <source>File size:</source> + <translation>Размер файла:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="76"/> + <source>Sample rate:</source> + <translation>Дискретизация:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="86"/> + <source>Bit rate:</source> + <translation>Битовая частота:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="117"/> + <source>MPEG level:</source> + <translation>Уровень MPEG:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="452"/> + <source>File path:</source> + <translation>Путь к файлу:</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="95"/> + <source>KB</source> + <translation>Кб</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Информация</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="77"/> + <source>kbps</source> + <translation>Кб/с</translation> + </message> +</context> +<context> + <name>SettingsDialog</name> + <message> + <location filename="../settingsdialog.ui" line="25"/> + <source>ID3 Tags</source> + <translation>ID3-теги</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="45"/> + <source>Default tag version:</source> + <translation>Версия тегов по умолчанию:</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="62"/> + <source>ID3v1</source> + <translation></translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="67"/> + <source>ID3v2</source> + <translation></translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="90"/> + <source>Enable ID3v1</source> + <translation>Использовать ID3v1</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="97"/> + <source>Enable ID3v2</source> + <translation>Использовать ID3v2</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="112"/> + <source>ID3v1 encoding:</source> + <translation>Кодировка ID3v1:</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="165"/> + <source>Default</source> + <translation>По-умолчанию</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="154"/> + <source>ID3v2 encoding:</source> + <translation>Кодировка ID3v2:</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="212"/> + <source>OK</source> + <translation>Применить</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="219"/> + <source>Cancel</source> + <translation>Отмена</translation> + </message> + <message> + <location filename="../settingsdialog.ui" line="13"/> + <source>MPEG Plugin Settings</source> + <translation>Настройка модуля MPEG</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/mad/translations/translations.qrc b/src/plugins/Input/mad/translations/translations.qrc new file mode 100644 index 000000000..34dbabbca --- /dev/null +++ b/src/plugins/Input/mad/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>mad_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Input/mad/ui_detailsdialog.h b/src/plugins/Input/mad/ui_detailsdialog.h new file mode 100644 index 000000000..d630be894 --- /dev/null +++ b/src/plugins/Input/mad/ui_detailsdialog.h @@ -0,0 +1,400 @@ +/******************************************************************************** +** Form generated from reading ui file 'detailsdialog.ui' +** +** Created: Thu Feb 7 00:21:43 2008 +** by: Qt User Interface Compiler version 4.3.0 +** +** WARNING! All changes made in this file will be lost when recompiling ui file! +********************************************************************************/ + +#ifndef UI_DETAILSDIALOG_H +#define UI_DETAILSDIALOG_H + +#include <QtCore/QVariant> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QButtonGroup> +#include <QtGui/QDialog> +#include <QtGui/QGridLayout> +#include <QtGui/QGroupBox> +#include <QtGui/QHBoxLayout> +#include <QtGui/QLabel> +#include <QtGui/QLineEdit> +#include <QtGui/QPushButton> +#include <QtGui/QRadioButton> +#include <QtGui/QSpacerItem> +#include <QtGui/QVBoxLayout> +#include <QtGui/QWidget> + +class Ui_DetailsDialog +{ +public: + QGridLayout *gridLayout; + QHBoxLayout *hboxLayout; + QLabel *label_28; + QLineEdit *pathLineEdit; + QVBoxLayout *vboxLayout; + QGroupBox *groupBox_3; + QHBoxLayout *hboxLayout1; + QRadioButton *id3v1RadioButton; + QRadioButton *id3v2RadioButton; + QRadioButton *apeRadioButton; + QGroupBox *groupBox; + QGridLayout *gridLayout1; + QLabel *label; + QLabel *levelLabel; + QLabel *label_2; + QLabel *bitRateLabel; + QLabel *label_3; + QLabel *sampleRateLabel; + QLabel *label_5; + QLabel *fileSizeLabel; + QLabel *label_6; + QLabel *modeLabel; + QLabel *label_8; + QLabel *copyrightLabel; + QLabel *label_9; + QLabel *originalLabel; + QGroupBox *tagGroupBox; + QVBoxLayout *vboxLayout1; + QWidget *tagsWidget; + QGridLayout *gridLayout2; + QLabel *label_21; + QLineEdit *titleLineEdit; + QLabel *label_22; + QLineEdit *artistLineEdit; + QLabel *label_23; + QLineEdit *albumLineEdit; + QLabel *label_24; + QLineEdit *commentLineEdit; + QLabel *label_25; + QLineEdit *yearLineEdit; + QLabel *label_26; + QLineEdit *trackLineEdit; + QLabel *label_27; + QLineEdit *genreLineEdit; + QHBoxLayout *hboxLayout2; + QPushButton *createButton; + QPushButton *deleteButton; + QPushButton *saveButton; + QSpacerItem *spacerItem; + QPushButton *pushButton_3; + + void setupUi(QDialog *DetailsDialog) + { + if (DetailsDialog->objectName().isEmpty()) + DetailsDialog->setObjectName(QString::fromUtf8("DetailsDialog")); + QSize size(593, 402); + size = size.expandedTo(DetailsDialog->minimumSizeHint()); + DetailsDialog->resize(size); + gridLayout = new QGridLayout(DetailsDialog); + gridLayout->setObjectName(QString::fromUtf8("gridLayout")); + hboxLayout = new QHBoxLayout(); + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + label_28 = new QLabel(DetailsDialog); + label_28->setObjectName(QString::fromUtf8("label_28")); + label_28->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + + hboxLayout->addWidget(label_28); + + pathLineEdit = new QLineEdit(DetailsDialog); + pathLineEdit->setObjectName(QString::fromUtf8("pathLineEdit")); + pathLineEdit->setReadOnly(true); + + hboxLayout->addWidget(pathLineEdit); + + + gridLayout->addLayout(hboxLayout, 0, 0, 1, 3); + + vboxLayout = new QVBoxLayout(); + vboxLayout->setObjectName(QString::fromUtf8("vboxLayout")); + groupBox_3 = new QGroupBox(DetailsDialog); + groupBox_3->setObjectName(QString::fromUtf8("groupBox_3")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(groupBox_3->sizePolicy().hasHeightForWidth()); + groupBox_3->setSizePolicy(sizePolicy); + hboxLayout1 = new QHBoxLayout(groupBox_3); + hboxLayout1->setObjectName(QString::fromUtf8("hboxLayout1")); + id3v1RadioButton = new QRadioButton(groupBox_3); + id3v1RadioButton->setObjectName(QString::fromUtf8("id3v1RadioButton")); + + hboxLayout1->addWidget(id3v1RadioButton); + + id3v2RadioButton = new QRadioButton(groupBox_3); + id3v2RadioButton->setObjectName(QString::fromUtf8("id3v2RadioButton")); + + hboxLayout1->addWidget(id3v2RadioButton); + + apeRadioButton = new QRadioButton(groupBox_3); + apeRadioButton->setObjectName(QString::fromUtf8("apeRadioButton")); + + hboxLayout1->addWidget(apeRadioButton); + + + vboxLayout->addWidget(groupBox_3); + + groupBox = new QGroupBox(DetailsDialog); + groupBox->setObjectName(QString::fromUtf8("groupBox")); + groupBox->setMinimumSize(QSize(200, 16)); + gridLayout1 = new QGridLayout(groupBox); + gridLayout1->setObjectName(QString::fromUtf8("gridLayout1")); + label = new QLabel(groupBox); + label->setObjectName(QString::fromUtf8("label")); + label->setTextFormat(Qt::AutoText); + label->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label, 0, 0, 1, 1); + + levelLabel = new QLabel(groupBox); + levelLabel->setObjectName(QString::fromUtf8("levelLabel")); + + gridLayout1->addWidget(levelLabel, 0, 1, 1, 1); + + label_2 = new QLabel(groupBox); + label_2->setObjectName(QString::fromUtf8("label_2")); + label_2->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_2, 1, 0, 1, 1); + + bitRateLabel = new QLabel(groupBox); + bitRateLabel->setObjectName(QString::fromUtf8("bitRateLabel")); + + gridLayout1->addWidget(bitRateLabel, 1, 1, 1, 1); + + label_3 = new QLabel(groupBox); + label_3->setObjectName(QString::fromUtf8("label_3")); + label_3->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_3, 2, 0, 1, 1); + + sampleRateLabel = new QLabel(groupBox); + sampleRateLabel->setObjectName(QString::fromUtf8("sampleRateLabel")); + + gridLayout1->addWidget(sampleRateLabel, 2, 1, 1, 1); + + label_5 = new QLabel(groupBox); + label_5->setObjectName(QString::fromUtf8("label_5")); + label_5->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_5, 3, 0, 1, 1); + + fileSizeLabel = new QLabel(groupBox); + fileSizeLabel->setObjectName(QString::fromUtf8("fileSizeLabel")); + + gridLayout1->addWidget(fileSizeLabel, 3, 1, 1, 1); + + label_6 = new QLabel(groupBox); + label_6->setObjectName(QString::fromUtf8("label_6")); + label_6->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_6, 4, 0, 1, 1); + + modeLabel = new QLabel(groupBox); + modeLabel->setObjectName(QString::fromUtf8("modeLabel")); + + gridLayout1->addWidget(modeLabel, 4, 1, 1, 1); + + label_8 = new QLabel(groupBox); + label_8->setObjectName(QString::fromUtf8("label_8")); + label_8->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_8, 5, 0, 1, 1); + + copyrightLabel = new QLabel(groupBox); + copyrightLabel->setObjectName(QString::fromUtf8("copyrightLabel")); + + gridLayout1->addWidget(copyrightLabel, 5, 1, 1, 1); + + label_9 = new QLabel(groupBox); + label_9->setObjectName(QString::fromUtf8("label_9")); + label_9->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout1->addWidget(label_9, 6, 0, 1, 1); + + originalLabel = new QLabel(groupBox); + originalLabel->setObjectName(QString::fromUtf8("originalLabel")); + + gridLayout1->addWidget(originalLabel, 6, 1, 1, 1); + + + vboxLayout->addWidget(groupBox); + + + gridLayout->addLayout(vboxLayout, 1, 0, 2, 1); + + tagGroupBox = new QGroupBox(DetailsDialog); + tagGroupBox->setObjectName(QString::fromUtf8("tagGroupBox")); + vboxLayout1 = new QVBoxLayout(tagGroupBox); + vboxLayout1->setObjectName(QString::fromUtf8("vboxLayout1")); + tagsWidget = new QWidget(tagGroupBox); + tagsWidget->setObjectName(QString::fromUtf8("tagsWidget")); + tagsWidget->setEnabled(true); + gridLayout2 = new QGridLayout(tagsWidget); + gridLayout2->setObjectName(QString::fromUtf8("gridLayout2")); + label_21 = new QLabel(tagsWidget); + label_21->setObjectName(QString::fromUtf8("label_21")); + label_21->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_21, 0, 0, 1, 1); + + titleLineEdit = new QLineEdit(tagsWidget); + titleLineEdit->setObjectName(QString::fromUtf8("titleLineEdit")); + + gridLayout2->addWidget(titleLineEdit, 0, 1, 1, 3); + + label_22 = new QLabel(tagsWidget); + label_22->setObjectName(QString::fromUtf8("label_22")); + label_22->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_22, 1, 0, 1, 1); + + artistLineEdit = new QLineEdit(tagsWidget); + artistLineEdit->setObjectName(QString::fromUtf8("artistLineEdit")); + + gridLayout2->addWidget(artistLineEdit, 1, 1, 1, 3); + + label_23 = new QLabel(tagsWidget); + label_23->setObjectName(QString::fromUtf8("label_23")); + label_23->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_23, 2, 0, 1, 1); + + albumLineEdit = new QLineEdit(tagsWidget); + albumLineEdit->setObjectName(QString::fromUtf8("albumLineEdit")); + + gridLayout2->addWidget(albumLineEdit, 2, 1, 1, 3); + + label_24 = new QLabel(tagsWidget); + label_24->setObjectName(QString::fromUtf8("label_24")); + label_24->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_24, 3, 0, 1, 1); + + commentLineEdit = new QLineEdit(tagsWidget); + commentLineEdit->setObjectName(QString::fromUtf8("commentLineEdit")); + + gridLayout2->addWidget(commentLineEdit, 3, 1, 1, 3); + + label_25 = new QLabel(tagsWidget); + label_25->setObjectName(QString::fromUtf8("label_25")); + label_25->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_25, 4, 0, 1, 1); + + yearLineEdit = new QLineEdit(tagsWidget); + yearLineEdit->setObjectName(QString::fromUtf8("yearLineEdit")); + + gridLayout2->addWidget(yearLineEdit, 4, 1, 1, 1); + + label_26 = new QLabel(tagsWidget); + label_26->setObjectName(QString::fromUtf8("label_26")); + label_26->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_26, 4, 2, 1, 1); + + trackLineEdit = new QLineEdit(tagsWidget); + trackLineEdit->setObjectName(QString::fromUtf8("trackLineEdit")); + + gridLayout2->addWidget(trackLineEdit, 4, 3, 1, 1); + + label_27 = new QLabel(tagsWidget); + label_27->setObjectName(QString::fromUtf8("label_27")); + label_27->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + gridLayout2->addWidget(label_27, 5, 0, 1, 1); + + genreLineEdit = new QLineEdit(tagsWidget); + genreLineEdit->setObjectName(QString::fromUtf8("genreLineEdit")); + + gridLayout2->addWidget(genreLineEdit, 5, 1, 1, 3); + + + vboxLayout1->addWidget(tagsWidget); + + hboxLayout2 = new QHBoxLayout(); + hboxLayout2->setObjectName(QString::fromUtf8("hboxLayout2")); + createButton = new QPushButton(tagGroupBox); + createButton->setObjectName(QString::fromUtf8("createButton")); + + hboxLayout2->addWidget(createButton); + + deleteButton = new QPushButton(tagGroupBox); + deleteButton->setObjectName(QString::fromUtf8("deleteButton")); + + hboxLayout2->addWidget(deleteButton); + + saveButton = new QPushButton(tagGroupBox); + saveButton->setObjectName(QString::fromUtf8("saveButton")); + saveButton->setEnabled(false); + + hboxLayout2->addWidget(saveButton); + + + vboxLayout1->addLayout(hboxLayout2); + + + gridLayout->addWidget(tagGroupBox, 1, 1, 1, 2); + + spacerItem = new QSpacerItem(111, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); + + gridLayout->addItem(spacerItem, 2, 1, 1, 1); + + pushButton_3 = new QPushButton(DetailsDialog); + pushButton_3->setObjectName(QString::fromUtf8("pushButton_3")); + + gridLayout->addWidget(pushButton_3, 2, 2, 1, 1); + + + retranslateUi(DetailsDialog); + QObject::connect(pushButton_3, SIGNAL(clicked()), DetailsDialog, SLOT(close())); + + QMetaObject::connectSlotsByName(DetailsDialog); + } // setupUi + + void retranslateUi(QDialog *DetailsDialog) + { + DetailsDialog->setWindowTitle(QApplication::translate("DetailsDialog", "Details", 0, QApplication::UnicodeUTF8)); + label_28->setText(QApplication::translate("DetailsDialog", "File path:", 0, QApplication::UnicodeUTF8)); + groupBox_3->setTitle(QApplication::translate("DetailsDialog", "Tag Choice", 0, QApplication::UnicodeUTF8)); + id3v1RadioButton->setText(QApplication::translate("DetailsDialog", "ID3v1", 0, QApplication::UnicodeUTF8)); + id3v2RadioButton->setText(QApplication::translate("DetailsDialog", "ID3v2", 0, QApplication::UnicodeUTF8)); + apeRadioButton->setText(QApplication::translate("DetailsDialog", "APE", 0, QApplication::UnicodeUTF8)); + groupBox->setTitle(QApplication::translate("DetailsDialog", "MPEG Info", 0, QApplication::UnicodeUTF8)); + label->setText(QApplication::translate("DetailsDialog", "MPEG level:", 0, QApplication::UnicodeUTF8)); + levelLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_2->setText(QApplication::translate("DetailsDialog", "Bit rate:", 0, QApplication::UnicodeUTF8)); + bitRateLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_3->setText(QApplication::translate("DetailsDialog", "Sample rate:", 0, QApplication::UnicodeUTF8)); + sampleRateLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_5->setText(QApplication::translate("DetailsDialog", "File size:", 0, QApplication::UnicodeUTF8)); + fileSizeLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_6->setText(QApplication::translate("DetailsDialog", "Mode:", 0, QApplication::UnicodeUTF8)); + modeLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_8->setText(QApplication::translate("DetailsDialog", "Copyright:", 0, QApplication::UnicodeUTF8)); + copyrightLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + label_9->setText(QApplication::translate("DetailsDialog", "Original:", 0, QApplication::UnicodeUTF8)); + originalLabel->setText(QApplication::translate("DetailsDialog", "-", 0, QApplication::UnicodeUTF8)); + tagGroupBox->setTitle(QApplication::translate("DetailsDialog", "ID3v1 Tag", 0, QApplication::UnicodeUTF8)); + label_21->setText(QApplication::translate("DetailsDialog", "Title:", 0, QApplication::UnicodeUTF8)); + label_22->setText(QApplication::translate("DetailsDialog", "Artist:", 0, QApplication::UnicodeUTF8)); + label_23->setText(QApplication::translate("DetailsDialog", "Album:", 0, QApplication::UnicodeUTF8)); + label_24->setText(QApplication::translate("DetailsDialog", "Comment:", 0, QApplication::UnicodeUTF8)); + label_25->setText(QApplication::translate("DetailsDialog", "Year:", 0, QApplication::UnicodeUTF8)); + label_26->setText(QApplication::translate("DetailsDialog", "Track number:", 0, QApplication::UnicodeUTF8)); + label_27->setText(QApplication::translate("DetailsDialog", "Genre:", 0, QApplication::UnicodeUTF8)); + createButton->setText(QApplication::translate("DetailsDialog", "Create", 0, QApplication::UnicodeUTF8)); + deleteButton->setText(QApplication::translate("DetailsDialog", "Delete", 0, QApplication::UnicodeUTF8)); + saveButton->setText(QApplication::translate("DetailsDialog", "Save", 0, QApplication::UnicodeUTF8)); + pushButton_3->setText(QApplication::translate("DetailsDialog", "Close", 0, QApplication::UnicodeUTF8)); + Q_UNUSED(DetailsDialog); + } // retranslateUi + +}; + +namespace Ui { + class DetailsDialog: public Ui_DetailsDialog {}; +} // namespace Ui + +#endif // UI_DETAILSDIALOG_H diff --git a/src/plugins/Input/mad/ui_settingsdialog.h b/src/plugins/Input/mad/ui_settingsdialog.h new file mode 100644 index 000000000..47cdac64f --- /dev/null +++ b/src/plugins/Input/mad/ui_settingsdialog.h @@ -0,0 +1,245 @@ +/******************************************************************************** +** Form generated from reading ui file 'settingsdialog.ui' +** +** Created: Thu Feb 7 00:21:43 2008 +** by: Qt User Interface Compiler version 4.3.0 +** +** WARNING! All changes made in this file will be lost when recompiling ui file! +********************************************************************************/ + +#ifndef UI_SETTINGSDIALOG_H +#define UI_SETTINGSDIALOG_H + +#include <QtCore/QVariant> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QButtonGroup> +#include <QtGui/QComboBox> +#include <QtGui/QDialog> +#include <QtGui/QGroupBox> +#include <QtGui/QHBoxLayout> +#include <QtGui/QLabel> +#include <QtGui/QPushButton> +#include <QtGui/QSpacerItem> +#include <QtGui/QVBoxLayout> + +class Ui_SettingsDialog +{ +public: + QVBoxLayout *vboxLayout; + QGroupBox *groupBox_2; + QVBoxLayout *vboxLayout1; + QHBoxLayout *hboxLayout; + QLabel *label_15_2; + QComboBox *firstTagComboBox; + QHBoxLayout *hboxLayout1; + QLabel *label_15_3; + QComboBox *secondTagComboBox; + QHBoxLayout *hboxLayout2; + QLabel *label_15_4; + QComboBox *thirdTagComboBox; + QGroupBox *groupBox; + QVBoxLayout *vboxLayout2; + QHBoxLayout *hboxLayout3; + QLabel *label_17_2_2; + QComboBox *id3v1EncComboBox; + QHBoxLayout *hboxLayout4; + QLabel *label_18_2_2; + QComboBox *id3v2EncComboBox; + QHBoxLayout *hboxLayout5; + QSpacerItem *spacerItem; + QPushButton *okButton; + QPushButton *cancelButton; + + void setupUi(QDialog *SettingsDialog) + { + if (SettingsDialog->objectName().isEmpty()) + SettingsDialog->setObjectName(QString::fromUtf8("SettingsDialog")); + QSize size(242, 303); + size = size.expandedTo(SettingsDialog->minimumSizeHint()); + SettingsDialog->resize(size); + vboxLayout = new QVBoxLayout(SettingsDialog); + vboxLayout->setObjectName(QString::fromUtf8("vboxLayout")); + groupBox_2 = new QGroupBox(SettingsDialog); + groupBox_2->setObjectName(QString::fromUtf8("groupBox_2")); + vboxLayout1 = new QVBoxLayout(groupBox_2); + vboxLayout1->setObjectName(QString::fromUtf8("vboxLayout1")); + hboxLayout = new QHBoxLayout(); + hboxLayout->setObjectName(QString::fromUtf8("hboxLayout")); + label_15_2 = new QLabel(groupBox_2); + label_15_2->setObjectName(QString::fromUtf8("label_15_2")); + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(label_15_2->sizePolicy().hasHeightForWidth()); + label_15_2->setSizePolicy(sizePolicy); + label_15_2->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + hboxLayout->addWidget(label_15_2); + + firstTagComboBox = new QComboBox(groupBox_2); + firstTagComboBox->setObjectName(QString::fromUtf8("firstTagComboBox")); + + hboxLayout->addWidget(firstTagComboBox); + + + vboxLayout1->addLayout(hboxLayout); + + hboxLayout1 = new QHBoxLayout(); + hboxLayout1->setObjectName(QString::fromUtf8("hboxLayout1")); + label_15_3 = new QLabel(groupBox_2); + label_15_3->setObjectName(QString::fromUtf8("label_15_3")); + sizePolicy.setHeightForWidth(label_15_3->sizePolicy().hasHeightForWidth()); + label_15_3->setSizePolicy(sizePolicy); + label_15_3->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + hboxLayout1->addWidget(label_15_3); + + secondTagComboBox = new QComboBox(groupBox_2); + secondTagComboBox->setObjectName(QString::fromUtf8("secondTagComboBox")); + + hboxLayout1->addWidget(secondTagComboBox); + + + vboxLayout1->addLayout(hboxLayout1); + + hboxLayout2 = new QHBoxLayout(); + hboxLayout2->setObjectName(QString::fromUtf8("hboxLayout2")); + label_15_4 = new QLabel(groupBox_2); + label_15_4->setObjectName(QString::fromUtf8("label_15_4")); + sizePolicy.setHeightForWidth(label_15_4->sizePolicy().hasHeightForWidth()); + label_15_4->setSizePolicy(sizePolicy); + label_15_4->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + hboxLayout2->addWidget(label_15_4); + + thirdTagComboBox = new QComboBox(groupBox_2); + thirdTagComboBox->setObjectName(QString::fromUtf8("thirdTagComboBox")); + + hboxLayout2->addWidget(thirdTagComboBox); + + + vboxLayout1->addLayout(hboxLayout2); + + + vboxLayout->addWidget(groupBox_2); + + groupBox = new QGroupBox(SettingsDialog); + groupBox->setObjectName(QString::fromUtf8("groupBox")); + vboxLayout2 = new QVBoxLayout(groupBox); + vboxLayout2->setObjectName(QString::fromUtf8("vboxLayout2")); + hboxLayout3 = new QHBoxLayout(); + hboxLayout3->setSpacing(6); + hboxLayout3->setObjectName(QString::fromUtf8("hboxLayout3")); + hboxLayout3->setContentsMargins(0, 0, 0, 0); + label_17_2_2 = new QLabel(groupBox); + label_17_2_2->setObjectName(QString::fromUtf8("label_17_2_2")); + label_17_2_2->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + hboxLayout3->addWidget(label_17_2_2); + + id3v1EncComboBox = new QComboBox(groupBox); + id3v1EncComboBox->setObjectName(QString::fromUtf8("id3v1EncComboBox")); + + hboxLayout3->addWidget(id3v1EncComboBox); + + + vboxLayout2->addLayout(hboxLayout3); + + hboxLayout4 = new QHBoxLayout(); + hboxLayout4->setSpacing(6); + hboxLayout4->setObjectName(QString::fromUtf8("hboxLayout4")); + hboxLayout4->setContentsMargins(0, 0, 0, 0); + label_18_2_2 = new QLabel(groupBox); + label_18_2_2->setObjectName(QString::fromUtf8("label_18_2_2")); + label_18_2_2->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); + + hboxLayout4->addWidget(label_18_2_2); + + id3v2EncComboBox = new QComboBox(groupBox); + id3v2EncComboBox->setObjectName(QString::fromUtf8("id3v2EncComboBox")); + + hboxLayout4->addWidget(id3v2EncComboBox); + + + vboxLayout2->addLayout(hboxLayout4); + + + vboxLayout->addWidget(groupBox); + + hboxLayout5 = new QHBoxLayout(); + hboxLayout5->setSpacing(6); + hboxLayout5->setObjectName(QString::fromUtf8("hboxLayout5")); + hboxLayout5->setContentsMargins(0, 0, 0, 0); + spacerItem = new QSpacerItem(131, 31, QSizePolicy::Expanding, QSizePolicy::Minimum); + + hboxLayout5->addItem(spacerItem); + + okButton = new QPushButton(SettingsDialog); + okButton->setObjectName(QString::fromUtf8("okButton")); + + hboxLayout5->addWidget(okButton); + + cancelButton = new QPushButton(SettingsDialog); + cancelButton->setObjectName(QString::fromUtf8("cancelButton")); + + hboxLayout5->addWidget(cancelButton); + + + vboxLayout->addLayout(hboxLayout5); + + + retranslateUi(SettingsDialog); + QObject::connect(cancelButton, SIGNAL(clicked()), SettingsDialog, SLOT(reject())); + + firstTagComboBox->setCurrentIndex(0); + secondTagComboBox->setCurrentIndex(0); + thirdTagComboBox->setCurrentIndex(0); + + + QMetaObject::connectSlotsByName(SettingsDialog); + } // setupUi + + void retranslateUi(QDialog *SettingsDialog) + { + SettingsDialog->setWindowTitle(QApplication::translate("SettingsDialog", "MPEG Plugin Settings", 0, QApplication::UnicodeUTF8)); + groupBox_2->setTitle(QApplication::translate("SettingsDialog", "Tag Priority", 0, QApplication::UnicodeUTF8)); + label_15_2->setText(QApplication::translate("SettingsDialog", "First:", 0, QApplication::UnicodeUTF8)); + firstTagComboBox->clear(); + firstTagComboBox->insertItems(0, QStringList() + << QApplication::translate("SettingsDialog", "ID3v1", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "ID3v2", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "APE", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "Disabled", 0, QApplication::UnicodeUTF8) + ); + label_15_3->setText(QApplication::translate("SettingsDialog", "Second:", 0, QApplication::UnicodeUTF8)); + secondTagComboBox->clear(); + secondTagComboBox->insertItems(0, QStringList() + << QApplication::translate("SettingsDialog", "ID3v1", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "ID3v2", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "APE", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "Disabled", 0, QApplication::UnicodeUTF8) + ); + label_15_4->setText(QApplication::translate("SettingsDialog", "Third:", 0, QApplication::UnicodeUTF8)); + thirdTagComboBox->clear(); + thirdTagComboBox->insertItems(0, QStringList() + << QApplication::translate("SettingsDialog", "ID3v1", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "ID3v2", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "APE", 0, QApplication::UnicodeUTF8) + << QApplication::translate("SettingsDialog", "Disabled", 0, QApplication::UnicodeUTF8) + ); + groupBox->setTitle(QApplication::translate("SettingsDialog", "Encodings", 0, QApplication::UnicodeUTF8)); + label_17_2_2->setText(QApplication::translate("SettingsDialog", "ID3v1 encoding:", 0, QApplication::UnicodeUTF8)); + label_18_2_2->setText(QApplication::translate("SettingsDialog", "ID3v2 encoding:", 0, QApplication::UnicodeUTF8)); + okButton->setText(QApplication::translate("SettingsDialog", "OK", 0, QApplication::UnicodeUTF8)); + cancelButton->setText(QApplication::translate("SettingsDialog", "Cancel", 0, QApplication::UnicodeUTF8)); + Q_UNUSED(SettingsDialog); + } // retranslateUi + +}; + +namespace Ui { + class SettingsDialog: public Ui_SettingsDialog {}; +} // namespace Ui + +#endif // UI_SETTINGSDIALOG_H diff --git a/src/plugins/Input/mpc/CMakeLists.txt b/src/plugins/Input/mpc/CMakeLists.txt new file mode 100644 index 000000000..dccd6dd6b --- /dev/null +++ b/src/plugins/Input/mpc/CMakeLists.txt @@ -0,0 +1,86 @@ +project(libmpc) + +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}/../../../) + +# libmpc and taglib +PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS) + +IF(NOT TAGLIB_LINK_FLAGS) + SET(TAGLIB_LINK_FLAGS -ltag) + SET(TAGLIB_INCLUDE_DIR /usr/include/taglib) + SET(TAGLIB_CFLAGS -I/usr/include/taglib) +ENDIF(NOT TAGLIB_LINK_FLAGS) + +include_directories(${FLAC_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR}) +link_directories(${FLAC_LINK_DIR} ${TAGLIB_LINK_DIR}) + +ADD_DEFINITIONS(${TAGLIB_CFLAGS}) + + +SET(libmpc_SRCS + decoder_mpc.cpp + decodermpcfactory.cpp + detailsdialog.cpp +) + +SET(libmpc_MOC_HDRS + decodermpcfactory.h + decoder_mpc.h + detailsdialog.h +) + +SET(libmpc_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libmpc_RCC_SRCS ${libmpc_RCCS}) + +QT4_WRAP_CPP(libmpc_MOC_SRCS ${libmpc_MOC_HDRS}) + +# user interface + + +SET(libmpc_UIS + detailsdialog.ui +) + +QT4_WRAP_UI(libmpc_UIS_H ${libmpc_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(mpc SHARED ${libmpc_SRCS} ${libmpc_MOC_SRCS} ${libmpc_UIS_H} + ${libmpc_RCC_SRCS}) +target_link_libraries(mpc ${QT_LIBRARIES} -lqmmp -lmpcdec ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS}) +install(TARGETS mpc DESTINATION ${LIB_DIR}/qmmp/Input PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) + +# clean remaining files + +SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES + "CMakeCache.txt;Makefile;cmake_install.cmake" +) + diff --git a/src/plugins/Input/mpc/Makefile b/src/plugins/Input/mpc/Makefile new file mode 100644 index 000000000..7aa3ee7b3 --- /dev/null +++ b/src/plugins/Input/mpc/Makefile @@ -0,0 +1,254 @@ +############################################################################# +# Makefile for building: libmpc.so +# Generated by qmake (2.01a) (Qt 4.3.1) on: Thu Feb 7 14:07:02 2008 +# Project: mpc.pro +# Template: lib +# Command: /usr/local/Trolltech/Qt-4.3.1/bin/qmake -unix -o Makefile mpc.pro +############################################################################# + +####### Compiler, tools and options + +CC = gcc +CXX = g++ +DEFINES = -DQT_NO_DEBUG -DQT_PLUGIN -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED +CFLAGS = -pipe -O2 -I/usr/include/taglib -Wall -W -D_REENTRANT -fPIC $(DEFINES) +CXXFLAGS = -pipe -O2 -I/usr/include/taglib -Wall -W -D_REENTRANT -fPIC $(DEFINES) +INCPATH = -I/usr/local/Trolltech/Qt-4.3.1/mkspecs/linux-g++ -I. -I/usr/local/Trolltech/Qt-4.3.1/include/QtCore -I/usr/local/Trolltech/Qt-4.3.1/include/QtCore -I/usr/local/Trolltech/Qt-4.3.1/include/QtGui -I/usr/local/Trolltech/Qt-4.3.1/include/QtGui -I/usr/local/Trolltech/Qt-4.3.1/include -I../../../qmmp -I.build/moc -I.build/ui +LINK = g++ +LFLAGS = -Wl,-rpath,/usr/local/Trolltech/Qt-4.3.1/lib -shared +LIBS = $(SUBLIBS) -L../../../../lib -L/usr/local/Trolltech/Qt-4.3.1/lib -lqmmp -L/usr/lib -lmpcdec -I/usr/include -ltag -lQtGui -L/usr/local/Trolltech/Qt-4.3.1/lib -L/usr/X11R6/lib -lpng -lSM -lICE -pthread -pthread -lXi -lXrender -lXrandr -lXfixes -lXcursor -lXinerama -lfreetype -lfontconfig -lXext -lX11 -lQtCore -lz -lm -pthread -lgthread-2.0 -lglib-2.0 -lrt -ldl -lpthread +AR = ar cqs +RANLIB = +QMAKE = /usr/local/Trolltech/Qt-4.3.1/bin/qmake +TAR = tar -cf +COMPRESS = gzip -9f +COPY = cp -f +SED = sed +COPY_FILE = $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE = install -m 644 -p +INSTALL_DIR = $(COPY_DIR) +INSTALL_PROGRAM = install -m 755 -p +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p + +####### Output directory + +OBJECTS_DIR = .build/obj/ + +####### Files + +SOURCES = decoder_mpc.cpp \ + decodermpcfactory.cpp \ + detailsdialog.cpp .build/moc/moc_decodermpcfactory.cpp \ + .build/moc/moc_detailsdialog.cpp +OBJECTS = .build/obj/decoder_mpc.o \ + .build/obj/decodermpcfactory.o \ + .build/obj/detailsdialog.o \ + .build/obj/moc_decodermpcfactory.o \ + .build/obj/moc_detailsdialog.o +DIST = /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_pre.prf \ + ../../../../qmmp.pri \ + ../../plugins.pri \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/link_pkgconfig.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/lex.prf \ + mpc.pro +QMAKE_TARGET = mpc +DESTDIR = ../../../../lib/qmmp/Input/ +TARGET = libmpc.so +TARGETD = libmpc.so + +first: all +####### Implicit rules + +.SUFFIXES: .o .c .cpp .cc .cxx .C + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cc.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cxx.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.C.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" + +####### Build rules + +all: Makefile ../../../../lib/qmmp/Input/$(TARGET) + +../../../../lib/qmmp/Input/$(TARGET): .build/ui/ui_detailsdialog.h $(OBJECTS) $(SUBLIBS) $(OBJCOMP) + @$(CHK_DIR_EXISTS) ../../../../lib/qmmp/Input/ || $(MKDIR) ../../../../lib/qmmp/Input/ + -$(DEL_FILE) $(TARGET) + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP) + -$(MOVE) $(TARGET) ../../../../lib/qmmp/Input/ + + + +Makefile: mpc.pro /usr/local/Trolltech/Qt-4.3.1/mkspecs/linux-g++/qmake.conf /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_pre.prf \ + ../../../../qmmp.pri \ + ../../plugins.pri \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/link_pkgconfig.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.1/mkspecs/features/lex.prf \ + /usr/local/Trolltech/Qt-4.3.1/lib/libQtGui.prl \ + /usr/local/Trolltech/Qt-4.3.1/lib/libQtCore.prl + $(QMAKE) -unix -o Makefile mpc.pro +/usr/local/Trolltech/Qt-4.3.1/mkspecs/common/g++.conf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/common/unix.conf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/common/linux.conf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/qconfig.pri: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_functions.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt_config.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/exclusive_builds.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_pre.prf: +../../../../qmmp.pri: +../../plugins.pri: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/release.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/default_post.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/link_pkgconfig.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/warn_on.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/qt.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/unix/thread.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/moc.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/resources.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/uic.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/yacc.prf: +/usr/local/Trolltech/Qt-4.3.1/mkspecs/features/lex.prf: +/usr/local/Trolltech/Qt-4.3.1/lib/libQtGui.prl: +/usr/local/Trolltech/Qt-4.3.1/lib/libQtCore.prl: +qmake: FORCE + @$(QMAKE) -unix -o Makefile mpc.pro + +dist: + @$(CHK_DIR_EXISTS) .build/obj/mpc1.0.0 || $(MKDIR) .build/obj/mpc1.0.0 + $(COPY_FILE) --parents $(SOURCES) $(DIST) .build/obj/mpc1.0.0/ && $(COPY_FILE) --parents decodermpcfactory.h decoder_mpc.h detailsdialog.h .build/obj/mpc1.0.0/ && $(COPY_FILE) --parents decoder_mpc.cpp decodermpcfactory.cpp detailsdialog.cpp .build/obj/mpc1.0.0/ && $(COPY_FILE) --parents detailsdialog.ui .build/obj/mpc1.0.0/ && (cd `dirname .build/obj/mpc1.0.0` && $(TAR) mpc1.0.0.tar mpc1.0.0 && $(COMPRESS) mpc1.0.0.tar) && $(MOVE) `dirname .build/obj/mpc1.0.0`/mpc1.0.0.tar.gz . && $(DEL_FILE) -r .build/obj/mpc1.0.0 + + +clean:compiler_clean + -$(DEL_FILE) $(OBJECTS) + -$(DEL_FILE) ../../../../lib/qmmp/Input/libmpc.so + -$(DEL_FILE) *~ core *.core + + +####### Sub-libraries + +distclean: clean + -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) Makefile + + +mocclean: compiler_moc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_source_make_all + +compiler_moc_header_make_all: .build/moc/moc_decodermpcfactory.cpp .build/moc/moc_detailsdialog.cpp +compiler_moc_header_clean: + -$(DEL_FILE) .build/moc/moc_decodermpcfactory.cpp .build/moc/moc_detailsdialog.cpp +.build/moc/moc_decodermpcfactory.cpp: decodermpcfactory.h + /usr/local/Trolltech/Qt-4.3.1/bin/moc $(DEFINES) $(INCPATH) decodermpcfactory.h -o .build/moc/moc_decodermpcfactory.cpp + +.build/moc/moc_detailsdialog.cpp: .build/ui/ui_detailsdialog.h \ + detailsdialog.h + /usr/local/Trolltech/Qt-4.3.1/bin/moc $(DEFINES) $(INCPATH) detailsdialog.h -o .build/moc/moc_detailsdialog.cpp + +compiler_rcc_make_all: +compiler_rcc_clean: +compiler_image_collection_make_all: qmake_image_collection.cpp +compiler_image_collection_clean: + -$(DEL_FILE) qmake_image_collection.cpp +compiler_moc_source_make_all: +compiler_moc_source_clean: +compiler_uic_make_all: .build/ui/ui_detailsdialog.h +compiler_uic_clean: + -$(DEL_FILE) .build/ui/ui_detailsdialog.h +.build/ui/ui_detailsdialog.h: detailsdialog.ui + /usr/local/Trolltech/Qt-4.3.1/bin/uic detailsdialog.ui -o .build/ui/ui_detailsdialog.h + +compiler_yacc_decl_make_all: +compiler_yacc_decl_clean: +compiler_yacc_impl_make_all: +compiler_yacc_impl_clean: +compiler_lex_make_all: +compiler_lex_clean: +compiler_clean: compiler_moc_header_clean compiler_uic_clean + +####### Compile + +.build/obj/decoder_mpc.o: decoder_mpc.cpp decoder_mpc.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/decoder_mpc.o decoder_mpc.cpp + +.build/obj/decodermpcfactory.o: decodermpcfactory.cpp detailsdialog.h \ + .build/ui/ui_detailsdialog.h \ + decoder_mpc.h \ + decodermpcfactory.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/decodermpcfactory.o decodermpcfactory.cpp + +.build/obj/detailsdialog.o: detailsdialog.cpp detailsdialog.h \ + .build/ui/ui_detailsdialog.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/detailsdialog.o detailsdialog.cpp + +.build/obj/moc_decodermpcfactory.o: .build/moc/moc_decodermpcfactory.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/moc_decodermpcfactory.o .build/moc/moc_decodermpcfactory.cpp + +.build/obj/moc_detailsdialog.o: .build/moc/moc_detailsdialog.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/moc_detailsdialog.o .build/moc/moc_detailsdialog.cpp + +####### Install + +install_target: first FORCE + @$(CHK_DIR_EXISTS) $(INSTALL_ROOT)/lib/qmmp/Input/ || $(MKDIR) $(INSTALL_ROOT)/lib/qmmp/Input/ + -$(INSTALL_PROGRAM) "../../../../lib/qmmp/Input/$(TARGET)" "$(INSTALL_ROOT)/lib/qmmp/Input/$(TARGET)" + -strip --strip-unneeded "$(INSTALL_ROOT)/lib/qmmp/Input/$(TARGET)" + +uninstall_target: FORCE + -$(DEL_FILE) "$(INSTALL_ROOT)/lib/qmmp/Input/$(TARGET)" + -$(DEL_DIR) $(INSTALL_ROOT)/lib/qmmp/Input/ + + +install: install_target FORCE + +uninstall: uninstall_target FORCE + +FORCE: + diff --git a/src/plugins/Input/mpc/decoder_mpc.cpp b/src/plugins/Input/mpc/decoder_mpc.cpp new file mode 100644 index 000000000..10591a384 --- /dev/null +++ b/src/plugins/Input/mpc/decoder_mpc.cpp @@ -0,0 +1,386 @@ +/*************************************************************************** + * 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 <QIODevice> + +#include "constants.h" +#include "buffer.h" +#include "output.h" +#include "recycler.h" + +#include "decoder_mpc.h" + +// this function used from xmms +inline static void copyBuffer(MPC_SAMPLE_FORMAT* pInBuf, char* pOutBuf, unsigned pLength) +{ + unsigned pSize = 16; + int clipMin = -1 << (pSize - 1); + int clipMax = (1 << (pSize - 1)) - 1; + int floatScale = 1 << (pSize - 1); + for (unsigned n = 0; n < 2 * pLength; n++) + { + int val; +#ifdef MPC_FIXED_POINT + val = shiftSigned(pInBuf[n], pSize - MPC_FIXED_POINT_SCALE_SHIFT); +#else + val = (int) (pInBuf[n] * floatScale); +#endif + if (val < clipMin) + val = clipMin; + else if (val > clipMax) + val = clipMax; + unsigned shift = 0; + do + { + pOutBuf[n * 2 + (shift / 8)] = (unsigned char) ((val >> shift) & 0xFF); + shift += 8; + } + while (shift < pSize); + } +} + +// mpc callbacks + +static mpc_int32_t mpc_callback_read (void *data, void *buffer, mpc_int32_t size) +{ + DecoderMPC *dmpc = (DecoderMPC *) data; + qint64 res; + + res = dmpc->input()->read((char *)buffer, size); + + return res; +} + +static mpc_bool_t mpc_callback_seek (void *data, mpc_int32_t offset) +{ + DecoderMPC *dmpc = (DecoderMPC *) data; + + return dmpc->input()->seek(offset); // ? TRUE : FALSE; +} + +static mpc_int32_t mpc_callback_tell (void *data) +{ + DecoderMPC *dmpc = (DecoderMPC *) data; + return dmpc->input()->pos (); +} + +static mpc_bool_t mpc_callback_canseek (void *data) +{ + DecoderMPC *dmpc = (DecoderMPC *) data; + return !dmpc->input()->isSequential () ; +} + +static mpc_int32_t mpc_callback_get_size (void *data) +{ + DecoderMPC *dmpc = (DecoderMPC *) data; + return dmpc->input()->size(); +} + +// Decoder class + +DecoderMPC::DecoderMPC(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + inited = FALSE; + user_stop = FALSE; + stat = 0; + output_buf = 0; + output_bytes = 0; + output_at = 0; + bks = 0; + done = FALSE; + finish = FALSE; + len = 0; + freq = 0; + bitrate = 0; + seekTime = -1.0; + totalTime = 0.0; + chan = 0; + output_size = 0; + m_data = 0; + + + + +} + + +DecoderMPC::~DecoderMPC() +{ + deinit(); + if(data()) + { + delete data(); + m_data = 0; + } + if (output_buf) + delete [] output_buf; + output_buf = 0; +} + + +void DecoderMPC::stop() +{ + user_stop = TRUE; +} + + +void DecoderMPC::flush(bool final) +{ + ulong min = final ? 0 : bks; + + while ((! done && ! finish) && output_bytes > min) + { + output()->recycler()->mutex()->lock (); + + while ((! done && ! finish) && output()->recycler()->full()) + { + mutex()->unlock(); + + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock (); + done = user_stop; + } + + if (user_stop || finish) + { + inited = FALSE; + done = TRUE; + } + else + { + output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan); + output_size += bks; + output_at = output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + + +bool DecoderMPC::initialize() +{ + bks = blockSize(); + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; + seekTime = -1.0; + totalTime = 0.0; + + + if (! input()) + { + error("DecoderMPC: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()) + { + error("DecoderMPC: cannot initialize. No input."); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()->isOpen()) + { + if (! input()->open(QIODevice::ReadOnly)) + { + error("DecoderMPC: cannot open input."); + return FALSE; + } + } + if (!m_data) + { + m_data = new mpc_data; + } + + qDebug("DecoderMPC: setting callbacks"); + m_data->reader.read = mpc_callback_read; + m_data->reader.seek = mpc_callback_seek; + m_data->reader.tell = mpc_callback_tell; + m_data->reader.canseek = mpc_callback_canseek; + m_data->reader.get_size = mpc_callback_get_size; + m_data->reader.data = this; + + mpc_streaminfo_init (&m_data->info); + + if (mpc_streaminfo_read (&m_data->info, &m_data->reader) != ERROR_CODE_OK) + return FALSE; + chan = data()->info.channels; + configure(data()->info.sample_freq, chan, 16, data()->info.bitrate); + + mpc_decoder_setup (&data()->decoder, &data()->reader); + + //mpc_decoder_scale_output (&data()->decoder, 3.0); + + if (!mpc_decoder_initialize (&data()->decoder, &data()->info)) + { + error("DecoderMPC: cannot get info."); + return FALSE; + } + totalTime = mpc_streaminfo_get_length(&data()->info); + inited = TRUE; + qDebug("DecoderMPC: initialize succes"); + return TRUE; +} + + +double DecoderMPC::lengthInSeconds() +{ + if (! inited) + return 0; + + return totalTime; +} + + +void DecoderMPC::seek(double pos) +{ + seekTime = pos; +} + + +void DecoderMPC::deinit() +{ + //FLAC__stream_decoder_finish (data()->decoder); + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; +} + +void DecoderMPC::run() +{ + mpc_uint32_t vbrAcc = 0; + mpc_uint32_t vbrUpd = 0; + mutex()->lock (); + + if (! inited) + { + mutex()->unlock(); + + return; + } + stat = DecoderState::Decoding; + mutex()->unlock(); + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + while (! done && ! finish) + { + mutex()->lock (); + // decode + + if (seekTime >= 0.0) + { + mpc_decoder_seek_seconds(&data()->decoder, seekTime); + seekTime = -1.0; + } + MPC_SAMPLE_FORMAT buffer[MPC_DECODER_BUFFER_LENGTH]; + + len = mpc_decoder_decode (&data()->decoder, buffer, &vbrAcc, &vbrUpd); + + copyBuffer(buffer, (char *) (output_buf + output_at), len); + + len = len * 4; + + if (len > 0) + { + bitrate = vbrUpd * data()->info.sample_freq / 1152; + output_at += len; + output_bytes += len; + + if (output()) + flush(); + + } + else if (len == 0) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock (); + // end of stream + while (! output()->recycler()->empty() && ! user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock (); + } + output()->recycler()->mutex()->unlock(); + } + + done = TRUE; + if (! user_stop) + { + finish = TRUE; + } + } + else + { + // error in read + error("DecoderMPC: Error while decoding stream, File appears to be " + "corrupted"); + + finish = TRUE; + } + + mutex()->unlock(); + } + + mutex()->lock (); + + if (finish) + stat = DecoderState::Finished; + else if (user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + deinit(); +} diff --git a/src/plugins/Input/mpc/decoder_mpc.h b/src/plugins/Input/mpc/decoder_mpc.h new file mode 100644 index 000000000..3b17b100e --- /dev/null +++ b/src/plugins/Input/mpc/decoder_mpc.h @@ -0,0 +1,80 @@ +/*************************************************************************** + * 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 __decoder_mpc_h +#define __decoder_mpc_h + +#include <mpcdec/mpcdec.h> + +#include "decoder.h" + +struct mpc_data +{ + mpc_decoder decoder; + mpc_reader reader; + mpc_streaminfo info; +}; + +class DecoderMPC : public Decoder +{ +public: + DecoderMPC(QObject *, DecoderFactory *, QIODevice *, Output *); + virtual ~DecoderMPC(); + + // Standard Decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + // Equalizer + bool isEQSupported() const { return FALSE; } + void setEQEnabled(bool) { ; } + void setEQGain(int) { ; } + void setEQBands(int[10]) { ; } + + struct mpc_data *data() { return m_data; } + + +private: + // thread run function + void run(); + struct mpc_data *m_data; + // helper functions + void flush(bool = FALSE); + void deinit(); + + bool inited, user_stop; + int stat; + + // output buffer + char *output_buf; + ulong output_bytes, output_at; + + unsigned int bks; + bool done, finish; + long len, freq, bitrate; + int chan; + unsigned long output_size; + double totalTime, seekTime; +}; + + +#endif // __decoder_mpc_h diff --git a/src/plugins/Input/mpc/decodermpcfactory.cpp b/src/plugins/Input/mpc/decodermpcfactory.cpp new file mode 100644 index 000000000..f2874a08d --- /dev/null +++ b/src/plugins/Input/mpc/decodermpcfactory.cpp @@ -0,0 +1,95 @@ +#include <QtGui> +#include <taglib/tag.h> +#include <taglib/fileref.h> + +#include "detailsdialog.h" +#include "decoder_mpc.h" +#include "decodermpcfactory.h" + + +// DecoderMPCFactory + +bool DecoderMPCFactory::supports(const QString &source) const +{ + + return (source.right(4).toLower() == ".mpc"); +} + +bool DecoderMPCFactory::canDecode(QIODevice *) const +{ + return FALSE; +} + +const DecoderProperties DecoderMPCFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("Musepack Plugin"); + properties.filter = "*.mpc"; + properties.description = tr("Musepack Files"); + //properties.contentType = ; + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +Decoder *DecoderMPCFactory::create(QObject *parent, QIODevice *input, + Output *output) +{ + return new DecoderMPC(parent, this, input, output); +} + +FileTag *DecoderMPCFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + + TagLib::FileRef fileRef(source.toLocal8Bit ()); + TagLib::Tag *tag = fileRef.tag(); + + if (tag && !tag->isEmpty()) + { + ftag->setValue(FileTag::ALBUM, + QString::fromUtf8(tag->album().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::ARTIST, + QString::fromUtf8(tag->artist().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::COMMENT, + QString::fromUtf8(tag->comment().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::GENRE, + QString::fromUtf8(tag->genre().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::TITLE, + QString::fromUtf8(tag->title().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::YEAR, tag->year()); + ftag->setValue(FileTag::TRACK, tag->track()); + } + + if (fileRef.audioProperties()) + ftag->setValue(FileTag::LENGTH, fileRef.audioProperties()->length()); + + return ftag; +} + +QObject* DecoderMPCFactory::showDetails(QWidget *parent, const QString &path) +{ + DetailsDialog *d = new DetailsDialog(parent, path); + d -> show(); + return d; +} + +void DecoderMPCFactory::showSettings(QWidget *) +{} + +void DecoderMPCFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Musepack Audio Plugin"), + tr("Qmmp Musepack Audio Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *DecoderMPCFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/mpc_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderMPCFactory) diff --git a/src/plugins/Input/mpc/decodermpcfactory.h b/src/plugins/Input/mpc/decodermpcfactory.h new file mode 100644 index 000000000..0f8dda55f --- /dev/null +++ b/src/plugins/Input/mpc/decodermpcfactory.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 DECODERMPCFACTORY_H +#define DECODERMPCFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> +#include <filetag.h> + + + + +class DecoderMPCFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/mpc/detailsdialog.cpp b/src/plugins/Input/mpc/detailsdialog.cpp new file mode 100644 index 000000000..472046717 --- /dev/null +++ b/src/plugins/Input/mpc/detailsdialog.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** + * 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 <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/mpcfile.h> + +#include <QFile> +#include <QFileInfo> + +#include "detailsdialog.h" + +#define QStringToTString_qt4(s) TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8) + +DetailsDialog::DetailsDialog(QWidget *parent, const QString &path) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_path = path; + setWindowTitle (path.section('/',-1)); + path.section('/',-1); + ui.pathLineEdit->setText(m_path); + if(QFile::exists(m_path)) + { + loadMPCInfo(); + loadTag(); + } +} + + +DetailsDialog::~DetailsDialog() +{} + +void DetailsDialog::loadMPCInfo() +{ + TagLib::MPC::File f (m_path.toLocal8Bit()); + QString text; + text = QString("%1").arg(f.audioProperties()->length()/60); + text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0')); + ui.lengthLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->sampleRate()); + ui.sampleRateLabel->setText(text+" "+tr("Hz")); + text = QString("%1").arg(f.audioProperties()->channels()); + ui.channelsLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->bitrate()); + ui.bitrateLabel->setText(text+" "+tr("kbps")); + text = QString("%1").arg(f.audioProperties()->mpcVersion()); + ui.versionLabel->setText(text); + text = QString("%1 "+tr("KB")).arg(f.length()/1024); + ui.fileSizeLabel->setText(text); +} + +void DetailsDialog::loadTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + if (f.tag()) + { //TODO: load codec name from config + + TagLib::String title = f.tag()->title(); + TagLib::String artist = f.tag()->artist(); + TagLib::String album = f.tag()->album(); + TagLib::String comment = f.tag()->comment(); + TagLib::String genre = f.tag()->genre(); + QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed(); + ui.titleLineEdit->setText(string); + string = QString::fromUtf8(artist.toCString(TRUE)).trimmed(); + ui.artistLineEdit->setText(string); + string = QString::fromUtf8(album.toCString(TRUE)).trimmed(); + ui.albumLineEdit->setText(string); + string = QString::fromUtf8(comment.toCString(TRUE)).trimmed(); + ui.commentLineEdit->setText(string); + string = QString("%1").arg(f.tag()->year()); + ui.yearLineEdit->setText(string); + string = QString("%1").arg(f.tag()->track()); + ui.trackLineEdit->setText(string); + string = QString::fromUtf8(genre.toCString(TRUE)).trimmed(); + ui.genreLineEdit->setText(string); + } + QFileInfo info(m_path); + ui.saveButton->setEnabled(info.isWritable()); + connect(ui.saveButton, SIGNAL(clicked()), SLOT(saveTag())); +} + +void DetailsDialog::saveTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + f.tag()->setTitle(QStringToTString_qt4(ui.titleLineEdit->text())); + f.tag()->setArtist(QStringToTString_qt4(ui.artistLineEdit->text())); + f.tag()->setAlbum(QStringToTString_qt4(ui.albumLineEdit->text())); + f.tag()->setComment(QStringToTString_qt4(ui.commentLineEdit->text())); + f.tag()->setGenre(QStringToTString_qt4(ui.genreLineEdit->text())); + f.tag()->setYear(ui.yearLineEdit->text().toUInt()); + f.tag()->setTrack(ui.trackLineEdit->text().toUInt()); + + f.save(); +} diff --git a/src/plugins/Input/mpc/detailsdialog.h b/src/plugins/Input/mpc/detailsdialog.h new file mode 100644 index 000000000..70540bda1 --- /dev/null +++ b/src/plugins/Input/mpc/detailsdialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * 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 DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include <QDialog> + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class DetailsDialog : public QDialog +{ +Q_OBJECT +public: + DetailsDialog(QWidget *parent = 0, const QString &path = 0); + + ~DetailsDialog(); + +private slots: + void saveTag(); + +private: + void loadMPCInfo(); + void loadTag(); + Ui::DetailsDialog ui; + QString m_path; + +}; + +#endif diff --git a/src/plugins/Input/mpc/detailsdialog.ui b/src/plugins/Input/mpc/detailsdialog.ui new file mode 100644 index 000000000..918dd3abf --- /dev/null +++ b/src/plugins/Input/mpc/detailsdialog.ui @@ -0,0 +1,349 @@ +<ui version="4.0" > + <class>DetailsDialog</class> + <widget class="QDialog" name="DetailsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>449</width> + <height>375</height> + </rect> + </property> + <property name="windowTitle" > + <string>Details</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item rowspan="2" row="1" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox" > + <property name="minimumSize" > + <size> + <width>175</width> + <height>16</height> + </size> + </property> + <property name="title" > + <string>Musepack Info</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>74</width> + <height>151</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" colspan="2" > + <widget class="QLabel" name="fileSizeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Length:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="2" > + <widget class="QLabel" name="lengthLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Sample rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" colspan="2" > + <widget class="QLabel" name="sampleRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_10" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Channels:</string> + </property> + <property name="textFormat" > + <enum>Qt::PlainText</enum> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Bitrate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" colspan="2" > + <widget class="QLabel" name="channelsLabel" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="4" column="1" colspan="2" > + <widget class="QLabel" name="bitrateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Stream version:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLabel" name="versionLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="pathLineEdit" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_28" > + <property name="text" > + <string>File path:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="pushButton_3" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2" colspan="2" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Musepack Tag</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="1" colspan="2" > + <widget class="QPushButton" name="saveButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Save</string> + </property> + </widget> + </item> + <item row="4" column="3" > + <widget class="QLineEdit" name="trackLineEdit" /> + </item> + <item row="4" column="2" > + <widget class="QLabel" name="label_26" > + <property name="text" > + <string>Track number:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="yearLineEdit" /> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_25" > + <property name="text" > + <string>Year:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_27" > + <property name="text" > + <string>Genre:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_24" > + <property name="text" > + <string>Comment:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_23" > + <property name="text" > + <string>Album:</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_22" > + <property name="text" > + <string>Artist:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_21" > + <property name="text" > + <string>Title:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="titleLineEdit" /> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLineEdit" name="artistLineEdit" /> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLineEdit" name="albumLineEdit" /> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLineEdit" name="commentLineEdit" /> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLineEdit" name="genreLineEdit" /> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_3</sender> + <signal>clicked()</signal> + <receiver>DetailsDialog</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>623</x> + <y>353</y> + </hint> + <hint type="destinationlabel" > + <x>539</x> + <y>352</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/mpc/mpc.pro b/src/plugins/Input/mpc/mpc.pro new file mode 100644 index 000000000..f1a50683d --- /dev/null +++ b/src/plugins/Input/mpc/mpc.pro @@ -0,0 +1,32 @@ +include(../../plugins.pri) + +FORMS += detailsdialog.ui +HEADERS += decodermpcfactory.h \ + decoder_mpc.h \ + detailsdialog.h +SOURCES += decoder_mpc.cpp \ + decodermpcfactory.cpp \ + detailsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Input/mpc +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libmpc.so + + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -lmpcdec -I/usr/include +PKGCONFIG += taglib +#TRANSLATIONS = translations/mpc_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} + +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target diff --git a/src/plugins/Input/mpc/translations/mpc_plugin_ru.qm b/src/plugins/Input/mpc/translations/mpc_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..0eb8c1533 --- /dev/null +++ b/src/plugins/Input/mpc/translations/mpc_plugin_ru.qm diff --git a/src/plugins/Input/mpc/translations/mpc_plugin_ru.ts b/src/plugins/Input/mpc/translations/mpc_plugin_ru.ts new file mode 100644 index 000000000..53ecd8dc8 --- /dev/null +++ b/src/plugins/Input/mpc/translations/mpc_plugin_ru.ts @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>DecoderMPCFactory</name> + <message> + <location filename="../decodermpcfactory.cpp" line="21"/> + <source>Musepack Plugin</source> + <translation>Модуль Musepack</translation> + </message> + <message> + <location filename="../decodermpcfactory.cpp" line="35"/> + <source>Musepack Files</source> + <translation>Файлы Musepack</translation> + </message> + <message> + <location filename="../decodermpcfactory.cpp" line="63"/> + <source>About Musepack Audio Plugin</source> + <translation>Об аудио-модуле Musepack</translation> + </message> + <message> + <location filename="../decodermpcfactory.cpp" line="64"/> + <source>Qmmp Musepack Audio Plugin</source> + <translation>Аудио-модуль Musepack для Qmmp</translation> + </message> + <message> + <location filename="../decodermpcfactory.cpp" line="65"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="52"/> + <source>Hz</source> + <translation>Гц</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>Musepack Info</source> + <translation>Информация Musepack</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="160"/> + <source>-</source> + <translation></translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="63"/> + <source>Length:</source> + <translation>Длительность:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="80"/> + <source>Sample rate:</source> + <translation>Дискретизация:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="100"/> + <source>Channels:</source> + <translation>Каналов:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="113"/> + <source>File size:</source> + <translation>Размер файла:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="123"/> + <source>Bitrate:</source> + <translation>Битовая частота:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="150"/> + <source>Stream version:</source> + <translation>Версия потока:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="177"/> + <source>File path:</source> + <translation>Путь к файлу:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="187"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="215"/> + <source>Musepack Tag</source> + <translation>Musepack-тег</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="230"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="240"/> + <source>Track number:</source> + <translation>Номер трека:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="253"/> + <source>Year:</source> + <translation>Год:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="263"/> + <source>Genre:</source> + <translation>Жанр:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="273"/> + <source>Comment:</source> + <translation>Комментарий:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="283"/> + <source>Album:</source> + <translation>Альбом:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="293"/> + <source>Artist:</source> + <translation>Исполнитель:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="303"/> + <source>Title:</source> + <translation>Название:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Информация</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="56"/> + <source>kbps</source> + <translation>Кб/с</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="59"/> + <source>KB</source> + <translation>Кб</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/mpc/translations/translations.qrc b/src/plugins/Input/mpc/translations/translations.qrc new file mode 100644 index 000000000..cc88de9ce --- /dev/null +++ b/src/plugins/Input/mpc/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>mpc_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Input/sndfile/CMakeLists.txt b/src/plugins/Input/sndfile/CMakeLists.txt new file mode 100644 index 000000000..974db398e --- /dev/null +++ b/src/plugins/Input/sndfile/CMakeLists.txt @@ -0,0 +1,74 @@ +project(libsndfile) + +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}/../../../) + +# libsndfile +PKGCONFIG(sndfile SNDFILE_INCLUDE_DIR SNDFILE_LINK_DIR SNDFILE_LINK_FLAGS SNDFILE_CFLAGS) + +IF(NOT SNDFILE_LINK_FLAGS) + SET(SNDFILE_LINK_FLAGS -lsndfile) + SET(SNDFILE_INCLUDE_DIR /usr/include) + SET(SNDFILE_CFLAGS -I/usr/include) +ENDIF(NOT SNDFILE_LINK_FLAGS) + +include_directories(${SNDFILE_INCLUDE_DIR}) +link_directories(${SNDFILE_LINK_DIR}) + +ADD_DEFINITIONS(${SNDFILE_CFLAGS}) + + +SET(libsndfile_SRCS + decoder_sndfile.cpp + decodersndfilefactory.cpp +) + +SET(libsndfile_MOC_HDRS + decodersndfilefactory.h + decoder_sndfile.h +) + +#SET(libsndfile_RCCS translations/translations.qrc) + +#QT4_ADD_RESOURCES(libsndfile_RCC_SRCS ${libsndfile_RCCS}) + +QT4_WRAP_CPP(libsndfile_MOC_SRCS ${libsndfile_MOC_HDRS}) + + +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(sndfile SHARED ${libsndfile_SRCS} ${libsndfile_MOC_SRCS} ${libsndfile_UIS_H} + ${libsndfile_RCC_SRCS}) + +target_link_libraries(sndfile ${QT_LIBRARIES} -lqmmp ${SNDFILE_LINK_FLAGS}) +install(TARGETS sndfile DESTINATION ${LIB_DIR}/qmmp/Input) + + + diff --git a/src/plugins/Input/sndfile/decoder_sndfile.cpp b/src/plugins/Input/sndfile/decoder_sndfile.cpp new file mode 100644 index 000000000..b4baa2ba4 --- /dev/null +++ b/src/plugins/Input/sndfile/decoder_sndfile.cpp @@ -0,0 +1,282 @@ +/*************************************************************************** + * 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 <QObject> +#include <QFile> +#include <QFileInfo> + + + +#include "constants.h" +#include "buffer.h" +#include "output.h" +#include "recycler.h" + +#include "decoder_sndfile.h" + +// Decoder class + +DecoderSndFile::DecoderSndFile(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + m_inited = FALSE; + m_user_stop = FALSE; + m_output_buf = 0; + m_output_bytes = 0; + m_output_at = 0; + bks = 0; + m_done = FALSE; + m_finish = FALSE; + m_freq = 0; + m_bitrate = 0; + m_seekTime = -1.0; + m_totalTime = 0.0; + m_chan = 0; + m_output_size = 0; + m_buf = 0; + m_sndfile = 0; +} + + +DecoderSndFile::~DecoderSndFile() +{ + deinit(); +} + + +void DecoderSndFile::stop() +{ + m_user_stop = TRUE; +} + + +void DecoderSndFile::flush(bool final) +{ + ulong min = final ? 0 : bks; + + while ((! m_done && ! m_finish) && m_output_bytes > min) + { + output()->recycler()->mutex()->lock (); + + while ((! m_done && ! m_finish) && output()->recycler()->full()) + { + mutex()->unlock(); + + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock (); + m_done = m_user_stop; + } + + if (m_user_stop || m_finish) + { + m_inited = FALSE; + m_done = TRUE; + } + else + { + m_output_bytes -= produceSound(m_output_buf, m_output_bytes, m_bitrate, m_chan); + m_output_size += bks; + m_output_at = m_output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + + +bool DecoderSndFile::initialize() +{ + bks = blockSize(); + m_inited = m_user_stop = m_done = m_finish = FALSE; + m_freq = m_bitrate = 0; + m_output_size = 0; + m_seekTime = -1.0; + m_totalTime = 0.0; + SF_INFO snd_info; + + + if (! input()) + { + error("DecoderSndFile: cannot initialize. No input."); + + return FALSE; + } + + if (! m_output_buf) + m_output_buf = new char[globalBufferSize]; + m_output_at = 0; + m_output_bytes = 0; + + QString filename = qobject_cast<QFile*>(input())->fileName (); + input()->close(); + + memset (&snd_info, 0, sizeof(snd_info)); + snd_info.format=0; + m_sndfile = sf_open(filename.toLocal8Bit(), SFM_READ, &snd_info); + if (!m_sndfile) + { + qWarning("DecoderSndFile: failed to open: %s", qPrintable(filename)); + return FALSE; + } + + m_freq = snd_info.samplerate; + m_chan = snd_info.channels; + + m_totalTime = (double) snd_info.frames / m_freq; + + m_bitrate = QFileInfo(filename).size () * 8.0 / m_totalTime / 1000.0 + 0.5; + + configure(m_freq, m_chan, 16, m_bitrate); + m_buf = new short[blockSize() / sizeof(short)]; + m_inited = TRUE; + qDebug("DecoderSndFile: detected format: %08X", snd_info.format); + qDebug("DecoderSndFile: initialize succes"); + return TRUE; +} + + +double DecoderSndFile::lengthInSeconds() +{ + if (! m_inited) + return 0; + + return m_totalTime; +} + + +void DecoderSndFile::seek(double pos) +{ + m_seekTime = pos; +} + + +void DecoderSndFile::deinit() +{ + m_inited = m_user_stop = m_done = m_finish = FALSE; + m_freq = m_bitrate = m_chan = 0; + m_output_size = 0; + if (m_inited) + { + delete m_buf; + m_buf = 0; + sf_close(m_sndfile); + m_sndfile = 0; + } +} + +void DecoderSndFile::run() +{ + + long len = 0; + int stat = 0; + + mutex()->lock (); + + if (! m_inited) + { + mutex()->unlock(); + + return; + } + + stat = DecoderState::Decoding; + mutex()->unlock(); + + { + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + while (! m_done && ! m_finish) + { + mutex()->lock (); + // decode + + if (m_seekTime >= 0.0) + { + m_output_size = sf_seek(m_sndfile, m_freq*m_seekTime, SEEK_SET); + m_seekTime = -1.0; + } + + len = sizeof(short)* sf_read_short (m_sndfile, m_buf, blockSize() / sizeof(short)); + + if (len > 0) + { + memmove((char *)(m_output_buf + m_output_at), (char *) m_buf, len); + m_output_at += len; + m_output_bytes += len; + + if (output()) + flush(); + } + else if (len == 0) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock (); + // end of stream + while (! output()->recycler()->empty() && ! m_user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock (); + } + output()->recycler()->mutex()->unlock(); + } + + m_done = TRUE; + if (! m_user_stop) + { + m_finish = TRUE; + } + } + else + { + // error in read + error("DecoderSndFile: Error while decoding stream, File appears to be " + "corrupted"); + + m_finish = TRUE; + } + + mutex()->unlock(); + } + + mutex()->lock (); + + if (m_finish) + stat = DecoderState::Finished; + else if (m_user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + dispatch(DecoderState ((DecoderState::Type) stat)); + deinit(); +} + diff --git a/src/plugins/Input/sndfile/decoder_sndfile.h b/src/plugins/Input/sndfile/decoder_sndfile.h new file mode 100644 index 000000000..53bb8fd81 --- /dev/null +++ b/src/plugins/Input/sndfile/decoder_sndfile.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * 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 DECODER_AUDIOFILE_H +#define DECODER_AUDIOFILE_H + +extern "C"{ +#include <sndfile.h> +} +#include "decoder.h" + + +class DecoderSndFile : public Decoder +{ +public: + DecoderSndFile(QObject *, DecoderFactory *, QIODevice *, Output *); + virtual ~DecoderSndFile(); + + // Standard Decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + +private: + // thread run function + void run(); + // helper functions + void flush(bool = FALSE); + void deinit(); + + // output buffer + char *m_output_buf; + + SNDFILE *m_sndfile; + ulong m_output_bytes, m_output_at; + //struct sndfile_data *m_data; + short *m_buf; + unsigned int bks; + bool m_done, m_finish, m_inited, m_user_stop; + long m_freq, m_bitrate; + int m_chan; + unsigned long m_output_size; + double m_totalTime, m_seekTime; +}; + + +#endif // DECODER_SNDFILE_H diff --git a/src/plugins/Input/sndfile/decodersndfilefactory.cpp b/src/plugins/Input/sndfile/decodersndfilefactory.cpp new file mode 100644 index 000000000..b918d32fc --- /dev/null +++ b/src/plugins/Input/sndfile/decodersndfilefactory.cpp @@ -0,0 +1,126 @@ +/*************************************************************************** + * 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 <sndfile.h> + +#include "decoder_sndfile.h" +#include "decodersndfilefactory.h" + + +// DecoderSndFileFactory + +bool DecoderSndFileFactory::supports(const QString &source) const +{ + + return (source.right(4).toLower() == ".wav") || + (source.right(3).toLower() == ".au") || + (source.right(4).toLower() == ".snd") || + (source.right(4).toLower() == ".aif") || + (source.right(5).toLower() == ".aiff") || + (source.right(5).toLower() == ".8svx") || + (source.right(4).toLower() == ".wav") || + (source.right(4).toLower() == ".sph") || + (source.right(3).toLower() == ".sf") || + (source.right(4).toLower() == ".voc"); +} + +bool DecoderSndFileFactory::canDecode(QIODevice *) const +{ + return FALSE; +} + +const DecoderProperties DecoderSndFileFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("Sndfile Plugin"); + properties.filter = "*.wav *.au *.snd *.aif *.aiff *.8svx *.sph *.sf *.voc"; + properties.description = tr("PCM Files"); + //properties.contentType = ""; + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +Decoder *DecoderSndFileFactory::create(QObject *parent, QIODevice *input, + Output *output) +{ + return new DecoderSndFile(parent, this, input, output); +} + +FileTag *DecoderSndFileFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + SF_INFO snd_info; + SNDFILE *sndfile = 0; + memset (&snd_info, 0, sizeof(snd_info)); + snd_info.format = 0; + sndfile = sf_open(source.toLocal8Bit(), SFM_READ, &snd_info); + if (!sndfile) + return ftag; + + if (sf_get_string(sndfile, SF_STR_TITLE)) + { + char* title = strdup(sf_get_string(sndfile, SF_STR_TITLE)); + ftag->setValue(FileTag::TITLE, QString::fromUtf8(title).trimmed()); + } + if (sf_get_string(sndfile, SF_STR_ARTIST)) + { + char* artist = strdup(sf_get_string(sndfile, SF_STR_ARTIST)); + ftag->setValue(FileTag::ARTIST, QString::fromUtf8(artist).trimmed()); + } + if (sf_get_string(sndfile, SF_STR_COMMENT)) + { + char* comment = strdup(sf_get_string(sndfile, SF_STR_COMMENT)); + ftag->setValue(FileTag::COMMENT, QString::fromUtf8(comment).trimmed()); + } + + ftag->setValue(FileTag::LENGTH ,int(snd_info.frames / snd_info.samplerate)); + + sf_close(sndfile); + return ftag; +} + +QObject* DecoderSndFileFactory::showDetails(QWidget *parent, const QString &path) +{ + return 0; +} + +void DecoderSndFileFactory::showSettings(QWidget *) +{} + +void DecoderSndFileFactory::showAbout(QWidget *parent) +{ + char version [128] ; + sf_command (NULL, SFC_GET_LIB_VERSION, version, sizeof (version)) ; + QMessageBox::about (parent, tr("About Sndfile Audio Plugin"), + tr("Qmmp Sndfile Audio Plugin")+"\n"+ + tr("Compiled against")+" "+QString(version)+"\n" + + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +} + +QTranslator *DecoderSndFileFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/sndfile_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderSndFileFactory) diff --git a/src/plugins/Input/sndfile/decodersndfilefactory.h b/src/plugins/Input/sndfile/decodersndfilefactory.h new file mode 100644 index 000000000..8439594fb --- /dev/null +++ b/src/plugins/Input/sndfile/decodersndfilefactory.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 DECODERSNDFILEFACTORY_H +#define DECODERSNDFILEFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> +#include <filetag.h> + + + + +class DecoderSndFileFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/sndfile/sndfile.pro b/src/plugins/Input/sndfile/sndfile.pro new file mode 100644 index 000000000..701ea58fb --- /dev/null +++ b/src/plugins/Input/sndfile/sndfile.pro @@ -0,0 +1,28 @@ +include(../../plugins.pri) + +HEADERS += decodersndfilefactory.h \ + decoder_sndfile.h +SOURCES += decoder_sndfile.cpp \ + decodersndfilefactory.cpp + +TARGET=$$PLUGINS_PREFIX/Input/sndfile +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libsndfile.so + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -I/usr/include + +PKGCONFIG += sndfile +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target diff --git a/src/plugins/Input/vorbis/CMakeLists.txt b/src/plugins/Input/vorbis/CMakeLists.txt new file mode 100644 index 000000000..baf5bad8c --- /dev/null +++ b/src/plugins/Input/vorbis/CMakeLists.txt @@ -0,0 +1,96 @@ +project(libvorbis) + +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}/../../../) + +# libvorbis and taglib +PKGCONFIG(ogg OGG_INCLUDE_DIR OGG_LINK_DIR OGG_LINK_FLAGS OGG_CFLAGS) +PKGCONFIG(vorbis VORBIS_INCLUDE_DIR VORBIS_LINK_DIR VORBIS_LINK_FLAGS VORBIS_CFLAGS) +PKGCONFIG(vorbisfile VORBISFILE_INCLUDE_DIR VORBISFILE_LINK_DIR VORBISFILE_LINK_FLAGS VORBISFILE_CFLAGS) + +PKGCONFIG(taglib TAGLIB_INCLUDE_DIR TAGLIB_LINK_DIR TAGLIB_LINK_FLAGS TAGLIB_CFLAGS) + +IF(NOT OGG_LINK_FLAGS) + SET(OGG_LINK_FLAGS -logg) +ENDIF(NOT OGG_LINK_FLAGS) + +IF(NOT VORBIS_LINK_FLAGS) + SET(VORBIS_LINK_FLAGS -lvorbis) +ENDIF(NOT VORBIS_LINK_FLAGS) + +IF(NOT VORBISFILE_LINK_FLAGS) + SET(VORBISFILE_LINK_FLAGS -lvorbisfile) +ENDIF(NOT VORBISFILE_LINK_FLAGS) + +IF(NOT TAGLIB_LINK_FLAGS) + SET(TAGLIB_LINK_FLAGS -ltag) + SET(TAGLIB_INCLUDE_DIR /usr/include/taglib) + SET(TAGLIB_CFLAGS -I/usr/include/taglib) +ENDIF(NOT TAGLIB_LINK_FLAGS) + +include_directories(${VORBIS_INCLUDE_DIR} ${TAGLIB_INCLUDE_DIR}) +link_directories(${VORBIS_LINK_DIR} ${TAGLIB_LINK_DIR}) + +#ADD_DEFINITIONS(${VORBIS_CFLAGS}) +ADD_DEFINITIONS(${TAGLIB_CFLAGS}) + + +SET(libvorbis_SRCS + decoder_vorbis.cpp + decodervorbisfactory.cpp + detailsdialog.cpp +) + +SET(libvorbis_MOC_HDRS + decodervorbisfactory.h + decoder_vorbis.h + detailsdialog.h +) + +SET(libvorbis_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libvorbis_RCC_SRCS ${libvorbis_RCCS}) + +QT4_WRAP_CPP(libvorbis_MOC_SRCS ${libvorbis_MOC_HDRS}) + +# user interface + + +SET(libvorbis_UIS + detailsdialog.ui +) + +QT4_WRAP_UI(libvorbis_UIS_H ${libvorbis_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(vorbis SHARED ${libvorbis_SRCS} ${libvorbis_MOC_SRCS} ${libvorbis_UIS_H} + ${libvorbis_RCC_SRCS}) +target_link_libraries(vorbis ${QT_LIBRARIES} -lqmmp ${VORBIS_LINK_FLAGS} ${VORBISFILE_LINK_FLAGS} ${OGG_LINK_FLAGS} ${TAGLIB_LINK_FLAGS} ${TAGLIB_CFLAGS}) +install(TARGETS vorbis DESTINATION ${LIB_DIR}/qmmp/Input PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) diff --git a/src/plugins/Input/vorbis/decoder_vorbis.cpp b/src/plugins/Input/vorbis/decoder_vorbis.cpp new file mode 100644 index 000000000..31fb99c6f --- /dev/null +++ b/src/plugins/Input/vorbis/decoder_vorbis.cpp @@ -0,0 +1,425 @@ +// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com> +// +// Use, modification and distribution is allowed without limitation, +// warranty, or liability of any kind. +// + +#include "decoder_vorbis.h" +#include "constants.h" +#include "buffer.h" +#include "output.h" +#include "recycler.h" +#include "filetag.h" + +#include <QObject> +#include <QIODevice> + + +// ic functions for OggVorbis + +static size_t oggread (void *buf, size_t size, size_t nmemb, void *src) +{ + if (! src) return 0; + + DecoderVorbis *dogg = (DecoderVorbis *) src; + int len = dogg->input()->read((char *) buf, (size * nmemb)); + return len / size; +} + + +static int oggseek(void *src, int64_t offset, int whence) +{ + DecoderVorbis *dogg = (DecoderVorbis *) src; + + if ( dogg->input()->isSequential ()) + return -1; + + long start = 0; + switch (whence) + { + case SEEK_END: + start = dogg->input()->size(); + break; + + case SEEK_CUR: + start = dogg->input()->pos(); + break; + + case SEEK_SET: + default: + start = 0; + } + + if (dogg->input()->seek(start + offset)) + return 0; + return -1; +} + + +static int oggclose(void *src) +{ + DecoderVorbis *dogg = (DecoderVorbis *) src; + dogg->input()->close(); + return 0; +} + + +static long oggtell(void *src) +{ + DecoderVorbis *dogg = (DecoderVorbis *) src; + long t = dogg->input()->pos(); + return t; +} + + +// Decoder class + +DecoderVorbis::DecoderVorbis(QObject *parent, DecoderFactory *d, QIODevice *i, Output *o) + : Decoder(parent, d, i, o) +{ + inited = FALSE; + user_stop = FALSE; + stat = 0; + output_buf = 0; + output_bytes = 0; + output_at = 0; + bks = 0; + done = FALSE; + finish = FALSE; + len = 0; + freq = 0; + bitrate = 0; + seekTime = -1.0; + totalTime = 0.0; + chan = 0; + output_size = 0; +} + + +DecoderVorbis::~DecoderVorbis() +{ + deinit(); + + if (output_buf) + delete [] output_buf; + output_buf = 0; +} + + +void DecoderVorbis::stop() +{ + user_stop = TRUE; +} + + +void DecoderVorbis::flush(bool final) +{ + ulong min = final ? 0 : bks; + + while ((! done && ! finish) && output_bytes > min) + { + output()->recycler()->mutex()->lock (); + + while ((! done && ! finish) && output()->recycler()->full()) + { + mutex()->unlock(); + + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + + mutex()->lock (); + done = user_stop; + } + + if (user_stop || finish) + { + inited = FALSE; + done = TRUE; + } + else + { + /*ulong sz = output_bytes < bks ? output_bytes : bks; + Buffer *b = output()->recycler()->get(); + + memcpy(b->data, output_buf, sz); + if (sz != bks) memset(b->data + sz, 0, bks - sz); + + b->nbytes = bks; + b->rate = bitrate; + output_size += b->nbytes; + output()->recycler()->add(); + + output_bytes -= sz; + memmove(output_buf, output_buf + sz, output_bytes);*/ + output_bytes -= produceSound(output_buf, output_bytes, bitrate, chan); + output_size += bks; + output_at = output_bytes; + } + + if (output()->recycler()->full()) + { + output()->recycler()->cond()->wakeOne(); + } + + output()->recycler()->mutex()->unlock(); + } +} + + +bool DecoderVorbis::initialize() +{ + qDebug("DecoderVorbis: initialize"); + bks = blockSize(); + + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; + seekTime = -1.0; + totalTime = 0.0; + if (! input()) + { + qDebug("DecoderVorbis: cannot initialize. No input"); + + return FALSE; + } + + if (! output_buf) + output_buf = new char[globalBufferSize]; + output_at = 0; + output_bytes = 0; + + if (! input()->isOpen()) + { + if (! input()->open(QIODevice::ReadOnly)) + { + qWarning(qPrintable("DecoderVorbis: failed to open input. " + + input()->errorString () + ".")); + return FALSE; + } + } + + ov_callbacks oggcb = + { + oggread, + oggseek, + oggclose, + oggtell + }; + if (ov_open_callbacks(this, &oggfile, NULL, 0, oggcb) < 0) + { + qWarning("DecoderVorbis: cannot open stream"); + + return FALSE; + } + + freq = 0; + bitrate = ov_bitrate(&oggfile, -1) / 1000; + chan = 0; + + totalTime = long(ov_time_total(&oggfile, 0)); + totalTime = totalTime < 0 ? 0 : totalTime; + + vorbis_info *ogginfo = ov_info(&oggfile, -1); + if (ogginfo) + { + freq = ogginfo->rate; + chan = ogginfo->channels; + } + + configure(freq, chan, 16, bitrate); + + inited = TRUE; + return TRUE; +} + + +double DecoderVorbis::lengthInSeconds() +{ + if (! inited) + return 0; + + return totalTime; +} + + +void DecoderVorbis::seek(double pos) +{ + seekTime = pos; +} + + +void DecoderVorbis::deinit() +{ + if (inited) + ov_clear(&oggfile); + inited = user_stop = done = finish = FALSE; + len = freq = bitrate = 0; + stat = chan = 0; + output_size = 0; +} + +void DecoderVorbis::updateTags() +{ + int i; + vorbis_comment *comments; + + FileTag tag; + comments = ov_comment (&oggfile, -1); + for (i = 0; i < comments->comments; i++) + { + if (!strncasecmp(comments->user_comments[i], "title=", + strlen ("title="))) + tag.setValue(FileTag::TITLE, QString::fromUtf8(comments->user_comments[i] + + strlen ("title="))); + else if (!strncasecmp(comments->user_comments[i], + "artist=", strlen ("artist="))) + tag.setValue(FileTag::ARTIST, + QString::fromUtf8(comments->user_comments[i] + + strlen ("artist="))); + else if (!strncasecmp(comments->user_comments[i], + "album=", strlen ("album="))) + tag.setValue(FileTag::ALBUM, + QString::fromUtf8(comments->user_comments[i] + + strlen ("album="))); + else if (!strncasecmp(comments->user_comments[i], + "comment=", strlen ("comment="))) + tag.setValue(FileTag::COMMENT, + QString::fromUtf8(comments->user_comments[i] + + strlen ("comment="))); + else if (!strncasecmp(comments->user_comments[i], + "genre=", strlen ("genre="))) + tag.setValue(FileTag::GENRE, QString::fromUtf8 (comments->user_comments[i] + + strlen ("genre="))); + else if (!strncasecmp(comments->user_comments[i], + "tracknumber=", + strlen ("tracknumber="))) + tag.setValue(FileTag::TRACK, atoi (comments->user_comments[i] + + strlen ("tracknumber="))); + else if (!strncasecmp(comments->user_comments[i], + "track=", strlen ("track="))) + tag.setValue(FileTag::TRACK, atoi (comments->user_comments[i] + + strlen ("track="))); + else if (!strncasecmp(comments->user_comments[i], + "date=", strlen ("date="))) + tag.setValue(FileTag::YEAR, atoi (comments->user_comments[i] + + strlen ("date="))); + + } + tag.setValue(FileTag::LENGTH, uint(totalTime)); + dispatch(tag); +} + +void DecoderVorbis::run() +{ + mutex()->lock (); + + if (! inited) + { + mutex()->unlock(); + + return; + } + + //stat = DecoderEvent::Decoding; + stat = DecoderState::Decoding; + mutex()->unlock(); + + { + //DecoderEvent e((DecoderEvent::Type) stat); + //dispatch(e); + //DecoderStatus st ((DecoderStatus::Type) stat); + dispatch(DecoderState ((DecoderState::Type) stat)); + + //emit statusChanged(stat); + } + + int section = 0; + int last_section = -1; + + while (! done && ! finish) + { + mutex()->lock (); + // decode + + if (seekTime >= 0.0) + { + ov_time_seek(&oggfile, double(seekTime)); + seekTime = -1.0; + + output_size = long(ov_time_tell(&oggfile)) * long(freq * chan * 2); + } + len = -1; + while (len < 0) + { + len = ov_read(&oggfile, (char *) (output_buf + output_at), bks, 0, 2, 1, + §ion); + } + if (section != last_section) + updateTags(); + last_section = section; + + if (len > 0) + { + bitrate = ov_bitrate_instant(&oggfile) / 1000; + + output_at += len; + output_bytes += len; + + if (output()) + flush(); + } + else if (len == 0) + { + flush(TRUE); + + if (output()) + { + output()->recycler()->mutex()->lock (); + // end of stream + while (! output()->recycler()->empty() && ! user_stop) + { + output()->recycler()->cond()->wakeOne(); + mutex()->unlock(); + output()->recycler()->cond()->wait(output()->recycler()->mutex()); + mutex()->lock (); + } + output()->recycler()->mutex()->unlock(); + } + + done = TRUE; + if (! user_stop) + { + finish = TRUE; + } + } + else + { + // error in read + error("DecoderVorbis: Error while decoding stream, File appears to be " + "corrupted"); + + finish = TRUE; + } + + mutex()->unlock(); + } + + mutex()->lock (); + + if (finish) + stat = DecoderState::Finished; + else if (user_stop) + stat = DecoderState::Stopped; + + mutex()->unlock(); + + { + /*DecoderEvent e((DecoderEvent::Type) stat); + dispatch(e);*/ + //DecoderStatus st ((DecoderStatus::Type) stat); + //emit statusChanged(st); + dispatch(DecoderState ((DecoderState::Type) stat)); + } + + deinit(); +} diff --git a/src/plugins/Input/vorbis/decoder_vorbis.h b/src/plugins/Input/vorbis/decoder_vorbis.h new file mode 100644 index 000000000..091d856ff --- /dev/null +++ b/src/plugins/Input/vorbis/decoder_vorbis.h @@ -0,0 +1,63 @@ +// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com> +// +// Use, modification and distribution is allowed without limitation, +// warranty, or liability of any kind. +// + +#ifndef __decoder_vorbis_h +#define __decoder_vorbis_h + +#include "decoder.h" + +#include <vorbis/vorbisfile.h> + + +class DecoderVorbis : public Decoder +{ +public: + DecoderVorbis(QObject *, DecoderFactory *, QIODevice *, Output *); + virtual ~DecoderVorbis(); + + // Standard Decoder API + bool initialize(); + double lengthInSeconds(); + void seek(double); + void stop(); + + // Equalizer + bool isEQSupported() const { return FALSE; } + void setEQEnabled(bool) { ; } + void setEQGain(int) { ; } + void setEQBands(int[10]) { ; } + + +private: + // thread run function + void run(); + + // helper functions + void flush(bool = FALSE); + void deinit(); + + void updateTags(); + + bool inited, user_stop; + int stat; + + // output buffer + char *output_buf; + ulong output_bytes, output_at; + + // OggVorbis Decoder + OggVorbis_File oggfile; + + unsigned int bks; + bool done, finish; + long len, freq, bitrate; + int chan; + unsigned long output_size; + double totalTime, seekTime; +}; + + +#endif // __decoder_vorbis_h diff --git a/src/plugins/Input/vorbis/decodervorbisfactory.cpp b/src/plugins/Input/vorbis/decodervorbisfactory.cpp new file mode 100644 index 000000000..c3b31ec52 --- /dev/null +++ b/src/plugins/Input/vorbis/decodervorbisfactory.cpp @@ -0,0 +1,105 @@ +#include <QtGui> +#include <taglib/tag.h> +#include <taglib/fileref.h> +#include <tag.h> + +#include "detailsdialog.h" +#include "decoder_vorbis.h" +#include "decodervorbisfactory.h" + + +// DecoderOggFactory + +bool DecoderVorbisFactory::supports(const QString &source) const +{ + return source.right(4).toLower() == ".ogg"; +} + +bool DecoderVorbisFactory::canDecode(QIODevice *input) const +{ + char buf[36]; + if (input->peek(buf, 36) == 36 && !memcmp(buf, "OggS", 4) + && !memcmp(buf + 29, "vorbis", 6)) + return TRUE; + + return FALSE; +} + +const DecoderProperties DecoderVorbisFactory::properties() const +{ + DecoderProperties properties; + properties.name = tr("Ogg Vorbis Plugin"); + properties.filter = "*.ogg"; + properties.description = tr("Ogg Vorbis Files"); + properties.contentType = "application/ogg;audio/x-vorbis+ogg"; + properties.hasAbout = TRUE; + properties.hasSettings = FALSE; + return properties; +} + +Decoder *DecoderVorbisFactory::create(QObject *parent, QIODevice *input, + Output *output) +{ + return new DecoderVorbis(parent, this, input, output); +} + +FileTag *DecoderVorbisFactory::createTag(const QString &source) +{ + FileTag *ftag = new FileTag(); + + TagLib::FileRef fileRef(source.toLocal8Bit ()); + TagLib::Tag *tag = fileRef.tag(); + + if (tag && !tag->isEmpty()) + { + ftag->setValue(FileTag::ALBUM, + QString::fromUtf8(tag->album().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::ARTIST, + QString::fromUtf8(tag->artist().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::COMMENT, + QString::fromUtf8(tag->comment().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::GENRE, + QString::fromUtf8(tag->genre().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::TITLE, + QString::fromUtf8(tag->title().toCString(TRUE)).trimmed()); + ftag->setValue(FileTag::YEAR, tag->year()); + ftag->setValue(FileTag::TRACK, tag->track()); + } + + if (fileRef.audioProperties()) + ftag->setValue(FileTag::LENGTH, fileRef.audioProperties()->length()); + + return ftag; +} + +QObject* DecoderVorbisFactory::showDetails(QWidget *parent, const QString &path) +{ + DetailsDialog *d = new DetailsDialog(parent, path); + d -> show(); + return d; +} + +void DecoderVorbisFactory::showSettings(QWidget *) +{ + /*SettingsDialog *s = new SettingsDialog(parent); + s -> show();*/ +} + +void DecoderVorbisFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Ogg Vorbis Audio Plugin"), + tr("Qmmp Ogg Vorbis Audio Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")+"\n"+ + tr("Source code based on mq3 progect") + ); +} + +QTranslator *DecoderVorbisFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/vorbis_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(DecoderVorbisFactory) diff --git a/src/plugins/Input/vorbis/decodervorbisfactory.h b/src/plugins/Input/vorbis/decodervorbisfactory.h new file mode 100644 index 000000000..6830fc102 --- /dev/null +++ b/src/plugins/Input/vorbis/decodervorbisfactory.h @@ -0,0 +1,54 @@ +/*************************************************************************** + * 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 DECODERVORBISFACTORY_H +#define DECODERVORBISFACTORY_H + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <decoder.h> +#include <output.h> +#include <decoderfactory.h> +#include <filetag.h> + + + + +class DecoderVorbisFactory : public QObject, + DecoderFactory +{ +Q_OBJECT +Q_INTERFACES(DecoderFactory); + +public: + bool supports(const QString &source) const; + bool canDecode(QIODevice *input) const; + const DecoderProperties properties() const; + Decoder *create(QObject *, QIODevice *, Output *); + FileTag *createTag(const QString &source); + QObject* showDetails(QWidget *parent, const QString &path); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + +#endif diff --git a/src/plugins/Input/vorbis/detailsdialog.cpp b/src/plugins/Input/vorbis/detailsdialog.cpp new file mode 100644 index 000000000..bbe441703 --- /dev/null +++ b/src/plugins/Input/vorbis/detailsdialog.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + * 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 <taglib/tag.h> +#include <taglib/fileref.h> +#include <taglib/vorbisfile.h> + +#include <QFile> +#include <QFileInfo> + +#include "detailsdialog.h" + +#define QStringToTString_qt4(s) TagLib::String(s.toUtf8().constData(), TagLib::String::UTF8) + +DetailsDialog::DetailsDialog(QWidget *parent, const QString &path) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + m_path = path; + setWindowTitle (path.section('/',-1)); + path.section('/',-1); + ui.pathLineEdit->setText(m_path); + if(QFile::exists(m_path)) + { + loadVorbisInfo(); + loadTag(); + } +} + + +DetailsDialog::~DetailsDialog() +{} + +void DetailsDialog::loadVorbisInfo() +{ + TagLib::Ogg::Vorbis::File f (m_path.toLocal8Bit()); + //l.label + //ui. f.audioProperties()->level(); + QString text; + text = QString("%1").arg(f.audioProperties()->length()/60); + text +=":"+QString("%1").arg(f.audioProperties()->length()%60,2,10,QChar('0')); + ui.lengthLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->sampleRate()); + ui.sampleRateLabel->setText(text+" "+tr("Hz")); + text = QString("%1").arg(f.audioProperties()->channels()); + ui.channelsLabel->setText(text); + text = QString("%1").arg(f.audioProperties()->bitrateNominal()); + ui.nominalLabel->setText(text+" "+tr("kbps")); + text = QString("%1").arg(f.audioProperties()->bitrateMaximum()); + ui.maximumLabel->setText(text+" "+tr("kbps")); + text = QString("%1").arg(f.audioProperties()->bitrateMinimum()); + ui.minimumLabel->setText(text+" "+tr("kbps")); + text = QString("%1 "+tr("KB")).arg(f.length()/1024); + ui.fileSizeLabel->setText(text); + +} + +void DetailsDialog::loadTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + if (f.tag()) + { //TODO: load codec name from config + + TagLib::String title = f.tag()->title(); + TagLib::String artist = f.tag()->artist(); + TagLib::String album = f.tag()->album(); + TagLib::String comment = f.tag()->comment(); + TagLib::String genre = f.tag()->genre(); + QString string = QString::fromUtf8(title.toCString(TRUE)).trimmed(); + ui.titleLineEdit->setText(string); + string = QString::fromUtf8(artist.toCString(TRUE)).trimmed(); + ui.artistLineEdit->setText(string); + string = QString::fromUtf8(album.toCString(TRUE)).trimmed(); + ui.albumLineEdit->setText(string); + string = QString::fromUtf8(comment.toCString(TRUE)).trimmed(); + ui.commentLineEdit->setText(string); + string = QString("%1").arg(f.tag()->year()); + ui.yearLineEdit->setText(string); + string = QString("%1").arg(f.tag()->track()); + ui.trackLineEdit->setText(string); + string = QString::fromUtf8(genre.toCString(TRUE)).trimmed(); + ui.genreLineEdit->setText(string); + } + QFileInfo info(m_path); + ui.saveButton->setEnabled(info.isWritable()); + connect(ui.saveButton, SIGNAL(clicked()), SLOT(saveTag())); +} + +void DetailsDialog::saveTag() +{ + TagLib::FileRef f (m_path.toLocal8Bit()); + + f.tag()->setTitle(QStringToTString_qt4(ui.titleLineEdit->text())); + f.tag()->setArtist(QStringToTString_qt4(ui.artistLineEdit->text())); + f.tag()->setAlbum(QStringToTString_qt4(ui.albumLineEdit->text())); + f.tag()->setComment(QStringToTString_qt4(ui.commentLineEdit->text())); + f.tag()->setGenre(QStringToTString_qt4(ui.genreLineEdit->text())); + f.tag()->setYear(ui.yearLineEdit->text().toUInt()); + f.tag()->setTrack(ui.trackLineEdit->text().toUInt()); + + f.save(); +} diff --git a/src/plugins/Input/vorbis/detailsdialog.h b/src/plugins/Input/vorbis/detailsdialog.h new file mode 100644 index 000000000..94d4243b8 --- /dev/null +++ b/src/plugins/Input/vorbis/detailsdialog.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * 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 DETAILSDIALOG_H +#define DETAILSDIALOG_H + +#include <QDialog> + +#include "ui_detailsdialog.h" + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class DetailsDialog : public QDialog +{ +Q_OBJECT +public: + DetailsDialog(QWidget *parent = 0, const QString &path = 0); + + ~DetailsDialog(); + +private slots: + void saveTag(); + +private: + void loadVorbisInfo(); + void loadTag(); + Ui::DetailsDialog ui; + QString m_path; + +}; + +#endif diff --git a/src/plugins/Input/vorbis/detailsdialog.ui b/src/plugins/Input/vorbis/detailsdialog.ui new file mode 100644 index 000000000..1804ab268 --- /dev/null +++ b/src/plugins/Input/vorbis/detailsdialog.ui @@ -0,0 +1,384 @@ +<ui version="4.0" > + <class>DetailsDialog</class> + <widget class="QDialog" name="DetailsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>536</width> + <height>375</height> + </rect> + </property> + <property name="windowTitle" > + <string>Details</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item rowspan="2" row="1" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox" > + <property name="minimumSize" > + <size> + <width>220</width> + <height>16</height> + </size> + </property> + <property name="title" > + <string>Ogg Vorbis Info</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="2" column="1" > + <widget class="QLabel" name="fileSizeLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLabel" name="channelsLabel" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Length:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="lengthLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Sample rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="sampleRateLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_10" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Channels:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item row="4" column="0" colspan="2" > + <widget class="QGroupBox" name="groupBox_3" > + <property name="title" > + <string>Bit Rate</string> + </property> + <property name="alignment" > + <set>Qt::AlignHCenter</set> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="1" column="1" > + <widget class="QLabel" name="maximumLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QLabel" name="minimumLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_9" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Minimum:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="nominalLabel" > + <property name="text" > + <string>-</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_8" > + <property name="text" > + <string>Maximum:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_7" > + <property name="text" > + <string>Nominal:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>File size:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="pathLineEdit" > + <property name="readOnly" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_28" > + <property name="text" > + <string>File path:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="QPushButton" name="pushButton_3" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="2" colspan="2" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Ogg Vorbis Tag</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>8</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item row="6" column="1" colspan="2" > + <widget class="QPushButton" name="saveButton" > + <property name="enabled" > + <bool>false</bool> + </property> + <property name="text" > + <string>Save</string> + </property> + </widget> + </item> + <item row="4" column="3" > + <widget class="QLineEdit" name="trackLineEdit" /> + </item> + <item row="4" column="2" > + <widget class="QLabel" name="label_26" > + <property name="text" > + <string>Track number:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="4" column="1" > + <widget class="QLineEdit" name="yearLineEdit" /> + </item> + <item row="4" column="0" > + <widget class="QLabel" name="label_25" > + <property name="text" > + <string>Year:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="5" column="0" > + <widget class="QLabel" name="label_27" > + <property name="text" > + <string>Genre:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_24" > + <property name="text" > + <string>Comment:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_23" > + <property name="text" > + <string>Album:</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_22" > + <property name="text" > + <string>Artist:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" > + <widget class="QLabel" name="label_21" > + <property name="text" > + <string>Title:</string> + </property> + <property name="alignment" > + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" colspan="3" > + <widget class="QLineEdit" name="titleLineEdit" /> + </item> + <item row="1" column="1" colspan="3" > + <widget class="QLineEdit" name="artistLineEdit" /> + </item> + <item row="2" column="1" colspan="3" > + <widget class="QLineEdit" name="albumLineEdit" /> + </item> + <item row="3" column="1" colspan="3" > + <widget class="QLineEdit" name="commentLineEdit" /> + </item> + <item row="5" column="1" colspan="2" > + <widget class="QLineEdit" name="genreLineEdit" /> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>pushButton_3</sender> + <signal>clicked()</signal> + <receiver>DetailsDialog</receiver> + <slot>close()</slot> + <hints> + <hint type="sourcelabel" > + <x>623</x> + <y>353</y> + </hint> + <hint type="destinationlabel" > + <x>539</x> + <y>352</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Input/vorbis/translations/translations.qrc b/src/plugins/Input/vorbis/translations/translations.qrc new file mode 100644 index 000000000..c5cacdfb0 --- /dev/null +++ b/src/plugins/Input/vorbis/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>vorbis_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.qm b/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..a6a3a77b7 --- /dev/null +++ b/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.qm diff --git a/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.ts b/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.ts new file mode 100644 index 000000000..16cf81ea3 --- /dev/null +++ b/src/plugins/Input/vorbis/translations/vorbis_plugin_ru.ts @@ -0,0 +1,164 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>DecoderVorbisFactory</name> + <message> + <location filename="../decodervorbisfactory.cpp" line="21"/> + <source>Ogg Vorbis Plugin</source> + <translation>Модуль Ogg Vorbis</translation> + </message> + <message> + <location filename="../decodervorbisfactory.cpp" line="34"/> + <source>Ogg Vorbis Files</source> + <translation>Файлы Ogg Vorbis</translation> + </message> + <message> + <location filename="../decodervorbisfactory.cpp" line="64"/> + <source>About Ogg Vorbis Audio Plugin</source> + <translation>Об аудио-модуле Ogg Vorbis</translation> + </message> + <message> + <location filename="../decodervorbisfactory.cpp" line="65"/> + <source>Qmmp Ogg Vorbis Audio Plugin</source> + <translation>Аудио-модуль Ogg Vorbis для Qmmp</translation> + </message> + <message> + <location filename="../decodervorbisfactory.cpp" line="66"/> + <source>Writen by: Ilya Kotov <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></translation> + </message> + <message> + <location filename="../decodervorbisfactory.cpp" line="68"/> + <source>Source code based on mq3 progect</source> + <translation>Исходный код основан на проекте mq3</translation> + </message> +</context> +<context> + <name>DetailsDialog</name> + <message> + <location filename="../detailsdialog.cpp" line="54"/> + <source>Hz</source> + <translation>Гц</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="265"/> + <source>Save</source> + <translation>Сохранить</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="275"/> + <source>Track number:</source> + <translation>Номер трека:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="288"/> + <source>Year:</source> + <translation>Год:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="298"/> + <source>Genre:</source> + <translation>Жанр:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="308"/> + <source>Comment:</source> + <translation>Комментарий:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="318"/> + <source>Album:</source> + <translation>Альбом:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="328"/> + <source>Artist:</source> + <translation>Исполнитель:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="338"/> + <source>Title:</source> + <translation>Название:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="222"/> + <source>Close</source> + <translation>Закрыть</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="162"/> + <source>-</source> + <translation></translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="192"/> + <source>File size:</source> + <translation>Размер файла:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="77"/> + <source>Sample rate:</source> + <translation>Дискретизация:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="212"/> + <source>File path:</source> + <translation>Путь к файлу:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="31"/> + <source>Ogg Vorbis Info</source> + <translation>Информация Ogg Vorbis</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="60"/> + <source>Length:</source> + <translation>Длительность:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="97"/> + <source>Channels:</source> + <translation>Каналов:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="120"/> + <source>Bit Rate</source> + <translation>Битовая частота</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="152"/> + <source>Minimum:</source> + <translation>Минимальная:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="169"/> + <source>Maximum:</source> + <translation>Максимальная:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="179"/> + <source>Nominal:</source> + <translation>Номинальная:</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="250"/> + <source>Ogg Vorbis Tag</source> + <translation>Оgg Vorbis-тег</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="62"/> + <source>kbps</source> + <translation>Кб/с</translation> + </message> + <message> + <location filename="../detailsdialog.cpp" line="63"/> + <source>KB</source> + <translation>Кб</translation> + </message> + <message> + <location filename="../detailsdialog.ui" line="13"/> + <source>Details</source> + <translation>Информация</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Input/vorbis/vorbis.pro b/src/plugins/Input/vorbis/vorbis.pro new file mode 100644 index 000000000..afd7b6510 --- /dev/null +++ b/src/plugins/Input/vorbis/vorbis.pro @@ -0,0 +1,35 @@ +# ???? ?????? ? KDevelop ?????????? qmake. +# ------------------------------------------- +# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Input/ogg +# ???? - ??????????: + +include(../../plugins.pri) + +FORMS += detailsdialog.ui +HEADERS += decodervorbisfactory.h \ + decoder_vorbis.h \ + detailsdialog.h +SOURCES += decoder_vorbis.cpp \ + decodervorbisfactory.cpp \ + detailsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Input/vorbis +QMAKE_CLEAN =$$PLUGINS_PREFIX/Input/libvorbis.so + +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin \ +link_pkgconfig +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib +PKGCONFIG += taglib ogg vorbisfile vorbis +#TRANSLATIONS = translations/vorbis_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty (LIB_DIR){ +LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Input +INSTALLS += target diff --git a/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption.pro b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption.pro new file mode 100644 index 000000000..1832a0166 --- /dev/null +++ b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = IncDecVolumeOption diff --git a/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/IncDecVolumeOption.pro b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/IncDecVolumeOption.pro new file mode 100644 index 000000000..fdcef5e38 --- /dev/null +++ b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/IncDecVolumeOption.pro @@ -0,0 +1,26 @@ +include(../../../../../qmmp.pri) + +QMMPSRCROOT = ../../../../ + +INCLUDEPATH += $$QMMPSRCROOT/ui \ + $$QMMPSRCROOT/qmmp \ + $$QMMPSRCROOT + +HEADERS += incdecvolumeoption.h \ + $$QMMPSRCROOT/ui/mainwindow.h \ + $$QMMPSRCROOT/qmmp/soundcore.h + +SOURCES += incdecvolumeoption.cpp \ + $$QMMPSRCROOT/qmmp/soundcore.cpp \ + $$QMMPSRCROOT/ui/mainwindow.cpp + +DESTDIR = ../ +QMAKE_CLEAN = ../libincdecvolumeoption.so + + + +CONFIG += release warn_on plugin + +TEMPLATE = lib + + diff --git a/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/Makefile b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/Makefile new file mode 100644 index 000000000..0ac10a77d --- /dev/null +++ b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/Makefile @@ -0,0 +1,301 @@ +############################################################################# +# Makefile for building: libIncDecVolumeOption.so +# Generated by qmake (2.01a) (Qt 4.3.0) on: Thu Feb 7 01:34:34 2008 +# Project: IncDecVolumeOption.pro +# Template: lib +# Command: /usr/local/Trolltech/Qt-4.3.0/bin/qmake -unix -o Makefile IncDecVolumeOption.pro +############################################################################# + +####### Compiler, tools and options + +CC = gcc +CXX = g++ +DEFINES = -DQT_NO_DEBUG -DQT_PLUGIN -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED +CFLAGS = -pipe -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES) +CXXFLAGS = -pipe -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES) +INCPATH = -I/usr/local/Trolltech/Qt-4.3.0/mkspecs/linux-g++ -I. -I/usr/local/Trolltech/Qt-4.3.0/include/QtCore -I/usr/local/Trolltech/Qt-4.3.0/include/QtCore -I/usr/local/Trolltech/Qt-4.3.0/include/QtGui -I/usr/local/Trolltech/Qt-4.3.0/include/QtGui -I/usr/local/Trolltech/Qt-4.3.0/include -I../../../../ui -I../../../../qmmp -I../../../.. -I.build/moc -I./.build/ui/ +LINK = g++ +LFLAGS = -Wl,-rpath,/usr/local/Trolltech/Qt-4.3.0/lib -shared +LIBS = $(SUBLIBS) -L/usr/local/Trolltech/Qt-4.3.0/lib -lQtGui -L/usr/local/Trolltech/Qt-4.3.0/lib -L/usr/X11R6/lib -lpng -lSM -lICE -pthread -pthread -lXi -lXrender -lXrandr -lXfixes -lXcursor -lXinerama -lfreetype -lfontconfig -lXext -lX11 -lQtCore -lz -lm -pthread -lgthread-2.0 -lrt -lglib-2.0 -ldl -lpthread +AR = ar cqs +RANLIB = +QMAKE = /usr/local/Trolltech/Qt-4.3.0/bin/qmake +TAR = tar -cf +COMPRESS = gzip -9f +COPY = cp -f +SED = sed +COPY_FILE = $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE = install -m 644 -p +INSTALL_DIR = $(COPY_DIR) +INSTALL_PROGRAM = install -m 755 -p +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p + +####### Output directory + +OBJECTS_DIR = .build/obj/ + +####### Files + +SOURCES = incdecvolumeoption.cpp \ + ../../../../qmmp/soundcore.cpp \ + ../../../../ui/mainwindow.cpp .build/moc/moc_incdecvolumeoption.cpp \ + .build/moc/moc_mainwindow.cpp \ + .build/moc/moc_soundcore.cpp +OBJECTS = .build/obj/incdecvolumeoption.o \ + .build/obj/soundcore.o \ + .build/obj/mainwindow.o \ + .build/obj/moc_incdecvolumeoption.o \ + .build/obj/moc_mainwindow.o \ + .build/obj/moc_soundcore.o +DIST = /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf \ + /rest/qmmp/qmmp/qmmp.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf \ + IncDecVolumeOption.pro +QMAKE_TARGET = IncDecVolumeOption +DESTDIR = ../ +TARGET = libIncDecVolumeOption.so +TARGETD = libIncDecVolumeOption.so + +first: all +####### Implicit rules + +.SUFFIXES: .o .c .cpp .cc .cxx .C + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cc.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cxx.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.C.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" + +####### Build rules + +all: Makefile ../$(TARGET) + +../$(TARGET): $(OBJECTS) $(SUBLIBS) $(OBJCOMP) + @$(CHK_DIR_EXISTS) ../ || $(MKDIR) ../ + -$(DEL_FILE) $(TARGET) + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP) + -$(MOVE) $(TARGET) ../ + + + +Makefile: IncDecVolumeOption.pro /usr/local/Trolltech/Qt-4.3.0/mkspecs/linux-g++/qmake.conf /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf \ + /rest/qmmp/qmmp/qmmp.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf \ + /usr/local/Trolltech/Qt-4.3.0/lib/libQtGui.prl \ + /usr/local/Trolltech/Qt-4.3.0/lib/libQtCore.prl + $(QMAKE) -unix -o Makefile IncDecVolumeOption.pro +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf: +/rest/qmmp/qmmp/qmmp.pri: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf: +/usr/local/Trolltech/Qt-4.3.0/lib/libQtGui.prl: +/usr/local/Trolltech/Qt-4.3.0/lib/libQtCore.prl: +qmake: FORCE + @$(QMAKE) -unix -o Makefile IncDecVolumeOption.pro + +dist: + @$(CHK_DIR_EXISTS) .build/obj/IncDecVolumeOption1.0.0 || $(MKDIR) .build/obj/IncDecVolumeOption1.0.0 + $(COPY_FILE) --parents $(SOURCES) $(DIST) .build/obj/IncDecVolumeOption1.0.0/ && $(COPY_FILE) --parents incdecvolumeoption.h ../../../../ui/mainwindow.h ../../../../qmmp/soundcore.h .build/obj/IncDecVolumeOption1.0.0/ && $(COPY_FILE) --parents incdecvolumeoption.cpp ../../../../qmmp/soundcore.cpp ../../../../ui/mainwindow.cpp .build/obj/IncDecVolumeOption1.0.0/ && (cd `dirname .build/obj/IncDecVolumeOption1.0.0` && $(TAR) IncDecVolumeOption1.0.0.tar IncDecVolumeOption1.0.0 && $(COMPRESS) IncDecVolumeOption1.0.0.tar) && $(MOVE) `dirname .build/obj/IncDecVolumeOption1.0.0`/IncDecVolumeOption1.0.0.tar.gz . && $(DEL_FILE) -r .build/obj/IncDecVolumeOption1.0.0 + + +clean:compiler_clean + -$(DEL_FILE) $(OBJECTS) + -$(DEL_FILE) ../libincdecvolumeoption.so + -$(DEL_FILE) *~ core *.core + + +####### Sub-libraries + +distclean: clean + -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) Makefile + + +mocclean: compiler_moc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_source_make_all + +compiler_moc_header_make_all: .build/moc/moc_incdecvolumeoption.cpp .build/moc/moc_mainwindow.cpp .build/moc/moc_soundcore.cpp +compiler_moc_header_clean: + -$(DEL_FILE) .build/moc/moc_incdecvolumeoption.cpp .build/moc/moc_mainwindow.cpp .build/moc/moc_soundcore.cpp +.build/moc/moc_incdecvolumeoption.cpp: incdecvolumeoption.h + /usr/local/Trolltech/Qt-4.3.0/bin/moc $(DEFINES) $(INCPATH) incdecvolumeoption.h -o .build/moc/moc_incdecvolumeoption.cpp + +.build/moc/moc_mainwindow.cpp: ../../../../qmmp/output.h \ + ../../../../qmmp/visual.h \ + ../../../../qmmp/outputfactory.h \ + ../../../../qmmp/visualfactory.h \ + ../../../../qmmp/recycler.h \ + ../../../../qmmp/decoder.h \ + ../../../../qmmp/filetag.h \ + ../../../../ui/display.h \ + ../../../../ui/pixmapwidget.h \ + ../../../../ui/mediafile.h \ + ../../../../qmmp/decoderfactory.h \ + ../../../../ui/titlebar.h \ + ../../../../ui/playlist.h \ + ../../../../ui/mainwindow.h \ + ../../../../ui/mainwindow.h + /usr/local/Trolltech/Qt-4.3.0/bin/moc $(DEFINES) $(INCPATH) ../../../../ui/mainwindow.h -o .build/moc/moc_mainwindow.cpp + +.build/moc/moc_soundcore.cpp: ../../../../qmmp/decoder.h \ + ../../../../qmmp/filetag.h \ + ../../../../qmmp/output.h \ + ../../../../qmmp/visual.h \ + ../../../../qmmp/outputfactory.h \ + ../../../../qmmp/visualfactory.h \ + ../../../../qmmp/recycler.h \ + ../../../../qmmp/soundcore.h + /usr/local/Trolltech/Qt-4.3.0/bin/moc $(DEFINES) $(INCPATH) ../../../../qmmp/soundcore.h -o .build/moc/moc_soundcore.cpp + +compiler_rcc_make_all: +compiler_rcc_clean: +compiler_image_collection_make_all: qmake_image_collection.cpp +compiler_image_collection_clean: + -$(DEL_FILE) qmake_image_collection.cpp +compiler_moc_source_make_all: +compiler_moc_source_clean: +compiler_uic_make_all: +compiler_uic_clean: +compiler_yacc_decl_make_all: +compiler_yacc_decl_clean: +compiler_yacc_impl_make_all: +compiler_yacc_impl_clean: +compiler_lex_make_all: +compiler_lex_clean: +compiler_clean: compiler_moc_header_clean + +####### Compile + +.build/obj/incdecvolumeoption.o: incdecvolumeoption.cpp incdecvolumeoption.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/incdecvolumeoption.o incdecvolumeoption.cpp + +.build/obj/soundcore.o: ../../../../qmmp/soundcore.cpp ../../../../qmmp/decoderfactory.h \ + ../../../../qmmp/constants.h \ + ../../../../qmmp/config.h \ + ../../../../qmmp/streamreader.h \ + ../../../../qmmp/effect.h \ + ../../../../qmmp/soundcore.h \ + ../../../../qmmp/decoder.h \ + ../../../../qmmp/filetag.h \ + ../../../../qmmp/output.h \ + ../../../../qmmp/visual.h \ + ../../../../qmmp/outputfactory.h \ + ../../../../qmmp/visualfactory.h \ + ../../../../qmmp/recycler.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/soundcore.o ../../../../qmmp/soundcore.cpp + +.build/obj/mainwindow.o: ../../../../ui/mainwindow.cpp ../../../../ui/textscroller.h \ + ../../../../ui/mainwindow.h \ + ../../../../qmmp/output.h \ + ../../../../qmmp/visual.h \ + ../../../../qmmp/outputfactory.h \ + ../../../../qmmp/visualfactory.h \ + ../../../../qmmp/recycler.h \ + ../../../../qmmp/decoder.h \ + ../../../../qmmp/filetag.h \ + ../../../../ui/display.h \ + ../../../../ui/pixmapwidget.h \ + ../../../../ui/mediafile.h \ + ../../../../qmmp/decoderfactory.h \ + ../../../../ui/titlebar.h \ + ../../../../ui/playlist.h \ + ../../../../qmmp/constants.h \ + ../../../../qmmp/config.h \ + ../../../../ui/fileloader.h \ + ../../../../ui/skin.h \ + ../../../../ui/playlistmodel.h \ + ../../../../ui/configdialog.h \ + ../../../../ui/dock.h \ + ../../../../ui/eqwidget.h \ + ../../../../ui/mainvisual.h \ + ../../../../ui/logscale.h \ + ../../../../ui/playlistformat.h \ + ../../../../ui/jumptotrackdialog.h \ + ../../../../ui/aboutdialog.h \ + ../../../../ui/filedialog.h \ + ../../../../ui/listwidget.h \ + ../../../../ui/visualmenu.h \ + ../../../../ui/commandlineoption.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/mainwindow.o ../../../../ui/mainwindow.cpp + +.build/obj/moc_incdecvolumeoption.o: .build/moc/moc_incdecvolumeoption.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/moc_incdecvolumeoption.o .build/moc/moc_incdecvolumeoption.cpp + +.build/obj/moc_mainwindow.o: .build/moc/moc_mainwindow.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/moc_mainwindow.o .build/moc/moc_mainwindow.cpp + +.build/obj/moc_soundcore.o: .build/moc/moc_soundcore.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o .build/obj/moc_soundcore.o .build/moc/moc_soundcore.cpp + +####### Install + +install: FORCE + +uninstall: FORCE + +FORCE: + diff --git a/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.cpp b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.cpp new file mode 100644 index 000000000..4bd35a910 --- /dev/null +++ b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.cpp @@ -0,0 +1,53 @@ +#include <QtPlugin> + +#include "incdecvolumeoption.h" +#include <soundcore.h> +#include "mainwindow.h" + +bool IncDecVolumeCommandLineOption::identify(const QString & str) const +{ + if( + str == QString("--volume-inc") || + str == QString("--volume-dec") + ) + { + return TRUE; + } + + return FALSE; +} + +const QString IncDecVolumeCommandLineOption::helpString() const +{ + return QString( + "--volume-inc Increase volume with step 10\n" + "--volume-dec Decrease volume with step 10\n" + ); +} + + +void IncDecVolumeCommandLineOption::executeCommand(const QString & option_string, MainWindow *mw) +{ + if (option_string == "--volume-inc") + { + int l = 0; + int r = 0; + mw->soundCore()->volume(&l,&r); + mw->soundCore()->setVolume(l+10,r+10); + } + else if (option_string == "--volume-dec") + { + int l = 0; + int r = 0; + mw->soundCore()->volume(&l,&r); + mw->soundCore()->setVolume(l-10,r-10); + } +} + +const QString IncDecVolumeCommandLineOption::name() const +{ + return "IncDecVolumeCommandLineOption"; +} + +Q_EXPORT_PLUGIN(IncDecVolumeCommandLineOption) + diff --git a/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.h b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.h new file mode 100644 index 000000000..6fbf3d24a --- /dev/null +++ b/src/plugins/Misc/CommandLineOptions/IncDecVolumeOption/incdecvolumeoption.h @@ -0,0 +1,23 @@ +#ifndef IncDecVolumeCommandLineOption_H +#define IncDecVolumeCommandLineOption_H + +#include <commandlineoption.h> + +#include <QString> +#include <QObject> + +class MainWindow; + +class IncDecVolumeCommandLineOption : public QObject, public CommandLineOption +{ +Q_OBJECT +Q_INTERFACES(CommandLineOption) +public: + virtual bool identify(const QString& opt_str)const; + virtual const QString name()const; + virtual const QString helpString()const; + virtual void executeCommand(const QString& opt_str,MainWindow* mw); +}; + +#endif + diff --git a/src/plugins/Misc/FileDialogs/FileDialogs.pro b/src/plugins/Misc/FileDialogs/FileDialogs.pro new file mode 100644 index 000000000..a15058c1f --- /dev/null +++ b/src/plugins/Misc/FileDialogs/FileDialogs.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs +SUBDIRS = Qt3FileDialog \ + QmmpFileDialog
\ No newline at end of file diff --git a/src/plugins/Misc/FileDialogs/Makefile b/src/plugins/Misc/FileDialogs/Makefile new file mode 100644 index 000000000..5e0580ccf --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Makefile @@ -0,0 +1,134 @@ +############################################################################# +# Makefile for building: FileDialogs +# Generated by qmake (2.01a) (Qt 4.2.3) on: Tue Jul 24 19:08:22 2007 +# Project: FileDialogs.pro +# Template: subdirs +# Command: /usr/local/Trolltech/Qt-4.2.3/bin/qmake -unix -o Makefile FileDialogs.pro +############################################################################# + +first: make_default +MAKEFILE = Makefile +QMAKE = /usr/local/Trolltech/Qt-4.2.3/bin/qmake +DEL_FILE = rm -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p +COPY = cp -f +COPY_FILE = $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE = install -m 644 -p +INSTALL_PROGRAM = install -m 755 -p +INSTALL_DIR = $(COPY_DIR) +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p +SUBTARGETS = \ + sub-Qt3FileDialog \ + sub-QmmpFileDialog + +Qt3FileDialog/$(MAKEFILE): + @$(CHK_DIR_EXISTS) Qt3FileDialog/ || $(MKDIR) Qt3FileDialog/ + cd Qt3FileDialog && $(QMAKE) Qt3FileDialog.pro -unix -o $(MAKEFILE) +sub-Qt3FileDialog-qmake_all: FORCE + @$(CHK_DIR_EXISTS) Qt3FileDialog/ || $(MKDIR) Qt3FileDialog/ + cd Qt3FileDialog && $(QMAKE) Qt3FileDialog.pro -unix -o $(MAKEFILE) +sub-Qt3FileDialog: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) +sub-Qt3FileDialog-make_default: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) +sub-Qt3FileDialog-make_first: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) first +sub-Qt3FileDialog-all: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) all +sub-Qt3FileDialog-clean: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) clean +sub-Qt3FileDialog-distclean: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) distclean +sub-Qt3FileDialog-install_subtargets: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) install +sub-Qt3FileDialog-uninstall_subtargets: Qt3FileDialog/$(MAKEFILE) FORCE + cd Qt3FileDialog && $(MAKE) -f $(MAKEFILE) uninstall +QmmpFileDialog/$(MAKEFILE): + @$(CHK_DIR_EXISTS) QmmpFileDialog/ || $(MKDIR) QmmpFileDialog/ + cd QmmpFileDialog && $(QMAKE) QmmpFileDialog.pro -unix -o $(MAKEFILE) +sub-QmmpFileDialog-qmake_all: FORCE + @$(CHK_DIR_EXISTS) QmmpFileDialog/ || $(MKDIR) QmmpFileDialog/ + cd QmmpFileDialog && $(QMAKE) QmmpFileDialog.pro -unix -o $(MAKEFILE) +sub-QmmpFileDialog: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) +sub-QmmpFileDialog-make_default: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) +sub-QmmpFileDialog-make_first: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) first +sub-QmmpFileDialog-all: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) all +sub-QmmpFileDialog-clean: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) clean +sub-QmmpFileDialog-distclean: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) distclean +sub-QmmpFileDialog-install_subtargets: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) install +sub-QmmpFileDialog-uninstall_subtargets: QmmpFileDialog/$(MAKEFILE) FORCE + cd QmmpFileDialog && $(MAKE) -f $(MAKEFILE) uninstall + +Makefile: FileDialogs.pro /usr/local/Trolltech/Qt-4.2.3/mkspecs/linux-g++/qmake.conf /usr/local/Trolltech/Qt-4.2.3/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/default_pre.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.2.3/mkspecs/features/uic.prf + $(QMAKE) -unix -o Makefile FileDialogs.pro +/usr/local/Trolltech/Qt-4.2.3/mkspecs/common/unix.conf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/common/g++.conf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/common/linux.conf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/qconfig.pri: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt_functions.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt_config.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/exclusive_builds.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/default_pre.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/release.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/default_post.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/warn_on.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/qt.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/unix/thread.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/moc.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/resources.prf: +/usr/local/Trolltech/Qt-4.2.3/mkspecs/features/uic.prf: +qmake: qmake_all FORCE + @$(QMAKE) -unix -o Makefile FileDialogs.pro + +qmake_all: sub-Qt3FileDialog-qmake_all sub-QmmpFileDialog-qmake_all FORCE + +make_default: sub-Qt3FileDialog-make_default sub-QmmpFileDialog-make_default FORCE +make_first: sub-Qt3FileDialog-make_first sub-QmmpFileDialog-make_first FORCE +all: sub-Qt3FileDialog-all sub-QmmpFileDialog-all FORCE +clean: sub-Qt3FileDialog-clean sub-QmmpFileDialog-clean FORCE +distclean: sub-Qt3FileDialog-distclean sub-QmmpFileDialog-distclean FORCE + -$(DEL_FILE) Makefile +install_subtargets: sub-Qt3FileDialog-install_subtargets sub-QmmpFileDialog-install_subtargets FORCE +uninstall_subtargets: sub-Qt3FileDialog-uninstall_subtargets sub-QmmpFileDialog-uninstall_subtargets FORCE + +/usr/local/Trolltech/Qt-4.2.3/bin/moc: + (cd $(QTDIR)/src/tools/moc && $(MAKE)) + +mocclean: compiler_moc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_source_make_all +install: install_subtargets FORCE + +uninstall: uninstall_subtargets FORCE + +FORCE: + diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/QmmpFileDialog.pro b/src/plugins/Misc/FileDialogs/QmmpFileDialog/QmmpFileDialog.pro new file mode 100644 index 000000000..d9b8a96df --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/QmmpFileDialog.pro @@ -0,0 +1,28 @@ +QMMPROOT = ../../../qmmp + +INCLUDEPATH += $$QMMPROOT/src +INCLUDEPATH += $$QMMPROOT/lib + +HEADERS += qmmpfiledialog.h \ + qmmpfiledialogimpl.h \ + $$QMMPROOT/src/filedialog.h \ + $$QMMPROOT/src/playlistmodel.h + +SOURCES += qmmpfiledialog.cpp \ + qmmpfiledialogimpl.cpp \ + $$QMMPROOT/src/filedialog.cpp \ + $$QMMPROOT/src/playlistmodel.cpp + + +FORMS += qmmpfiledialog.ui + +RESOURCES += images/images.qrc + +DESTDIR = ../ +QMAKE_CLEAN += ../libqmmpfiledialog.so + + +CONFIG += release warn_on plugin + +TEMPLATE = lib + diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/cdup.png b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/cdup.png Binary files differnew file mode 100644 index 000000000..5d966a77b --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/cdup.png diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/detail.png b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/detail.png Binary files differnew file mode 100644 index 000000000..2e552a425 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/detail.png diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/images.qrc b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/images.qrc new file mode 100644 index 000000000..c194a6b91 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/images.qrc @@ -0,0 +1,8 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>cdup.png</file> + <file>detail.png</file> + <file>list.png</file> + </qresource> +</RCC> diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/list.png b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/list.png Binary files differnew file mode 100644 index 000000000..13b44a620 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/images/list.png diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.cpp b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.cpp new file mode 100644 index 000000000..fce9f6e53 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.cpp @@ -0,0 +1,44 @@ +#include <QtPlugin> + +#include "qmmpfiledialogimpl.h" +#include "qmmpfiledialog.h" + + + +QmmpFileDialog::QmmpFileDialog() +{ + m_dialog = new QmmpFileDialogImpl(); + connect(m_dialog,SIGNAL(filesAdded(const QStringList&)),this,SIGNAL(filesAdded(const QStringList&))); +} + +void QmmpFileDialog::handleSelected(/*const QStringList& s */) +{ +} + +bool QmmpFileDialog::modal()const +{ + return FALSE; +} + +QmmpFileDialog::~QmmpFileDialog() +{ + qWarning("QmmpFileDialog::~QmmpFileDialog()"); + delete m_dialog; +} +void QmmpFileDialog::raise(const QString& d,Mode m,const QStringList& f) +{ + m_dialog->setModeAndMask(d,m,f); + m_dialog->show(); + m_dialog->raise(); +} + + +FileDialog* QmmpFileDialogFactory::create(){ return new QmmpFileDialog();} + +QString QmmpFileDialogFactory::name(){return QmmpFileDialogFactoryName;} + +QString QmmpFileDialogFactory::QmmpFileDialogFactoryName = "Qmmp File Dialog"; + + +Q_EXPORT_PLUGIN(QmmpFileDialogFactory) + diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.h b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.h new file mode 100644 index 000000000..30a4fef59 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.h @@ -0,0 +1,40 @@ +#ifndef QMMPFILEDIALOG_H +#define QMMPFILEDIALOG_H + +#include <filedialog.h> + +class QmmpFileDialogImpl; + +class QmmpFileDialog : public FileDialog +{ +Q_OBJECT + public: + QmmpFileDialog(); + virtual ~QmmpFileDialog(); + virtual bool modal()const; + virtual void raise(const QString&,Mode = AddFiles,const QStringList& = QStringList()); + public slots: + void handleSelected(); + + private: + QmmpFileDialogImpl * m_dialog; +}; + + + + +class QmmpFileDialogFactory : public QObject, public FileDialogFactory +{ + Q_OBJECT + Q_INTERFACES(FileDialogFactory); + public: + virtual FileDialog* create(); + virtual QString name(); + virtual ~QmmpFileDialogFactory(){;} + static QString QmmpFileDialogFactoryName; +}; + + +#endif + + diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.ui b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.ui new file mode 100644 index 000000000..dbfb2edc2 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialog.ui @@ -0,0 +1,216 @@ +<ui version="4.0" > + <class>QmmpFileDialog</class> + <widget class="QDialog" name="QmmpFileDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>517</width> + <height>312</height> + </rect> + </property> + <property name="windowTitle" > + <string>Add Files</string> + </property> + <layout class="QVBoxLayout" > + <property name="margin" > + <number>9</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Look in:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="lookInComboBox" > + <property name="sizePolicy" > + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>1</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="upToolButton" > + <property name="text" > + <string>Up</string> + </property> + <property name="icon" > + <iconset resource="images/images.qrc" >:/cdup.png</iconset> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="listToolButton" > + <property name="toolTip" > + <string>List view</string> + </property> + <property name="text" > + <string>lst</string> + </property> + <property name="icon" > + <iconset resource="images/images.qrc" >:/list.png</iconset> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="iconToolButton" > + <property name="toolTip" > + <string>Icon view</string> + </property> + <property name="text" > + <string>icn</string> + </property> + <property name="icon" > + <iconset resource="images/images.qrc" >:/detail.png</iconset> + </property> + <property name="iconSize" > + <size> + <width>16</width> + <height>16</height> + </size> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QListView" name="fileListView" > + <property name="editTriggers" > + <set>QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed|QAbstractItemView::NoEditTriggers</set> + </property> + <property name="dragEnabled" > + <bool>true</bool> + </property> + <property name="dragDropMode" > + <enum>QAbstractItemView::NoDragDrop</enum> + </property> + <property name="alternatingRowColors" > + <bool>false</bool> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="selectionBehavior" > + <enum>QAbstractItemView::SelectItems</enum> + </property> + <property name="movement" > + <enum>QListView::Static</enum> + </property> + <property name="layoutMode" > + <enum>QListView::Batched</enum> + </property> + <property name="viewMode" > + <enum>QListView::ListMode</enum> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <widget class="QLabel" name="label" > + <property name="text" > + <string>File name:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="fileNameLineEdit" /> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" > + <property name="margin" > + <number>0</number> + </property> + <property name="spacing" > + <number>6</number> + </property> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QPushButton" name="addPushButton" > + <property name="text" > + <string>Add</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="closePushButton" > + <property name="text" > + <string>Close</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources> + <include location="images/images.qrc" /> + </resources> + <connections> + <connection> + <sender>closePushButton</sender> + <signal>clicked()</signal> + <receiver>QmmpFileDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>459</x> + <y>291</y> + </hint> + <hint type="destinationlabel" > + <x>153</x> + <y>289</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.cpp b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.cpp new file mode 100644 index 000000000..984cc7204 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.cpp @@ -0,0 +1,109 @@ +#include "qmmpfiledialogimpl.h" + +#include <QDirModel> +#include <QFileInfo> + +QmmpFileDialogImpl::QmmpFileDialogImpl( QWidget * parent, Qt::WindowFlags f) : QDialog(parent,f) +{ + setupUi(this); + setAttribute(Qt::WA_QuitOnClose, FALSE); + m_model = new QDirModel(this); + m_model->setSorting(QDir::Type /*| QDir::Name*/); + fileListView->setModel(m_model); + //fileListView->setViewMode(QListView::IconMode); + listToolButton->setChecked(true); +} + +QmmpFileDialogImpl::~QmmpFileDialogImpl() +{ +} + +void QmmpFileDialogImpl::on_lookInComboBox_activated(const QString&) +{ + qWarning("TODO: %s %d",__FILE__,__LINE__); +} + +void QmmpFileDialogImpl::on_upToolButton_clicked() +{ + fileListView->setRootIndex(m_model->parent(fileListView->rootIndex())); + lookInComboBox->setEditText(m_model->filePath(fileListView->rootIndex())); +} + +void QmmpFileDialogImpl::on_fileListView_doubleClicked(const QModelIndex& ind) +{ + if(ind.isValid()) + { + QFileInfo info = m_model->fileInfo(ind); + if(info.isDir()) + { + fileListView->setRootIndex(ind); + lookInComboBox->setEditText(m_model->filePath(ind)); + } + else + { + QStringList l; + l << m_model->filePath(ind); + emit filesAdded(l); + } + } + +} + +void QmmpFileDialogImpl::on_fileNameLineEdit_returnPressed() +{ + on_addPushButton_clicked(); +} + +void QmmpFileDialogImpl::on_addPushButton_clicked() +{ + QModelIndexList ml = fileListView->selectionModel()->selectedIndexes(); + QStringList l; + foreach(QModelIndex i,ml) + l << m_model->filePath(i); + qWarning("!!!!!!!!!"); + emit filesAdded(l); +} + +void QmmpFileDialogImpl::setModeAndMask(const QString& d,FileDialog::Mode m, const QStringList & mask) +{ + if(m == FileDialog::AddFiles) + { + setWindowTitle("Add Files"); + m_model->setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + } + else if(m == FileDialog::AddDirs) + { + setWindowTitle("Add Dirs"); + m_model->setFilter(QDir::Dirs | QDir::NoDotAndDotDot); + } + else + { + setWindowTitle("Save File"); + m_model->setFilter(QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); + qWarning("To be implemented..."); + } + + m_model->setSorting(QDir::Type); + fileListView->setRootIndex(m_model->index(d)); + m_model->sort(0); + lookInComboBox->setEditText(d); +} + +void QmmpFileDialogImpl::on_listToolButton_toggled(bool yes) +{ + if(yes) + { + iconToolButton->setChecked(false); + fileListView->setViewMode(QListView::ListMode); + } +} + +void QmmpFileDialogImpl::on_iconToolButton_toggled(bool yes) +{ + if(yes) + { + listToolButton->setChecked(false); + fileListView->setViewMode(QListView::IconMode); + } +} + diff --git a/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.h b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.h new file mode 100644 index 000000000..2be25bf05 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/QmmpFileDialog/qmmpfiledialogimpl.h @@ -0,0 +1,33 @@ +#ifndef QMMPFILEDIALOGIMPL_H +#define QMMPFILEDIALOGIMPL_H + +#include "ui_qmmpfiledialog.h" +#include <QDialog> + +#include "filedialog.h" + +class QDirModel; + + +class QmmpFileDialogImpl : public QDialog , private Ui::QmmpFileDialog +{ +Q_OBJECT +public: + QmmpFileDialogImpl( QWidget * parent = 0, Qt::WindowFlags f = 0 ); + ~QmmpFileDialogImpl(); + void setModeAndMask(const QString&,FileDialog::Mode m,const QStringList& mask); +protected slots: + void on_lookInComboBox_activated(const QString&); + void on_upToolButton_clicked(); + void on_fileListView_doubleClicked(const QModelIndex&); + void on_fileNameLineEdit_returnPressed(); + void on_addPushButton_clicked(); + void on_listToolButton_toggled(bool); + void on_iconToolButton_toggled(bool); +signals: + void filesAdded(const QStringList&); +protected: + QDirModel* m_model; + +}; +#endif //QMMPFILEDIALOGIMPL_H diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/Qt3FileDialog.pro b/src/plugins/Misc/FileDialogs/Qt3FileDialog/Qt3FileDialog.pro new file mode 100644 index 000000000..7954ebf01 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/Qt3FileDialog.pro @@ -0,0 +1,28 @@ +QMMPROOT = ../../../qmmp + +INCLUDEPATH += $$QMMPROOT/src +INCLUDEPATH += $$QMMPROOT/lib + +HEADERS += qt3filedialogfactory.h \ + qt3filedialog.h \ + $$QMMPROOT/src/filedialog.h \ + $$QMMPROOT/src/playlistmodel.h + +SOURCES += qt3filedialog.cpp \ + qt3filedialogfactory.cpp \ + $$QMMPROOT/src/filedialog.cpp \ + $$QMMPROOT/src/playlistmodel.cpp + + +DESTDIR = ../ +QMAKE_CLEAN += ../libqt3filedialog.so + + +CONFIG += release warn_on plugin + +TEMPLATE = lib + +QT += qt3support + +#target.path = /plugins/FileDialogs +#INSTALLS += target diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.cpp b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.cpp new file mode 100644 index 000000000..b9b9bc898 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.cpp @@ -0,0 +1,35 @@ +#include "qt3filedialog.h" +#include <Q3FileDialog> + +Qt3SupportFileDialog::~Qt3SupportFileDialog() +{ + qWarning("Qt3SupportFileDialog::~Qt3SupportFileDialog()"); +} + +QString Qt3SupportFileDialog::existingDirectory(QWidget * parent , const QString & caption, const QString & dir) +{ + return Q3FileDialog::getExistingDirectory ( dir, + parent, + 0, + caption, + true, + true ); +} + +QString Qt3SupportFileDialog::openFileName(QWidget * parent,const QString & caption,const QString & /*dir*/,const QString & filter, + QString * selectedFilter) +{ + return Q3FileDialog::getOpenFileName ( QString(),filter,parent,0,caption,selectedFilter); +} + +QStringList Qt3SupportFileDialog::openFileNames(QWidget * parent, const QString & caption , const QString & dir , + const QString & filter, QString * selectedFilter) +{ + return Q3FileDialog::getOpenFileNames(filter,dir,parent,0,caption,selectedFilter); +} + +QString Qt3SupportFileDialog::saveFileName ( QWidget * parent, const QString & caption, + const QString &, const QString & filter, QString * selectedFilter) +{ + return Q3FileDialog::getSaveFileName(QString(),filter,parent,0,caption,selectedFilter); +} diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.h b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.h new file mode 100644 index 000000000..bbce2e3c8 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialog.h @@ -0,0 +1,23 @@ +#ifndef QT3FILEDIALOG_H +#define QT3FILEDIALOG_H + +#include <filedialog.h> + + +class Qt3SupportFileDialog : public FileDialog +{ + public: + virtual ~Qt3SupportFileDialog(); + virtual QString existingDirectory(QWidget * parent , const QString & , const QString & dir); + virtual QString openFileName(QWidget * parent,const QString & caption,const QString & dir,const QString & filter, + QString * selectedFilter); + virtual QStringList openFileNames(QWidget * parent, const QString & caption , const QString & dir , + const QString & filter, QString * selectedFilter); + virtual QString saveFileName ( QWidget * parent, const QString & caption, + const QString & dir, const QString & filter, QString * selectedFilter); + +}; + +#endif + + diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.cpp b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.cpp new file mode 100644 index 000000000..45b4260bb --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.cpp @@ -0,0 +1,20 @@ +#include <QtPlugin> +#include "qt3filedialogfactory.h" +#include "qt3filedialog.h" + + +FileDialog* Qt3SupportFileDialogFactory::create() +{ + return new Qt3SupportFileDialog(); +} + +QString Qt3SupportFileDialogFactory::name() +{ + return Qt3SupportFileDialogFactory::Qt3SupportFileDialogFactoryName; +} + + +QString Qt3SupportFileDialogFactory::Qt3SupportFileDialogFactoryName = "Qt3 File Dialog"; + +Q_EXPORT_PLUGIN(Qt3SupportFileDialogFactory) + diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.h b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.h new file mode 100644 index 000000000..e5d3b4595 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/qt3filedialogfactory.h @@ -0,0 +1,19 @@ +#ifndef QT3FILEDIALOGFACTORY_H +#define QT3FILEDIALOGFACTORY_H + + +#include <filedialog.h> + +class Qt3SupportFileDialogFactory : public QObject, public FileDialogFactory +{ + Q_OBJECT + Q_INTERFACES(FileDialogFactory); + public: + virtual FileDialog* create(); + virtual QString name(); + virtual ~Qt3SupportFileDialogFactory(){;} + static QString Qt3SupportFileDialogFactoryName; +}; + +#endif + diff --git a/src/plugins/Misc/FileDialogs/Qt3FileDialog/readme b/src/plugins/Misc/FileDialogs/Qt3FileDialog/readme new file mode 100644 index 000000000..966f7201d --- /dev/null +++ b/src/plugins/Misc/FileDialogs/Qt3FileDialog/readme @@ -0,0 +1,14 @@ +This is simple example of File Dialog plugin. +Requires Qt3Support library. This plugin developed +mostly for QMMP customizing demonstration - +we don't think that somebody will prefer use Qt3 +look&feel :) + +Build: Change QMMPROOT variable in .pro file according your +QMMP build tree location.Then qmake && make + +Install: copy library into ~/.qmmp/plugins/FileDialogs directory. + + + vovanec@gmail.com + diff --git a/src/plugins/Misc/FileDialogs/libQmmpFileDialog.so b/src/plugins/Misc/FileDialogs/libQmmpFileDialog.so Binary files differnew file mode 100755 index 000000000..d04ccf53a --- /dev/null +++ b/src/plugins/Misc/FileDialogs/libQmmpFileDialog.so diff --git a/src/plugins/Misc/FileDialogs/libQt3FileDialog.so b/src/plugins/Misc/FileDialogs/libQt3FileDialog.so Binary files differnew file mode 100755 index 000000000..efbb0d313 --- /dev/null +++ b/src/plugins/Misc/FileDialogs/libQt3FileDialog.so diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/CSVPlaylistFormat.pro b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/CSVPlaylistFormat.pro new file mode 100644 index 000000000..11c27c3d3 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/CSVPlaylistFormat.pro @@ -0,0 +1,20 @@ +QMMPROOT = ../../../qmmp + +HEADERS += csvplaylistformat.h + +SOURCES += csvplaylistformat.cpp \ + $$QMMPROOT/src/mediafile.cpp + +DESTDIR = ../ +QMAKE_CLEAN = ../libcsvplaylistformat.so + +INCLUDEPATH += $$QMMPROOT/src \ + $$QMMPROOT/lib + +CONFIG += release warn_on plugin + +TEMPLATE = lib + +#QMAKE_LIBDIR += ../../../ +#LIBS += -lqmmp -L/usr/lib -I/usr/include + diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/Makefile b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/Makefile new file mode 100644 index 000000000..5afa50b01 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/Makefile @@ -0,0 +1,213 @@ +############################################################################# +# Makefile for building: libCSVPlaylistFormat.so +# Generated by qmake (2.01a) (Qt 4.3.0) on: Mon Dec 24 01:59:02 2007 +# Project: CSVPlaylistFormat.pro +# Template: lib +# Command: /usr/local/Trolltech/Qt-4.3.0/bin/qmake -unix -o Makefile CSVPlaylistFormat.pro +############################################################################# + +####### Compiler, tools and options + +CC = gcc +CXX = g++ +DEFINES = -DQT_NO_DEBUG -DQT_PLUGIN -DQT_GUI_LIB -DQT_CORE_LIB -DQT_SHARED +CFLAGS = -pipe -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES) +CXXFLAGS = -pipe -O2 -Wall -W -D_REENTRANT -fPIC $(DEFINES) +INCPATH = -I/usr/local/Trolltech/Qt-4.3.0/mkspecs/linux-g++ -I. -I/usr/local/Trolltech/Qt-4.3.0/include/QtCore -I/usr/local/Trolltech/Qt-4.3.0/include/QtCore -I/usr/local/Trolltech/Qt-4.3.0/include/QtGui -I/usr/local/Trolltech/Qt-4.3.0/include/QtGui -I/usr/local/Trolltech/Qt-4.3.0/include -I../../../qmmp/src -I../../../qmmp/lib -I. -I. +LINK = g++ +LFLAGS = -Wl,-rpath,/usr/local/Trolltech/Qt-4.3.0/lib -shared +LIBS = $(SUBLIBS) -L/usr/local/Trolltech/Qt-4.3.0/lib -lQtGui -L/usr/local/Trolltech/Qt-4.3.0/lib -L/usr/X11R6/lib -lpng -lSM -lICE -pthread -pthread -lXi -lXrender -lXrandr -lXfixes -lXcursor -lXinerama -lfreetype -lfontconfig -lXext -lX11 -lQtCore -lz -lm -pthread -lgthread-2.0 -lrt -lglib-2.0 -ldl -lpthread +AR = ar cqs +RANLIB = +QMAKE = /usr/local/Trolltech/Qt-4.3.0/bin/qmake +TAR = tar -cf +COMPRESS = gzip -9f +COPY = cp -f +SED = sed +COPY_FILE = $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE = install -m 644 -p +INSTALL_DIR = $(COPY_DIR) +INSTALL_PROGRAM = install -m 755 -p +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p + +####### Output directory + +OBJECTS_DIR = ./ + +####### Files + +SOURCES = csvplaylistformat.cpp \ + ../../../qmmp/src/mediafile.cpp moc_csvplaylistformat.cpp +OBJECTS = csvplaylistformat.o \ + mediafile.o \ + moc_csvplaylistformat.o +DIST = /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf \ + CSVPlaylistFormat.pro +QMAKE_TARGET = CSVPlaylistFormat +DESTDIR = ../ +TARGET = libCSVPlaylistFormat.so +TARGETD = libCSVPlaylistFormat.so + +first: all +####### Implicit rules + +.SUFFIXES: .o .c .cpp .cc .cxx .C + +.cpp.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cc.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.cxx.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.C.o: + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<" + +.c.o: + $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<" + +####### Build rules + +all: Makefile ../$(TARGET) + +../$(TARGET): $(OBJECTS) $(SUBLIBS) $(OBJCOMP) + @$(CHK_DIR_EXISTS) ../ || $(MKDIR) ../ + -$(DEL_FILE) $(TARGET) + $(LINK) $(LFLAGS) -o $(TARGET) $(OBJECTS) $(LIBS) $(OBJCOMP) + -$(MOVE) $(TARGET) ../ + + + +Makefile: CSVPlaylistFormat.pro /usr/local/Trolltech/Qt-4.3.0/mkspecs/linux-g++/qmake.conf /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf \ + /usr/local/Trolltech/Qt-4.3.0/lib/libQtGui.prl \ + /usr/local/Trolltech/Qt-4.3.0/lib/libQtCore.prl + $(QMAKE) -unix -o Makefile CSVPlaylistFormat.pro +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf: +/usr/local/Trolltech/Qt-4.3.0/lib/libQtGui.prl: +/usr/local/Trolltech/Qt-4.3.0/lib/libQtCore.prl: +qmake: FORCE + @$(QMAKE) -unix -o Makefile CSVPlaylistFormat.pro + +dist: + @$(CHK_DIR_EXISTS) .tmp/CSVPlaylistFormat1.0.0 || $(MKDIR) .tmp/CSVPlaylistFormat1.0.0 + $(COPY_FILE) --parents $(SOURCES) $(DIST) .tmp/CSVPlaylistFormat1.0.0/ && $(COPY_FILE) --parents csvplaylistformat.h .tmp/CSVPlaylistFormat1.0.0/ && $(COPY_FILE) --parents csvplaylistformat.cpp ../../../qmmp/src/mediafile.cpp .tmp/CSVPlaylistFormat1.0.0/ && (cd `dirname .tmp/CSVPlaylistFormat1.0.0` && $(TAR) CSVPlaylistFormat1.0.0.tar CSVPlaylistFormat1.0.0 && $(COMPRESS) CSVPlaylistFormat1.0.0.tar) && $(MOVE) `dirname .tmp/CSVPlaylistFormat1.0.0`/CSVPlaylistFormat1.0.0.tar.gz . && $(DEL_FILE) -r .tmp/CSVPlaylistFormat1.0.0 + + +clean:compiler_clean + -$(DEL_FILE) $(OBJECTS) + -$(DEL_FILE) ../libcsvplaylistformat.so + -$(DEL_FILE) *~ core *.core + + +####### Sub-libraries + +distclean: clean + -$(DEL_FILE) $(TARGET) + -$(DEL_FILE) Makefile + + +mocclean: compiler_moc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_source_make_all + +compiler_moc_header_make_all: moc_csvplaylistformat.cpp +compiler_moc_header_clean: + -$(DEL_FILE) moc_csvplaylistformat.cpp +moc_csvplaylistformat.cpp: csvplaylistformat.h + /usr/local/Trolltech/Qt-4.3.0/bin/moc $(DEFINES) $(INCPATH) csvplaylistformat.h -o moc_csvplaylistformat.cpp + +compiler_rcc_make_all: +compiler_rcc_clean: +compiler_image_collection_make_all: qmake_image_collection.cpp +compiler_image_collection_clean: + -$(DEL_FILE) qmake_image_collection.cpp +compiler_moc_source_make_all: +compiler_moc_source_clean: +compiler_uic_make_all: +compiler_uic_clean: +compiler_yacc_decl_make_all: +compiler_yacc_decl_clean: +compiler_yacc_impl_make_all: +compiler_yacc_impl_clean: +compiler_lex_make_all: +compiler_lex_clean: +compiler_clean: compiler_moc_header_clean + +####### Compile + +csvplaylistformat.o: csvplaylistformat.cpp csvplaylistformat.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o csvplaylistformat.o csvplaylistformat.cpp + +mediafile.o: ../../../qmmp/src/mediafile.cpp ../../../qmmp/src/mediafile.h + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o mediafile.o ../../../qmmp/src/mediafile.cpp + +moc_csvplaylistformat.o: moc_csvplaylistformat.cpp + $(CXX) -c $(CXXFLAGS) $(INCPATH) -o moc_csvplaylistformat.o moc_csvplaylistformat.cpp + +####### Install + +install: FORCE + +uninstall: FORCE + +FORCE: + diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.cpp b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.cpp new file mode 100644 index 000000000..bff8a20b5 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.cpp @@ -0,0 +1,72 @@ +#include <QtPlugin> +#include <QFileInfo> + +#include "csvplaylistformat.h" +#include <mediafile.h> + + +bool CSVPlaylistFormat::hasFormat(const QString & f) +{ + foreach(QString s,m_supported_formats) + if(f == s) + return true; + + return false; +} + +QStringList CSVPlaylistFormat::getExtensions() const +{ + return m_supported_formats; +} + +CSVPlaylistFormat::CSVPlaylistFormat() +{ + m_supported_formats << "csv"; +} + +QString CSVPlaylistFormat::name() const +{ + return "CSVPlaylistFormat"; +} + + +QStringList CSVPlaylistFormat::decode(const QString & contents) +{ + qWarning("CONTENTS: %s",qPrintable(contents)); + QStringList out; + QStringList splitted = contents.split("\n"); + if(!splitted.isEmpty()) + { + foreach(QString str, splitted) + { + QStringList song = str.split(";"); + qWarning("SONG: %s",qPrintable(song[0])); + if(song.count() > 1) + { + QString unverified = song[1]; + if(QFileInfo(unverified).exists()) + out << QFileInfo(unverified).absoluteFilePath(); + else + qWarning("File %s does not exist",unverified.toLocal8Bit().data()); + } + + } + return out; + } + else + qWarning("Error parsing CSV playlist format"); + + return QStringList(); +} + +QString CSVPlaylistFormat::encode(const QList< MediaFile * > & contents) +{ + QStringList out; + foreach(MediaFile* f,contents) + out.append(f->title() + ";" + f->path() + ";" + QString::number(f->length())); + + return out.join("\n"); +} + +Q_EXPORT_PLUGIN(CSVPlaylistFormat) + diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.h b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.h new file mode 100644 index 000000000..45e93d346 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/csvplaylistformat.h @@ -0,0 +1,33 @@ +#ifndef CSVPLAYLISTFORMAT_H +#define CSVPLAYLISTFORMAT_H + +#include <playlistformat.h> + +#include <QStringList> + +class MediaFile; + +/*! + * Example of custom playlist CSV semicolon separated format. + * Each line represents a song in the next format: + * TITLE;FILEPATH;DURATION + */ + +class CSVPlaylistFormat : public QObject, public PlaylistFormat +{ +Q_OBJECT +Q_INTERFACES(PlaylistFormat) +public: + CSVPlaylistFormat(); + virtual ~CSVPlaylistFormat(){;} + virtual QStringList decode(const QString& contents); + virtual QString encode(const QList<MediaFile*>& contents); + virtual QStringList getExtensions()const; + virtual bool hasFormat(const QString& ext); + virtual QString name()const; +protected: + QStringList m_supported_formats; +}; + +#endif //CSVPLAYLISTFORMAT_H + diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/moc_csvplaylistformat.cpp b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/moc_csvplaylistformat.cpp new file mode 100644 index 000000000..133bf4692 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/moc_csvplaylistformat.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** Meta object code from reading C++ file 'csvplaylistformat.h' +** +** Created: Mon Dec 24 01:59:05 2007 +** by: The Qt Meta Object Compiler version 59 (Qt 4.3.0) +** +** WARNING! All changes made in this file will be lost! +*****************************************************************************/ + +#include "csvplaylistformat.h" +#if !defined(Q_MOC_OUTPUT_REVISION) +#error "The header file 'csvplaylistformat.h' doesn't include <QObject>." +#elif Q_MOC_OUTPUT_REVISION != 59 +#error "This file was generated using the moc from 4.3.0. It" +#error "cannot be used with the include files from this version of Qt." +#error "(The moc has changed too much.)" +#endif + +static const uint qt_meta_data_CSVPlaylistFormat[] = { + + // content: + 1, // revision + 0, // classname + 0, 0, // classinfo + 0, 0, // methods + 0, 0, // properties + 0, 0, // enums/sets + + 0 // eod +}; + +static const char qt_meta_stringdata_CSVPlaylistFormat[] = { + "CSVPlaylistFormat\0" +}; + +const QMetaObject CSVPlaylistFormat::staticMetaObject = { + { &QObject::staticMetaObject, qt_meta_stringdata_CSVPlaylistFormat, + qt_meta_data_CSVPlaylistFormat, 0 } +}; + +const QMetaObject *CSVPlaylistFormat::metaObject() const +{ + return &staticMetaObject; +} + +void *CSVPlaylistFormat::qt_metacast(const char *_clname) +{ + if (!_clname) return 0; + if (!strcmp(_clname, qt_meta_stringdata_CSVPlaylistFormat)) + return static_cast<void*>(const_cast< CSVPlaylistFormat*>(this)); + if (!strcmp(_clname, "PlaylistFormat")) + return static_cast< PlaylistFormat*>(const_cast< CSVPlaylistFormat*>(this)); + if (!strcmp(_clname, "PlaylistFormatInterface/1.0")) + return static_cast< PlaylistFormat*>(const_cast< CSVPlaylistFormat*>(this)); + return QObject::qt_metacast(_clname); +} + +int CSVPlaylistFormat::qt_metacall(QMetaObject::Call _c, int _id, void **_a) +{ + _id = QObject::qt_metacall(_c, _id, _a); + if (_id < 0) + return _id; + return _id; +} diff --git a/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/readme b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/readme new file mode 100644 index 000000000..a047e358b --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/CSVPlaylistFormat/readme @@ -0,0 +1,12 @@ +This is simple example of custom Playlist Format plugin. +It represents CVS semicolon-separated format storage. +This plugin developed mostly for QMMP customizing demonstration. + +Build: Change QMMPROOT variable in .pro file according your +QMMP build tree location.Then qmake && make + +Install: copy library into ~/.qmmp/plugins/PlaylistFormats directory. + + + vovanec@gmail.com + diff --git a/src/plugins/Misc/PlaylistFormats/Makefile b/src/plugins/Misc/PlaylistFormats/Makefile new file mode 100644 index 000000000..cbf632042 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/Makefile @@ -0,0 +1,112 @@ +############################################################################# +# Makefile for building: PlaylistFormats +# Generated by qmake (2.01a) (Qt 4.3.0) on: Mon Dec 24 01:59:00 2007 +# Project: PlaylistFormats.pro +# Template: subdirs +# Command: /usr/local/Trolltech/Qt-4.3.0/bin/qmake -unix -o Makefile PlaylistFormats.pro +############################################################################# + +first: make_default +MAKEFILE = Makefile +QMAKE = /usr/local/Trolltech/Qt-4.3.0/bin/qmake +DEL_FILE = rm -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p +COPY = cp -f +COPY_FILE = $(COPY) +COPY_DIR = $(COPY) -r +INSTALL_FILE = install -m 644 -p +INSTALL_PROGRAM = install -m 755 -p +INSTALL_DIR = $(COPY_DIR) +DEL_FILE = rm -f +SYMLINK = ln -sf +DEL_DIR = rmdir +MOVE = mv -f +CHK_DIR_EXISTS= test -d +MKDIR = mkdir -p +SUBTARGETS = \ + sub-CSVPlaylistFormat + +CSVPlaylistFormat//$(MAKEFILE): + @$(CHK_DIR_EXISTS) CSVPlaylistFormat/ || $(MKDIR) CSVPlaylistFormat/ + cd CSVPlaylistFormat/ && $(QMAKE) CSVPlaylistFormat.pro -unix -o $(MAKEFILE) +sub-CSVPlaylistFormat-qmake_all: FORCE + @$(CHK_DIR_EXISTS) CSVPlaylistFormat/ || $(MKDIR) CSVPlaylistFormat/ + cd CSVPlaylistFormat/ && $(QMAKE) CSVPlaylistFormat.pro -unix -o $(MAKEFILE) +sub-CSVPlaylistFormat: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) +sub-CSVPlaylistFormat-make_default: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) +sub-CSVPlaylistFormat-make_first: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) first +sub-CSVPlaylistFormat-all: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) all +sub-CSVPlaylistFormat-clean: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) clean +sub-CSVPlaylistFormat-distclean: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) distclean +sub-CSVPlaylistFormat-install_subtargets: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) install +sub-CSVPlaylistFormat-uninstall_subtargets: CSVPlaylistFormat//$(MAKEFILE) FORCE + cd CSVPlaylistFormat/ && $(MAKE) -f $(MAKEFILE) uninstall + +Makefile: PlaylistFormats.pro /usr/local/Trolltech/Qt-4.3.0/mkspecs/linux-g++/qmake.conf /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf \ + /usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf + $(QMAKE) -unix -o Makefile PlaylistFormats.pro +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/g++.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/unix.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/common/linux.conf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/qconfig.pri: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_functions.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt_config.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/exclusive_builds.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_pre.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/release.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/default_post.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/warn_on.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/qt.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/unix/thread.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/moc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/resources.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/uic.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/yacc.prf: +/usr/local/Trolltech/Qt-4.3.0/mkspecs/features/lex.prf: +qmake: qmake_all FORCE + @$(QMAKE) -unix -o Makefile PlaylistFormats.pro + +qmake_all: sub-CSVPlaylistFormat-qmake_all FORCE + +make_default: sub-CSVPlaylistFormat-make_default FORCE +make_first: sub-CSVPlaylistFormat-make_first FORCE +all: sub-CSVPlaylistFormat-all FORCE +clean: sub-CSVPlaylistFormat-clean FORCE +distclean: sub-CSVPlaylistFormat-distclean FORCE + -$(DEL_FILE) Makefile +install_subtargets: sub-CSVPlaylistFormat-install_subtargets FORCE +uninstall_subtargets: sub-CSVPlaylistFormat-uninstall_subtargets FORCE + +mocclean: compiler_moc_header_clean compiler_moc_source_clean + +mocables: compiler_moc_header_make_all compiler_moc_source_make_all +install: install_subtargets FORCE + +uninstall: uninstall_subtargets FORCE + +FORCE: + diff --git a/src/plugins/Misc/PlaylistFormats/PlaylistFormats.pro b/src/plugins/Misc/PlaylistFormats/PlaylistFormats.pro new file mode 100644 index 000000000..4235b3190 --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/PlaylistFormats.pro @@ -0,0 +1,2 @@ +TEMPLATE = subdirs +SUBDIRS = CSVPlaylistFormat
\ No newline at end of file diff --git a/src/plugins/Misc/PlaylistFormats/libCSVPlaylistFormat.so b/src/plugins/Misc/PlaylistFormats/libCSVPlaylistFormat.so Binary files differnew file mode 100755 index 000000000..f9677157c --- /dev/null +++ b/src/plugins/Misc/PlaylistFormats/libCSVPlaylistFormat.so diff --git a/src/plugins/Output/CMakeLists.txt b/src/plugins/Output/CMakeLists.txt new file mode 100644 index 000000000..864d945c1 --- /dev/null +++ b/src/plugins/Output/CMakeLists.txt @@ -0,0 +1,24 @@ +SET(USE_ALSA TRUE CACHE BOOL "enable/disable alsa plugin") +SET(USE_JACK TRUE CACHE BOOL "enable/disable jack plugin") +SET(USE_OSS TRUE CACHE BOOL "enable/disable oss plugin") + +IF(USE_ALSA) +MESSAGE( STATUS "ALSA ON") +add_subdirectory(alsa) +ELSE(USE_ALSA) +MESSAGE( STATUS "ALSA OFF") +ENDIF(USE_ALSA) + +IF(USE_JACK) +MESSAGE( STATUS "JACK ON") +add_subdirectory(jack) +ELSE(USE_JACK) +MESSAGE( STATUS "JACK OFF") +ENDIF(USE_JACK) + +IF(USE_OSS) +MESSAGE( STATUS "OSS ON") +add_subdirectory(oss) +ELSE(USE_OSS) +MESSAGE( STATUS "OSS OFF") +ENDIF(USE_OSS)
\ No newline at end of file diff --git a/src/plugins/Output/Output.pro b/src/plugins/Output/Output.pro new file mode 100644 index 000000000..eed8b9f14 --- /dev/null +++ b/src/plugins/Output/Output.pro @@ -0,0 +1,26 @@ +# ???? ?????? ? KDevelop ?????????? qmake. +# ------------------------------------------- +# ?????????? ???????????? ???????? ???????? ???????: ./Plugins/Output +# ???? - ?????? ? ????????????? + +include(../../../qmmp.pri) + +CONFIG += release warn_on +TEMPLATE = subdirs + +SUBDIRS += alsa + +contains(CONFIG, JACK_PLUGIN){ + SUBDIRS += jack + message(***********************) + message(* JACK plugin enabled *) + message(***********************) +} + +contains(CONFIG, OSS_PLUGIN){ + SUBDIRS += oss + message(**********************) + message(* OSS plugin enabled *) + message(**********************) +} + 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 <forkotov02@hotmail.ru></source> + <translation>Autor: Ilja Kotov <forkotov02@hotmail.ru></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 Binary files differnew file mode 100644 index 000000000..78ed38962 --- /dev/null +++ b/src/plugins/Output/alsa/translations/alsa_plugin_ru.qm 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 <forkotov02@hotmail.ru></source> + <translation>Разработчик: Илья Котов <forkotov02@hotmail.ru></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> diff --git a/src/plugins/Output/jack/CMakeLists.txt b/src/plugins/Output/jack/CMakeLists.txt new file mode 100644 index 000000000..665a6fb13 --- /dev/null +++ b/src/plugins/Output/jack/CMakeLists.txt @@ -0,0 +1,74 @@ +project(libjack) + +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}/../../../) + +# libjack and taglib +PKGCONFIG(jack JACK_INCLUDE_DIR JACK_LINK_DIR JACK_LINK_FLAGS JACK_CFLAGS) +PKGCONFIG(samplerate SAMPLERATE_INCLUDE_DIR SAMPLERATE_LINK_DIR SAMPLERATE_LINK_FLAGS SAMPLERATE_CFLAGS) + + +IF(NOT JACK_LINK_FLAGS) + SET(JACK_LINK_FLAGS -ljack -lrt) +ENDIF(NOT JACK_LINK_FLAGS) + +IF(NOT SAMPLERATE_LINK_FLAGS) + SET(SAMPLERATE_LINK_FLAGS -lsamplerate) +ENDIF(NOT SAMPLERATE_LINK_FLAGS) + +include_directories(${JACK_INCLUDE_DIR} ${JACK_INCLUDE_DIR}) +link_directories(${SAMPLERATE_LINK_DIR} ${SAMPLERATE_LINK_DIR}) + +ADD_DEFINITIONS(${JACK_CFLAGS}) +ADD_DEFINITIONS(${SAMPLERATE_CFLAGS}) + + +SET(libjack_SRCS + outputjackfactory.cpp + outputjack.cpp + bio2jack.c +) + +SET(libjack_MOC_HDRS + outputjackfactory.h + outputjack.h + bio2jack.h +) + +SET(libjack_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libjack_RCC_SRCS ${libjack_RCCS}) + +QT4_WRAP_CPP(libjack_MOC_SRCS ${libjack_MOC_HDRS}) + + + +ADD_LIBRARY(jack SHARED ${libjack_SRCS} ${libjack_MOC_SRCS} ${libjack_RCC_SRCS}) +target_link_libraries(jack ${QT_LIBRARIES} -lqmmp ${JACK_LINK_FLAGS} ${SAMPLERATE_LINK_FLAGS}) +install(TARGETS jack 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/jack/bio2jack.c b/src/plugins/Output/jack/bio2jack.c new file mode 100644 index 000000000..aef7ea9b7 --- /dev/null +++ b/src/plugins/Output/jack/bio2jack.c @@ -0,0 +1,2635 @@ +/* + * Copyright 2003-2006 Chris Morgan <cmorgan@alum.wpi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* NOTE: All functions that take a jack_driver_t* do NOT lock the device, in order to get a */ +/* jack_driver_t* you must call getDriver() which will pthread_mutex_lock() */ + +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <math.h> +#include <unistd.h> +#include <inttypes.h> +#include <jack/jack.h> +#include <jack/ringbuffer.h> +#include <pthread.h> +#include <sys/time.h> +#include <samplerate.h> + +#include "bio2jack.h" + +/* enable/disable TRACING through the JACK_Callback() function */ +/* this can sometimes be too much information */ +#define TRACE_CALLBACK 0 + +/* set to 1 for verbose output */ +#define VERBOSE_OUTPUT 0 + +/* set to 1 to enable debug messages */ +#define DEBUG_OUTPUT 0 + +/* set to 1 to enable tracing */ +#define TRACE_ENABLE 0 + +/* set to 1 to enable the function timers */ +#define TIMER_ENABLE 0 + +/* set to 1 to enable tracing of getDriver() and releaseDriver() */ +#define TRACE_getReleaseDevice 0 + +#define ENABLE_WARNINGS 0 + +#define DEFAULT_RB_SIZE 4096 + +#define OUTFILE stderr + +#if TIMER_ENABLE +/* This seemingly construct makes timing arbitrary functions really easy + all you have to do is place a 'TIMER("start\n")' at the beginning and + a 'TIMER("stop\n")' at the end of any function and this does the rest + (naturally you can place any printf-compliant text you like in the argument + along with the associated values). */ +static struct timeval timer_now; +#define TIMER(format,args...) gettimeofday(&timer_now,0); \ + fprintf(OUTFILE, "%ld.%06ld: %s::%s(%d) "format, timer_now.tv_sec, timer_now.tv_usec, __FILE__, __FUNCTION__, __LINE__, ##args) +#else +#define TIMER(...) +#endif + +#if TRACE_ENABLE +#define TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \ + fflush(OUTFILE); +#else +#define TRACE(...) +#endif + +#if DEBUG_OUTPUT +#define DEBUG(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \ + fflush(OUTFILE); +#else +#define DEBUG(...) +#endif + +#if TRACE_CALLBACK +#define CALLBACK_TRACE(format,args...) fprintf(OUTFILE, "%s::%s(%d) "format, __FILE__, __FUNCTION__, __LINE__,##args); \ + fflush(OUTFILE); +#else +#define CALLBACK_TRACE(...) +#endif + +#if ENABLE_WARNINGS +#define WARN(format,args...) fprintf(OUTFILE, "WARN: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args); \ + fflush(OUTFILE); +#else +#define WARN(...) +#endif + +#define ERR(format,args...) fprintf(OUTFILE, "ERR: %s::%s(%d) "format, __FILE__,__FUNCTION__,__LINE__,##args); \ + fflush(OUTFILE); + +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#define max(a,b) (((a) < (b)) ? (b) : (a)) + +#define MAX_OUTPUT_PORTS 10 +#define MAX_INPUT_PORTS 10 + +typedef struct jack_driver_s +{ + bool allocated; /* whether or not this device has been allocated */ + + int deviceID; /* id of this device */ + int clientCtr; /* to prevent overlapping client ids */ + long jack_sample_rate; /* jack samples(frames) per second */ + + long client_sample_rate; /* client samples(frames) per second */ + double output_sample_rate_ratio; /* ratio between jack's output rate & ours */ + double input_sample_rate_ratio; /* ratio between our input rate & jack's */ + + unsigned long num_input_channels; /* number of input channels(1 is mono, 2 stereo etc..) */ + unsigned long num_output_channels; /* number of output channels(1 is mono, 2 stereo etc..) */ + + unsigned long bits_per_channel; /* number of bits per channel (only 8 & 16 are currently supported) */ + + unsigned long bytes_per_output_frame; /* (num_output_channels * bits_per_channel) / 8 */ + unsigned long bytes_per_input_frame; /* (num_input_channels * bits_per_channel) / 8 */ + + unsigned long bytes_per_jack_output_frame; /* (num_output_channels * bits_per_channel) / 8 */ + unsigned long bytes_per_jack_input_frame; /* (num_input_channels * bits_per_channel) / 8 */ + + unsigned long latencyMS; /* latency in ms between writing and actual audio output of the written data */ + + long clientBytesInJack; /* number of INPUT bytes(from the client of bio2jack) we wrote to jack(not necessary the number of bytes we wrote to jack) */ + long jack_buffer_size; /* size of the buffer jack will pass in to the process callback */ + + unsigned long callback_buffer1_size; /* number of bytes in the buffer allocated for processing data in JACK_Callback */ + char *callback_buffer1; + unsigned long callback_buffer2_size; /* number of bytes in the buffer allocated for processing data in JACK_Callback */ + char *callback_buffer2; + + unsigned long rw_buffer1_size; /* number of bytes in the buffer allocated for processing data in JACK_(Read|Write) */ + char *rw_buffer1; + + struct timeval previousTime; /* time of last JACK_Callback() write to jack, allows for MS accurate bytes played */ + + unsigned long written_client_bytes; /* input bytes we wrote to jack, not necessarily actual bytes we wrote to jack due to channel and other conversion */ + unsigned long played_client_bytes; /* input bytes that jack has played */ + + unsigned long client_bytes; /* total bytes written by the client of bio2jack via JACK_Write() */ + + jack_port_t *output_port[MAX_OUTPUT_PORTS]; /* output ports */ + jack_port_t *input_port[MAX_OUTPUT_PORTS]; /* input ports */ + + jack_client_t *client; /* pointer to jack client */ + + char **jack_port_name; /* user given strings for the port names, can be NULL */ + unsigned int jack_port_name_count; /* the number of port names given */ + + unsigned long jack_output_port_flags; /* flags to be passed to jack when opening the output ports */ + unsigned long jack_input_port_flags; /* flags to be passed to jack when opening the output ports */ + + jack_ringbuffer_t *pPlayPtr; /* the playback ringbuffer */ + jack_ringbuffer_t *pRecPtr; /* the recording ringbuffer */ + + SRC_STATE *output_src; /* SRC object for the output stream */ + SRC_STATE *input_src; /* SRC object for the output stream */ + + enum status_enum state; /* one of PLAYING, PAUSED, STOPPED, CLOSED, RESET etc */ + + unsigned int volume[MAX_OUTPUT_PORTS]; /* percentage of sample value to preserve, 100 would be no attenuation */ + enum JACK_VOLUME_TYPE volumeEffectType; /* linear or dbAttenuation, if dbAttenuation volume is the number of dBs of + attenuation to apply, 0 volume being no attenuation, full volume */ + + long position_byte_offset; /* an offset that we will apply to returned position queries to achieve */ + /* the position that the user of the driver desires set */ + + bool in_use; /* true if this device is currently in use */ + + pthread_mutex_t mutex; /* mutex to lock this specific device */ + + /* variables used for trying to restart the connection to jack */ + bool jackd_died; /* true if jackd has died and we should try to restart it */ + struct timeval last_reconnect_attempt; +} jack_driver_t; + + +static char *client_name; /* the name bio2jack will use when creating a new + jack client. client_name_%deviceID% will be used */ + + +static bool do_sample_rate_conversion; /* whether the client has requested sample rate conversion, + default to on for improved compatibility */ + +/* + Which SRC converter function we should use when doing sample rate conversion. + Default to the fastest of the 'good quality' set. + */ +static int preferred_src_converter = SRC_SINC_FASTEST; + +static bool init_done = 0; /* just to prevent clients from calling JACK_Init twice, that would be very bad */ + +static enum JACK_PORT_CONNECTION_MODE port_connection_mode = CONNECT_ALL; + +/* enable/disable code that allows us to close a device without actually closing the jack device */ +/* this works around the issue where jack doesn't always close devices by the time the close function call returns */ +#define JACK_CLOSE_HACK 1 + +typedef jack_default_audio_sample_t sample_t; +typedef jack_nframes_t nframes_t; + +/* allocate devices for output */ +/* if you increase this past 10, you might want to update 'out_client_name = ... ' in JACK_OpenDevice */ +#define MAX_OUTDEVICES 10 +static jack_driver_t outDev[MAX_OUTDEVICES]; + +static pthread_mutex_t device_mutex = PTHREAD_MUTEX_INITIALIZER; /* this is to lock the entire outDev array + to make managing it in a threaded + environment sane */ + +#if JACK_CLOSE_HACK +static void JACK_CloseDevice(jack_driver_t * drv, bool close_client); +#else +static void JACK_CloseDevice(jack_driver_t * drv); +#endif + + +/* Prototypes */ +static int JACK_OpenDevice(jack_driver_t * drv); +static unsigned long JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv); +static void JACK_ResetFromDriver(jack_driver_t * drv); +static long JACK_GetPositionFromDriver(jack_driver_t * drv, + enum pos_enum position, int type); +static void JACK_CleanupDriver(jack_driver_t * drv); + + +/* Return the difference between two timeval structures in terms of milliseconds */ +long +TimeValDifference(struct timeval *start, struct timeval *end) +{ + double long ms; /* milliseconds value */ + + ms = end->tv_sec - start->tv_sec; /* compute seconds difference */ + ms *= (double) 1000; /* convert to milliseconds */ + + ms += (double) (end->tv_usec - start->tv_usec) / (double) 1000; /* add on microseconds difference */ + + return (long) ms; +} + +/* get a device and lock the devices mutex */ +/* */ +/* also attempt to reconnect to jack since this function is called from */ +/* most other bio2jack functions it provides a good point to attempt reconnection */ +/* */ +/* Ok, I know this looks complicated and it kind of is. The point is that when you're + trying to trace mutexes it's more important to know *who* called us than just that + we were called. This uses from pre-processor trickery so that the fprintf is actually + placed in the function making the getDriver call. Thus, the __FUNCTION__ and __LINE__ + macros will actually reference our caller, rather than getDriver. The reason the + fprintf call is passes as a parameter is because this macro has to still return a + jack_driver_t* and we want to log both before *and* after the getDriver call for + easier detection of blocked calls. + */ +#if TRACE_getReleaseDevice +#define getDriver(x) _getDriver(x,fprintf(OUTFILE, "%s::%s(%d) getting driver %d\n", __FILE__, __FUNCTION__, __LINE__,x)); TRACE("got driver %d\n",x); +jack_driver_t * +_getDriver(int deviceID, int ignored) +{ + fflush(OUTFILE); +#else +jack_driver_t * +getDriver(int deviceID) +{ +#endif + jack_driver_t *drv = &outDev[deviceID]; + + if(pthread_mutex_lock(&drv->mutex) != 0) + ERR("lock returned an error\n"); + + /* should we try to restart the jack server? */ + if(drv->jackd_died && drv->client == 0) + { + struct timeval now; + gettimeofday(&now, 0); + + /* wait 250ms before trying again */ + if(TimeValDifference(&drv->last_reconnect_attempt, &now) >= 250) + { + JACK_OpenDevice(drv); + drv->last_reconnect_attempt = now; + } + } + + return drv; +} + +#if TRACE_getReleaseDevice +#define tryGetDriver(x) _tryGetDriver(x,fprintf(OUTFILE, "%s::%s(%d) trying to get driver %d\n", __FILE__, __FUNCTION__, __LINE__,x)); TRACE("got driver %d\n",x); +jack_driver_t * +_tryGetDriver(int deviceID, int ignored) +{ + fflush(OUTFILE); +#else +jack_driver_t * +tryGetDriver(int deviceID) +{ +#endif + jack_driver_t *drv = &outDev[deviceID]; + + int err; + if((err = pthread_mutex_trylock(&drv->mutex)) == 0) + return drv; + + if(err == EBUSY) + { + TRACE("driver %d is busy\n",deviceID); + return 0; + } + + ERR("lock returned an error\n"); + return 0; +} + + +/* release a device's mutex */ +/* */ +/* This macro is similar to the one for getDriver above, only simpler since we only + really need to know when the lock was release for the sake of debugging. +*/ +#if TRACE_getReleaseDevice +#define releaseDriver(x) TRACE("releasing driver %d\n",x->deviceID); _releaseDriver(x); +void +_releaseDriver(jack_driver_t * drv) +#else +void +releaseDriver(jack_driver_t * drv) +#endif +{ + /* + #if TRACE_getReleaseDevice + TRACE("deviceID == %d\n", drv->deviceID); + #endif + */ + if(pthread_mutex_unlock(&drv->mutex) != 0) + ERR("lock returned an error\n"); +} + + +/* Return a string corresponding to the input state */ +char * +DEBUGSTATE(enum status_enum state) +{ + if(state == PLAYING) + return "PLAYING"; + else if(state == PAUSED) + return "PAUSED"; + else if(state == STOPPED) + return "STOPPED"; + else if(state == CLOSED) + return "CLOSED"; + else if(state == RESET) + return "RESET"; + else + return "unknown state"; +} + + +#define SAMPLE_MAX_16BIT 32767.0f +#define SAMPLE_MAX_8BIT 255.0f + +/* floating point volume routine */ +/* volume should be a value between 0.0 and 1.0 */ +static void +float_volume_effect(sample_t * buf, unsigned long nsamples, float volume, + int skip) +{ + if(volume < 0) + volume = 0; + if(volume > 1.0) + volume = 1.0; + + while(nsamples--) + { + *buf = (*buf) * volume; + buf += skip; + } +} + +/* place one channel into a multi-channel stream */ +static inline void +mux(sample_t * dst, sample_t * src, unsigned long nsamples, + unsigned long dst_skip) +{ + /* ALERT: signed sign-extension portability !!! */ + while(nsamples--) + { + *dst = *src; + dst += dst_skip; + src++; + } +} + +/* pull one channel out of a multi-channel stream */ +static void +demux(sample_t * dst, sample_t * src, unsigned long nsamples, + unsigned long src_skip) +{ + /* ALERT: signed sign-extension portability !!! */ + while(nsamples--) + { + *dst = *src; + dst++; + src += src_skip; + } +} + +/* convert from 16 bit to floating point */ +static inline void +sample_move_short_float(sample_t * dst, short *src, unsigned long nsamples) +{ + /* ALERT: signed sign-extension portability !!! */ + unsigned long i; + for(i = 0; i < nsamples; i++) + dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_16BIT; +} + +/* convert from floating point to 16 bit */ +static inline void +sample_move_float_short(short *dst, sample_t * src, unsigned long nsamples) +{ + /* ALERT: signed sign-extension portability !!! */ + unsigned long i; + for(i = 0; i < nsamples; i++) + dst[i] = (short) ((src[i]) * SAMPLE_MAX_16BIT); +} + +/* convert from 8 bit to floating point */ +static inline void +sample_move_char_float(sample_t * dst, unsigned char *src, unsigned long nsamples) +{ + /* ALERT: signed sign-extension portability !!! */ + unsigned long i; + for(i = 0; i < nsamples; i++) + dst[i] = (sample_t) (src[i]) / SAMPLE_MAX_8BIT; +} + +/* convert from floating point to 8 bit */ +static inline void +sample_move_float_char(unsigned char *dst, sample_t * src, unsigned long nsamples) +{ + /* ALERT: signed sign-extension portability !!! */ + unsigned long i; + for(i = 0; i < nsamples; i++) + dst[i] = (char) ((src[i]) * SAMPLE_MAX_8BIT); +} + +/* fill dst buffer with nsamples worth of silence */ +static inline void +sample_silence_float(sample_t * dst, unsigned long nsamples) +{ + /* ALERT: signed sign-extension portability !!! */ + while(nsamples--) + { + *dst = 0; + dst++; + } +} + +static inline bool +ensure_buffer_size(char **buffer, unsigned long *cur_size, + unsigned long needed_size) +{ + DEBUG("current size = %lu, needed size = %lu\n", *cur_size, needed_size); + if(*cur_size >= needed_size) + return TRUE; + DEBUG("reallocing\n"); + char *tmp = realloc(*buffer, needed_size); + if(tmp) + { + *cur_size = needed_size; + *buffer = tmp; + return TRUE; + } + DEBUG("reallocing failed\n"); + return FALSE; +} + +/****************************************************************** + * JACK_callback + * + * every time the jack server wants something from us it calls this + * function, so we either deliver it some sound to play or deliver it nothing + * to play + */ +static int +JACK_callback(nframes_t nframes, void *arg) +{ + jack_driver_t *drv = (jack_driver_t *) arg; + + unsigned int i; + int src_error = 0; + + TIMER("start\n"); + gettimeofday(&drv->previousTime, 0); /* record the current time */ + + CALLBACK_TRACE("nframes %ld, sizeof(sample_t) == %d\n", (long) nframes, + sizeof(sample_t)); + + if(!drv->client) + ERR("client is closed, this is weird...\n"); + + sample_t *out_buffer[MAX_OUTPUT_PORTS]; + /* retrieve the buffers for the output ports */ + for(i = 0; i < drv->num_output_channels; i++) + out_buffer[i] = (sample_t *) jack_port_get_buffer(drv->output_port[i], nframes); + + sample_t *in_buffer[MAX_INPUT_PORTS]; + /* retrieve the buffers for the input ports */ + for(i = 0; i < drv->num_input_channels; i++) + in_buffer[i] = (sample_t *) jack_port_get_buffer(drv->input_port[i], nframes); + + /* handle playing state */ + if(drv->state == PLAYING) + { + /* handle playback data, if any */ + if(drv->num_output_channels > 0) + { + unsigned long jackFramesAvailable = nframes; /* frames we have left to write to jack */ + unsigned long numFramesToWrite; /* num frames we are writing */ + size_t inputBytesAvailable = jack_ringbuffer_read_space(drv->pPlayPtr); + unsigned long inputFramesAvailable; /* frames we have available */ + + inputFramesAvailable = inputBytesAvailable / drv->bytes_per_jack_output_frame; + size_t jackBytesAvailable = jackFramesAvailable * drv->bytes_per_jack_output_frame; + + long read = 0; + + CALLBACK_TRACE("playing... jackFramesAvailable = %ld inputFramesAvailable = %ld\n", + jackFramesAvailable, inputFramesAvailable); + +#if JACK_CLOSE_HACK + if(drv->in_use == FALSE) + { + /* output silence if nothing is being outputted */ + for(i = 0; i < drv->num_output_channels; i++) + sample_silence_float(out_buffer[i], nframes); + + return -1; + } +#endif + + /* make sure our buffer is large enough for the data we are writing */ + /* ie. callback_buffer2_size < (bytes we already wrote + bytes we are going to write in this loop) */ + if(!ensure_buffer_size + (&drv->callback_buffer2, &drv->callback_buffer2_size, + jackBytesAvailable)) + { + ERR("allocated %lu bytes, need %lu bytes\n", + drv->callback_buffer2_size, (unsigned long)jackBytesAvailable); + return -1; + } + + /* do sample rate conversion if needed & requested */ + if(drv->output_src && drv->output_sample_rate_ratio != 1.0) + { + long bytes_needed_write = nframes * drv->bytes_per_jack_output_frame; + + /* make a very good guess at how many raw bytes we'll need to satisfy jack's request after conversion */ + long bytes_needed_read = min(inputBytesAvailable, + (double) (bytes_needed_write + + drv-> + output_sample_rate_ratio + * + drv-> + bytes_per_jack_output_frame) + / drv->output_sample_rate_ratio); + DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n", + bytes_needed_read, bytes_needed_write, + drv->output_sample_rate_ratio); + + if(!ensure_buffer_size(&drv->callback_buffer1, + &drv->callback_buffer1_size, + bytes_needed_read)) + { + ERR("could not realloc callback_buffer2!\n"); + return 1; + } + if(!ensure_buffer_size(&drv->callback_buffer2, + &drv->callback_buffer2_size, + bytes_needed_write)) + { + ERR("could not realloc callback_buffer2!\n"); + return 1; + } + + if(jackFramesAvailable && inputBytesAvailable > 0) + { + /* read in the data, but don't move the read pointer until we know how much SRC used */ + jack_ringbuffer_peek(drv->pPlayPtr, drv->callback_buffer1, + bytes_needed_read); + + SRC_DATA srcdata; + srcdata.data_in = (sample_t *) drv->callback_buffer1; + srcdata.input_frames = bytes_needed_read / drv->bytes_per_jack_output_frame; + srcdata.src_ratio = drv->output_sample_rate_ratio; + srcdata.data_out = (sample_t *) drv->callback_buffer2; + srcdata.output_frames = nframes; + srcdata.end_of_input = 0; // it's a stream, it never ends + DEBUG("input_frames = %ld, output_frames = %ld\n", + srcdata.input_frames, srcdata.output_frames); + /* convert the sample rate */ + src_error = src_process(drv->output_src, &srcdata); + DEBUG("used = %ld, generated = %ld, error = %d: %s.\n", + srcdata.input_frames_used, srcdata.output_frames_gen, + src_error, src_strerror(src_error)); + + if(src_error == 0) + { + /* now we can move the read pointer */ + jack_ringbuffer_read_advance(drv->pPlayPtr, + srcdata. + input_frames_used * + drv->bytes_per_jack_output_frame); + /* add on what we wrote */ + read = srcdata.input_frames_used * drv->bytes_per_output_frame; + jackFramesAvailable -= srcdata.output_frames_gen; /* take away what was used */ + } + } + } + else /* no resampling needed or requested */ + { + /* read as much data from the buffer as is available */ + if(jackFramesAvailable && inputBytesAvailable > 0) + { + /* write as many bytes as we have space remaining, or as much as we have data to write */ + numFramesToWrite = min(jackFramesAvailable, inputFramesAvailable); + jack_ringbuffer_read(drv->pPlayPtr, drv->callback_buffer2, + jackBytesAvailable); + /* add on what we wrote */ + read = numFramesToWrite * drv->bytes_per_output_frame; + jackFramesAvailable -= numFramesToWrite; /* take away what was written */ + } + } + + drv->written_client_bytes += read; + drv->played_client_bytes += drv->clientBytesInJack; /* move forward by the previous bytes we wrote since those must have finished by now */ + drv->clientBytesInJack = read; /* record the input bytes we wrote to jack */ + + /* see if we still have jackBytesLeft here, if we do that means that we + ran out of wave data to play and had a buffer underrun, fill in + the rest of the space with zero bytes so at least there is silence */ + if(jackFramesAvailable) + { + WARN("buffer underrun of %ld frames\n", jackFramesAvailable); + for(i = 0; i < drv->num_output_channels; i++) + sample_silence_float(out_buffer[i] + + (nframes - jackFramesAvailable), + jackFramesAvailable); + } + + /* if we aren't converting or we are converting and src_error == 0 then we should */ + /* apply volume and demux */ + if(!(drv->output_src && drv->output_sample_rate_ratio != 1.0) || (src_error == 0)) + { + /* apply volume */ + for(i = 0; i < drv->num_output_channels; i++) + { + if(drv->volumeEffectType == dbAttenuation) + { + /* assume the volume setting is dB of attenuation, a volume of 0 */ + /* is 0dB attenuation */ + float volume = powf(10.0, -((float) drv->volume[i]) / 20.0); + float_volume_effect((sample_t *) drv->callback_buffer2 + i, + (nframes - jackFramesAvailable), volume, drv->num_output_channels); + } else + { + float_volume_effect((sample_t *) drv->callback_buffer2 + i, (nframes - jackFramesAvailable), + ((float) drv->volume[i] / 100.0), + drv->num_output_channels); + } + } + + /* demux the stream: we skip over the number of samples we have output channels as the channel data */ + /* is encoded like chan1,chan2,chan3,chan1,chan2,chan3... */ + for(i = 0; i < drv->num_output_channels; i++) + { + demux(out_buffer[i], + (sample_t *) drv->callback_buffer2 + i, + (nframes - jackFramesAvailable), drv->num_output_channels); + } + } + } + + /* handle record data, if any */ + if(drv->num_input_channels > 0) + { + long jack_bytes = nframes * drv->bytes_per_jack_input_frame; /* how many bytes jack is feeding us */ + + if(!ensure_buffer_size(&drv->callback_buffer1, &drv->callback_buffer1_size, jack_bytes)) + { + ERR("allocated %lu bytes, need %lu bytes\n", + drv->callback_buffer1_size, jack_bytes); + return -1; + } + + /* mux the invividual channels into one stream */ + for(i = 0; i < drv->num_input_channels; i++) + { + mux((sample_t *) drv->callback_buffer1 + i, in_buffer[i], + nframes, drv->num_input_channels); + } + + /* do sample rate conversion if needed & requested */ + if(drv->input_src && drv->input_sample_rate_ratio != 1.0) + { + /* make a very good guess at how many raw bytes we'll need to read all the data jack gave us */ + long bytes_needed_write = (double) (jack_bytes + + drv->input_sample_rate_ratio * + drv->bytes_per_jack_input_frame) * + drv->input_sample_rate_ratio; + DEBUG("guessing that we need %ld bytes in and %ld out for rate conversion ratio = %f\n", + nframes * drv->bytes_per_jack_input_frame, + bytes_needed_write, drv->input_sample_rate_ratio); + + if(!ensure_buffer_size(&drv->callback_buffer2, + &drv->callback_buffer2_size, + bytes_needed_write)) + { + ERR("could not realloc callback_buffer2!\n"); + return 1; + } + + SRC_DATA srcdata; + srcdata.data_in = (sample_t *) drv->callback_buffer1; + srcdata.input_frames = nframes; + srcdata.src_ratio = drv->input_sample_rate_ratio; + srcdata.data_out = (sample_t *) drv->callback_buffer2; + srcdata.output_frames = drv->callback_buffer2_size / drv->bytes_per_jack_input_frame; + srcdata.end_of_input = 0; // it's a stream, it never ends + DEBUG("input_frames = %ld, output_frames = %ld\n", + srcdata.input_frames, srcdata.output_frames); + /* convert the sample rate */ + src_error = src_process(drv->input_src, &srcdata); + DEBUG("used = %ld, generated = %ld, error = %d: %s.\n", + srcdata.input_frames_used, srcdata.output_frames_gen, + src_error, src_strerror(src_error)); + + if(src_error == 0) + { + long write_space = jack_ringbuffer_write_space(drv->pRecPtr); + long bytes_used = srcdata.output_frames_gen * drv->bytes_per_jack_input_frame; + /* if there isn't enough room, make some. sure this discards data, but when dealing with input sources + it seems like it's better to throw away old data than new */ + if(write_space < bytes_used) + { + /* the ringbuffer is designed such that only one thread should ever access each pointer. + since calling read_advance here will be touching the read pointer which is also accessed + by JACK_Read, we need to lock the mutex first for safety */ + jack_driver_t *d = tryGetDriver(drv->deviceID); + if( d ) + { + /* double check the write space after we've gained the lock, just + in case JACK_Read was being called before we gained it */ + write_space = jack_ringbuffer_write_space(drv->pRecPtr); + if(write_space < bytes_used) + { + /* hey, we warn about underruns, we might as well warn about overruns as well */ + WARN("buffer overrun of %ld bytes\n", jack_bytes - write_space); + jack_ringbuffer_read_advance(drv->pRecPtr, bytes_used - write_space); + } + + releaseDriver(drv); + } + } + + jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer2, + bytes_used); + } + } + else /* no resampling needed */ + { + long write_space = jack_ringbuffer_write_space(drv->pRecPtr); + /* if there isn't enough room, make some. sure this discards data, but when dealing with input sources + it seems like it's better to throw away old data than new */ + if(write_space < jack_bytes) + { + /* the ringbuffer is designed such that only one thread should ever access each pointer. + since calling read_advance here will be touching the read pointer which is also accessed + by JACK_Read, we need to lock the mutex first for safety */ + jack_driver_t *d = tryGetDriver(drv->deviceID); + if( d ) + { + /* double check the write space after we've gained the lock, just + in case JACK_Read was being called before we gained it */ + write_space = jack_ringbuffer_write_space(drv->pRecPtr); + if(write_space < jack_bytes) + { + ERR("buffer overrun of %ld bytes\n", jack_bytes - write_space); + jack_ringbuffer_read_advance(drv->pRecPtr, jack_bytes - write_space); + } + releaseDriver(drv); + } + } + + jack_ringbuffer_write(drv->pRecPtr, drv->callback_buffer1, + jack_bytes); + } + } + } + else if(drv->state == PAUSED || + drv->state == STOPPED || + drv->state == CLOSED || drv->state == RESET) + { + CALLBACK_TRACE("%s, outputting silence\n", DEBUGSTATE(drv->state)); + + /* output silence if nothing is being outputted */ + for(i = 0; i < drv->num_output_channels; i++) + sample_silence_float(out_buffer[i], nframes); + + /* if we were told to reset then zero out some variables */ + /* and transition to STOPPED */ + if(drv->state == RESET) + { + drv->written_client_bytes = 0; + drv->played_client_bytes = 0; /* number of the clients bytes that jack has played */ + + drv->client_bytes = 0; /* bytes that the client wrote to use */ + + drv->clientBytesInJack = 0; /* number of input bytes in jack(not necessary the number of bytes written to jack) */ + + drv->position_byte_offset = 0; + + if(drv->pPlayPtr) + jack_ringbuffer_reset(drv->pPlayPtr); + + if(drv->pRecPtr) + jack_ringbuffer_reset(drv->pRecPtr); + + drv->state = STOPPED; /* transition to STOPPED */ + } + } + + CALLBACK_TRACE("done\n"); + TIMER("finish\n"); + + return 0; +} + + +/****************************************************************** + * JACK_bufsize + * + * Called whenever the jack server changes the the max number + * of frames passed to JACK_callback + */ +static int +JACK_bufsize(nframes_t nframes, void *arg) +{ + jack_driver_t *drv = (jack_driver_t *) arg; + TRACE("the maximum buffer size is now %lu frames\n", (long) nframes); + + drv->jack_buffer_size = nframes; + + return 0; +} + +/****************************************************************** + * JACK_srate + */ +int +JACK_srate(nframes_t nframes, void *arg) +{ + jack_driver_t *drv = (jack_driver_t *) arg; + + drv->jack_sample_rate = (long) nframes; + + /* make sure to recalculate the ratios needed for proper sample rate conversion */ + drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate; + if(drv->output_src) src_set_ratio(drv->output_src, drv->output_sample_rate_ratio); + + drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate; + if(drv->input_src) src_set_ratio(drv->input_src, drv->input_sample_rate_ratio); + + TRACE("the sample rate is now %lu/sec\n", (long) nframes); + return 0; +} + + +/****************************************************************** + * JACK_shutdown + * + * if this is called then jack shut down... handle this appropriately */ +void +JACK_shutdown(void *arg) +{ + jack_driver_t *drv = (jack_driver_t *) arg; + + TRACE("\n"); + + getDriver(drv->deviceID); + + drv->client = 0; /* reset client */ + drv->jackd_died = TRUE; + + TRACE("jack shutdown, setting client to 0 and jackd_died to true, closing device\n"); + +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + + TRACE("trying to reconnect right now\n"); + /* lets see if we can't reestablish the connection */ + if(JACK_OpenDevice(drv) != ERR_SUCCESS) + { + ERR("unable to reconnect with jack\n"); + } + + releaseDriver(drv); +} + + +/****************************************************************** + * JACK_Error + * + * Callback for jack errors + */ +static void +JACK_Error(const char *desc) +{ + ERR("%s\n", desc); +} + + +/****************************************************************** + * JACK_OpenDevice + * + * RETURNS: ERR_SUCCESS upon success + */ +static int +JACK_OpenDevice(jack_driver_t * drv) +{ + const char **ports; + char *our_client_name = 0; + unsigned int i; + int failed = 0; + + TRACE("creating jack client and setting up callbacks\n"); + +#if JACK_CLOSE_HACK + /* see if this device is already open */ + if(drv->client) + { + /* if this device is already in use then it is bad for us to be in here */ + if(drv->in_use) + return ERR_OPENING_JACK; + + TRACE("using existing client\n"); + drv->in_use = TRUE; + return ERR_SUCCESS; + } +#endif + + /* set up an error handler */ + jack_set_error_function(JACK_Error); + + + /* build the client name */ + our_client_name = (char *) malloc(snprintf + (our_client_name, 0, "%s_%d_%d%02d", client_name, getpid(), + drv->deviceID, drv->clientCtr + 1) + 1); + sprintf(our_client_name, "%s_%d_%d%02d", client_name, getpid(), + drv->deviceID, drv->clientCtr++); + + /* try to become a client of the JACK server */ + TRACE("client name '%s'\n", our_client_name); + if((drv->client = jack_client_new(our_client_name)) == 0) + { + /* try once more */ + TRACE("trying once more to jack_client_new"); + if((drv->client = jack_client_new(our_client_name)) == 0) + { + ERR("jack server not running?\n"); + free(our_client_name); + return ERR_OPENING_JACK; + } + } + + free(our_client_name); + + TRACE("setting up jack callbacks\n"); + + /* JACK server to call `JACK_callback()' whenever + there is work to be done. */ + jack_set_process_callback(drv->client, JACK_callback, drv); + + /* setup a buffer size callback */ + jack_set_buffer_size_callback(drv->client, JACK_bufsize, drv); + + /* tell the JACK server to call `srate()' whenever + the sample rate of the system changes. */ + jack_set_sample_rate_callback(drv->client, JACK_srate, drv); + + /* tell the JACK server to call `jack_shutdown()' if + it ever shuts down, either entirely, or if it + just decides to stop calling us. */ + jack_on_shutdown(drv->client, JACK_shutdown, drv); + + /* display the current sample rate. once the client is activated + (see below), you should rely on your own sample rate + callback (see above) for this value. */ + drv->jack_sample_rate = jack_get_sample_rate(drv->client); + drv->output_sample_rate_ratio = (double) drv->jack_sample_rate / (double) drv->client_sample_rate; + drv->input_sample_rate_ratio = (double) drv->client_sample_rate / (double) drv->jack_sample_rate; + TRACE("client sample rate: %lu, jack sample rate: %lu, output ratio = %f, input ratio = %f\n", + drv->client_sample_rate, drv->jack_sample_rate, + drv->output_sample_rate_ratio, drv->input_sample_rate_ratio); + + drv->jack_buffer_size = jack_get_buffer_size(drv->client); + + /* create the output ports */ + TRACE("creating output ports\n"); + for(i = 0; i < drv->num_output_channels; i++) + { + char portname[32]; + sprintf(portname, "out_%d", i); + TRACE("port %d is named '%s'\n", i, portname); + /* NOTE: Yes, this is supposed to be JackPortIsOutput since this is an output */ + /* port FROM bio2jack */ + drv->output_port[i] = jack_port_register(drv->client, portname, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0); + } + + /* create the input ports */ + TRACE("creating input ports\n"); + for(i = 0; i < drv->num_input_channels; i++) + { + char portname[32]; + sprintf(portname, "in_%d", i); + TRACE("port %d is named '%s'\n", i, portname); + /* NOTE: Yes, this is supposed to be JackPortIsInput since this is an input */ + /* port TO bio2jack */ + drv->input_port[i] = jack_port_register(drv->client, portname, + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsInput, 0); + } + +#if JACK_CLOSE_HACK + drv->in_use = TRUE; +#endif + + /* tell the JACK server that we are ready to roll */ + TRACE("calling jack_activate()\n"); + if(jack_activate(drv->client)) + { + ERR("cannot activate client\n"); + return ERR_OPENING_JACK; + } + + /* if we have output channels and the port connection mode isn't CONNECT_NONE */ + /* then we should connect up some ports */ + if((drv->num_output_channels > 0) && (port_connection_mode != CONNECT_NONE)) + { + /* determine how we are to acquire output port names */ + if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1)) + { + if(drv->jack_port_name_count == 0) + { + TRACE("jack_get_ports() passing in NULL/NULL\n"); + ports = jack_get_ports(drv->client, NULL, NULL, + drv->jack_output_port_flags); + } + else + { + TRACE("jack_get_ports() passing in port of '%s'\n", + drv->jack_port_name[0]); + ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL, + drv->jack_output_port_flags); + } + + /* display a trace of the output ports we found */ + unsigned int num_ports = 0; + if(ports) + { + for(i = 0; ports[i]; i++) + { + TRACE("ports[%d] = '%s'\n", i, ports[i]); + num_ports++; + } + } + + /* ensure that we found enough ports */ + if(!ports || (i < drv->num_output_channels)) + { + TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n", + drv->jack_output_port_flags); +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + return ERR_PORT_NOT_FOUND; + } + + /* connect a port for each output channel. Note: you can't do this before + the client is activated (this may change in the future). */ + for(i = 0; i < drv->num_output_channels; i++) + { + TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]); + if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[i])) + { + ERR("cannot connect to output port %d('%s')\n", i, ports[i]); + failed = 1; + } + } + + /* only if we are in CONNECT_ALL mode should we keep connecting ports up beyond */ + /* the minimum number of ports required for each output channel coming into bio2jack */ + if(port_connection_mode == CONNECT_ALL) + { + /* It's much cheaper and easier to let JACK do the processing required to + connect 2 channels to 4 or 4 channels to 2 or any other combinations. + This effectively eliminates the need for sample_move_d16_d16() */ + if(drv->num_output_channels < num_ports) + { + for(i = drv->num_output_channels; ports[i]; i++) + { + int n = i % drv->num_output_channels; + TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]); + if(jack_connect(drv->client, jack_port_name(drv->output_port[n]), ports[i])) + { + // non fatal + ERR("cannot connect to output port %d('%s')\n", n, ports[i]); + } + } + } + else if(drv->num_output_channels > num_ports) + { + for(i = num_ports; i < drv->num_output_channels; i++) + { + int n = i % num_ports; + TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[n]); + if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[n])) + { + // non fatal + ERR("cannot connect to output port %d('%s')\n", i, ports[n]); + } + } + } + } + + free(ports); /* free the returned array of ports */ + } + else + { + for(i = 0; i < drv->jack_port_name_count; i++) + { + TRACE("jack_get_ports() portname %d of '%s\n", i, + drv->jack_port_name[i]); + ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL, + drv->jack_output_port_flags); + + if(!ports) + { + ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n", + drv->jack_output_port_flags); + return ERR_PORT_NOT_FOUND; + } + + TRACE("ports[%d] = '%s'\n", 0, ports[0]); /* display a trace of the output port we found */ + + /* connect the port */ + TRACE("jack_connect() to port %d('%p')\n", i, drv->output_port[i]); + if(jack_connect(drv->client, jack_port_name(drv->output_port[i]), ports[0])) + { + ERR("cannot connect to output port %d('%s')\n", 0, ports[0]); + failed = 1; + } + free(ports); /* free the returned array of ports */ + } + } + } /* if( drv->num_output_channels > 0 ) */ + + + if(drv->num_input_channels > 0) + { + /* determine how we are to acquire input port names */ + if((drv->jack_port_name_count == 0) || (drv->jack_port_name_count == 1)) + { + if(drv->jack_port_name_count == 0) + { + TRACE("jack_get_ports() passing in NULL/NULL\n"); + ports = jack_get_ports(drv->client, NULL, NULL, drv->jack_input_port_flags); + } + else + { + TRACE("jack_get_ports() passing in port of '%s'\n", + drv->jack_port_name[0]); + ports = jack_get_ports(drv->client, drv->jack_port_name[0], NULL, + drv->jack_input_port_flags); + } + + /* display a trace of the input ports we found */ + unsigned int num_ports = 0; + if(ports) + { + for(i = 0; ports[i]; i++) + { + TRACE("ports[%d] = '%s'\n", i, ports[i]); + num_ports++; + } + } + + /* ensure that we found enough ports */ + if(!ports || (i < drv->num_input_channels)) + { + TRACE("ERR: jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n", + drv->jack_input_port_flags); +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + return ERR_PORT_NOT_FOUND; + } + + /* connect the ports. Note: you can't do this before + the client is activated (this may change in the future). */ + for(i = 0; i < drv->num_input_channels; i++) + { + TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]); + if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[i]))) + { + ERR("cannot connect to input port %d('%s')\n", i, ports[i]); + failed = 1; + } + } + + /* It's much cheaper and easier to let JACK do the processing required to + connect 2 channels to 4 or 4 channels to 2 or any other combinations. + This effectively eliminates the need for sample_move_d16_d16() */ + if(drv->num_input_channels < num_ports) + { + for(i = drv->num_input_channels; ports[i]; i++) + { + int n = i % drv->num_input_channels; + TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]); + if(jack_connect(drv->client, ports[i], jack_port_name(drv->input_port[n]))) + { + // non fatal + ERR("cannot connect to input port %d('%s')\n", n, ports[i]); + } + } + } + else if(drv->num_input_channels > num_ports) + { + for(i = num_ports; i < drv->num_input_channels; i++) + { + int n = i % num_ports; + TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[n]); + if(jack_connect(drv->client, ports[n], jack_port_name(drv->input_port[i]))) + { + // non fatal + ERR("cannot connect to input port %d('%s')\n", i, ports[n]); + } + } + } + + free(ports); /* free the returned array of ports */ + } + else + { + for(i = 0; i < drv->jack_port_name_count; i++) + { + TRACE("jack_get_ports() portname %d of '%s\n", i, + drv->jack_port_name[i]); + ports = jack_get_ports(drv->client, drv->jack_port_name[i], NULL, + drv->jack_input_port_flags); + + if(!ports) + { + ERR("jack_get_ports() failed to find ports with jack port flags of 0x%lX'\n", + drv->jack_input_port_flags); + return ERR_PORT_NOT_FOUND; + } + + TRACE("ports[%d] = '%s'\n", 0, ports[0]); /* display a trace of the input port we found */ + + /* connect the port */ + TRACE("jack_connect() to port %d('%p')\n", i, drv->input_port[i]); + if(jack_connect(drv->client, jack_port_name(drv->input_port[i]), ports[0])) + { + ERR("cannot connect to input port %d('%s')\n", 0, ports[0]); + failed = 1; + } + free(ports); /* free the returned array of ports */ + } + } + } /* if( drv->num_input_channels > 0 ) */ + + /* if something failed we need to shut the client down and return 0 */ + if(failed) + { + TRACE("failed, closing and returning error\n"); +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + return ERR_OPENING_JACK; + } + + TRACE("success\n"); + + drv->jackd_died = FALSE; /* clear out this flag so we don't keep attempting to restart things */ + drv->state = PLAYING; /* clients seem to behave much better with this on from the start, especially when recording */ + + return ERR_SUCCESS; /* return success */ +} + + +/****************************************************************** + * JACK_CloseDevice + * + * Close the connection to the server cleanly. + * If close_client is TRUE we close the client for this device instead of + * just marking the device as in_use(JACK_CLOSE_HACK only) + */ +#if JACK_CLOSE_HACK +static void +JACK_CloseDevice(jack_driver_t * drv, bool close_client) +#else +static void +JACK_CloseDevice(jack_driver_t * drv) +#endif +{ + unsigned int i; + +#if JACK_CLOSE_HACK + if(close_client) + { +#endif + + TRACE("closing the jack client thread\n"); + if(drv->client) + { + TRACE("after jack_deactivate()\n"); + int errorCode = jack_client_close(drv->client); + if(errorCode) + ERR("jack_client_close() failed returning an error code of %d\n", + errorCode); + } + + /* reset client */ + drv->client = 0; + + /* free up the port strings */ + TRACE("freeing up %d port strings\n", drv->jack_port_name_count); + if(drv->jack_port_name_count > 1) + { + for(i = 0; i < drv->jack_port_name_count; i++) + free(drv->jack_port_name[i]); + free(drv->jack_port_name); + } + JACK_CleanupDriver(drv); + + JACK_ResetFromDriver(drv); + +#if JACK_CLOSE_HACK + } else + { + TRACE("setting in_use to FALSE\n"); + drv->in_use = FALSE; + + if(!drv->client) + { + TRACE("critical error, closing a device that has no client\n"); + } + } +#endif +} + + + + +/**************************************/ +/* External interface functions below */ +/**************************************/ + +/* Clear out any buffered data, stop playing, zero out some variables */ +static void +JACK_ResetFromDriver(jack_driver_t * drv) +{ + TRACE("resetting drv->deviceID(%d)\n", drv->deviceID); + + /* NOTE: we use the RESET state so we don't need to worry about clearing out */ + /* variables that the callback modifies while the callback is running */ + /* we set the state to RESET and the callback clears the variables out for us */ + drv->state = RESET; /* tell the callback that we are to reset, the callback will transition this to STOPPED */ +} + +/* Clear out any buffered data, stop playing, zero out some variables */ +void +JACK_Reset(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + TRACE("resetting deviceID(%d)\n", deviceID); + JACK_ResetFromDriver(drv); + releaseDriver(drv); +} + + +/* + * open the audio device for writing to + * + * deviceID is set to the opened device + * if client is non-zero and in_use is FALSE then just set in_use to TRUE + * + * return value is zero upon success, non-zero upon failure + * + * if ERR_RATE_MISMATCH (*rate) will be updated with the jack servers rate + */ +int +JACK_Open(int *deviceID, unsigned int bits_per_channel, unsigned long *rate, + int channels) +{ + /* we call through to JACK_OpenEx(), but default the input channels to 0 for better backwards + compatibility with clients written before recording was available */ + return JACK_OpenEx(deviceID, bits_per_channel, + rate, + 0, channels, + NULL, 0, JackPortIsPhysical); +} + +/* + * see JACK_Open() for comments + * NOTE: jack_port_name has three ways of being used: + * - NULL - finds all ports with the given flags + * - A single regex string used to retrieve all port names + * - A series of port names, one for each output channel + * + * we set *deviceID + */ +int +JACK_OpenEx(int *deviceID, unsigned int bits_per_channel, + unsigned long *rate, + unsigned int input_channels, unsigned int output_channels, + const char **jack_port_name, + unsigned int jack_port_name_count, unsigned long jack_port_flags) +{ + jack_driver_t *drv = 0; + unsigned int i; + int retval; + + if(input_channels < 1 && output_channels < 1) + { + ERR("no input OR output channels, nothing to do\n"); + return ERR_OPENING_JACK; + } + + switch (bits_per_channel) + { + case 8: + case 16: + break; + default: + ERR("invalid bits_per_channel\n"); + return ERR_OPENING_JACK; + } + + /* Lock the device_mutex and find one that's not allocated already. + We'll keep this lock until we've either made use of it, or given up. */ + pthread_mutex_lock(&device_mutex); + + for(i = 0; i < MAX_OUTDEVICES; i++) + { + if(!outDev[i].allocated) + { + drv = &outDev[i]; + break; + } + } + + if(!drv) + { + ERR("no more devices available\n"); + return ERR_OPENING_JACK; + } + + /* We found an unallocated device, now lock it for extra saftey */ + getDriver(drv->deviceID); + + TRACE("bits_per_channel=%d rate=%ld, input_channels=%d, output_channels=%d\n", + bits_per_channel, *rate, input_channels, output_channels); + + if(output_channels > MAX_OUTPUT_PORTS) + { + ERR("output_channels == %d, MAX_OUTPUT_PORTS == %d\n", output_channels, + MAX_OUTPUT_PORTS); + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return ERR_TOO_MANY_OUTPUT_CHANNELS; + } + + if(input_channels > MAX_INPUT_PORTS) + { + ERR("input_channels == %d, MAX_INPUT_PORTS == %d\n", input_channels, + MAX_INPUT_PORTS); + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return ERR_TOO_MANY_INPUT_CHANNELS; + } + + drv->jack_output_port_flags = jack_port_flags | JackPortIsInput; /* port must be input(ie we can put data into it), so mask this in */ + drv->jack_input_port_flags = jack_port_flags | JackPortIsOutput; /* port must be output(ie we can get data from it), so mask this in */ + + /* check that we have the correct number of port names + FIXME?: not sure how we should handle output ports vs input ports.... + */ + if((jack_port_name_count > 1) + && ((jack_port_name_count < output_channels) + || (jack_port_name_count < input_channels))) + { + ERR("specified individual port names but not enough, gave %d names, need %d\n", + jack_port_name_count, output_channels); + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH; + } else + { + /* copy this data into the device information */ + drv->jack_port_name_count = jack_port_name_count; + + if(drv->jack_port_name_count != 0) + { + drv->jack_port_name = + (char **) malloc(sizeof(char *) * drv->jack_port_name_count); + for(i = 0; i < drv->jack_port_name_count; i++) + { + drv->jack_port_name[i] = strdup(jack_port_name[i]); + TRACE("jack_port_name[%d] == '%s'\n", i, jack_port_name[i]); + } + } else + { + drv->jack_port_name = NULL; + TRACE("jack_port_name = NULL\n"); + } + } + + /* initialize some variables */ + drv->in_use = FALSE; + + JACK_ResetFromDriver(drv); /* flushes all queued buffers, sets status to STOPPED and resets some variables */ + + /* drv->jack_sample_rate is set by JACK_OpenDevice() */ + drv->client_sample_rate = *rate; + drv->bits_per_channel = bits_per_channel; + drv->num_input_channels = input_channels; + drv->num_output_channels = output_channels; + drv->bytes_per_input_frame = (drv->bits_per_channel * drv->num_input_channels) / 8; + drv->bytes_per_output_frame = (drv->bits_per_channel * drv->num_output_channels) / 8; + drv->bytes_per_jack_output_frame = sizeof(sample_t) * drv->num_output_channels; + drv->bytes_per_jack_input_frame = sizeof(sample_t) * drv->num_input_channels; + + if(drv->num_output_channels > 0) + { + drv->pPlayPtr = jack_ringbuffer_create(drv->num_output_channels * + drv->bytes_per_jack_output_frame * + DEFAULT_RB_SIZE); + } + + if(drv->num_input_channels > 0) + { + drv->pRecPtr = jack_ringbuffer_create(drv->num_input_channels * + drv->bytes_per_jack_input_frame * + DEFAULT_RB_SIZE); + } + + DEBUG("bytes_per_output_frame == %ld\n", drv->bytes_per_output_frame); + DEBUG("bytes_per_input_frame == %ld\n", drv->bytes_per_input_frame); + DEBUG("bytes_per_jack_output_frame == %ld\n", + drv->bytes_per_jack_output_frame); + DEBUG("bytes_per_jack_input_frame == %ld\n", + drv->bytes_per_jack_input_frame); + + /* go and open up the device */ + retval = JACK_OpenDevice(drv); + if(retval != ERR_SUCCESS) + { + TRACE("error opening jack device\n"); + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return retval; + } + else + { + TRACE("succeeded opening jack device\n"); + } + + /* setup SRC objects just in case they'll be needed but only if requested */ + if(do_sample_rate_conversion) + { + int error; + if(drv->num_output_channels > 0) + { + drv->output_src = src_new(preferred_src_converter, drv->num_output_channels, &error); + if(error != 0) + { + src_delete(drv->output_src); + drv->output_src = 0; + ERR("Could not created SRC object for output stream %d: %s\n", + error, src_strerror(error)); + } + } + if(drv->num_input_channels > 0) + { + drv->input_src = src_new(preferred_src_converter, drv->num_input_channels, &error); + if(error != 0) + { + src_delete(drv->input_src); + drv->input_src = 0; + ERR("Could not created SRC object for input stream %d: %s\n", + error, src_strerror(error)); + } + } + } + else if((long) (*rate) != drv->jack_sample_rate) + { + TRACE("rate of %ld doesn't match jack sample rate of %ld, returning error\n", + *rate, drv->jack_sample_rate); + *rate = drv->jack_sample_rate; +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return ERR_RATE_MISMATCH; + } + + drv->allocated = TRUE; /* record that we opened this device */ + + DEBUG("sizeof(sample_t) == %d\n", sizeof(sample_t)); + + int periodSize = jack_get_buffer_size(drv->client); + int periods = 0; + /* FIXME: maybe we should keep different latency values for input vs output? */ + if(drv->num_output_channels > 0) + { + periods = jack_port_get_total_latency(drv->client, + drv->output_port[0]) / periodSize; + drv->latencyMS = periodSize * periods * 1000 / (drv->jack_sample_rate * + (drv->bits_per_channel / 8 * + drv->num_output_channels)); + } + else if(drv->num_input_channels > 0) + { + periods = jack_port_get_total_latency(drv->client, + drv->input_port[0]) / periodSize; + drv->latencyMS = + periodSize * periods * 1000 / (drv->jack_sample_rate * + (drv->bits_per_channel / 8 * + drv->num_input_channels)); + } + + TRACE("drv->latencyMS == %ldms\n", drv->latencyMS); + + *deviceID = drv->deviceID; /* set the deviceID for the caller */ + releaseDriver(drv); + pthread_mutex_unlock(&device_mutex); + return ERR_SUCCESS; /* success */ +} + +/* Close the jack device */ +//FIXME: add error handling in here at some point... +/* NOTE: return 0 for success, non-zero for failure */ +int +JACK_Close(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + + TRACE("deviceID(%d)\n", deviceID); + +#if JACK_CLOSE_HACK + JACK_CloseDevice(drv, TRUE); +#else + JACK_CloseDevice(drv); +#endif + + JACK_ResetFromDriver(drv); /* reset this device to a normal starting state */ + + pthread_mutex_lock(&device_mutex); + + /* free buffer memory */ + drv->callback_buffer1_size = 0; + if(drv->callback_buffer1) free(drv->callback_buffer1); + drv->callback_buffer1 = 0; + + drv->callback_buffer2_size = 0; + if(drv->callback_buffer2) free(drv->callback_buffer2); + drv->callback_buffer2 = 0; + + drv->rw_buffer1_size = 0; + if(drv->rw_buffer1) free(drv->rw_buffer1); + drv->rw_buffer1 = 0; + + if(drv->pPlayPtr) jack_ringbuffer_free(drv->pPlayPtr); + drv->pPlayPtr = 0; + + if(drv->pRecPtr) jack_ringbuffer_free(drv->pRecPtr); + drv->pRecPtr = 0; + + /* free the SRC objects */ + if(drv->output_src) src_delete(drv->output_src); + drv->output_src = 0; + + if(drv->input_src) src_delete(drv->input_src); + drv->input_src = 0; + + drv->allocated = FALSE; /* release this device */ + + pthread_mutex_unlock(&device_mutex); + + releaseDriver(drv); + + return 0; +} + +/* If we haven't already taken in the max allowed data then create a wave header */ +/* to package the audio data and attach the wave header to the end of the */ +/* linked list of wave headers */ +/* These wave headers will be peeled off as they are played by the callback routine */ +/* Return value is the number of bytes written */ +/* NOTE: this function takes the length of data to be written bytes */ +long +JACK_Write(int deviceID, unsigned char *data, unsigned long bytes) +{ + jack_driver_t *drv = getDriver(deviceID); + + long frames_free, frames; + + TIMER("start\n"); + + TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes); + + /* check and see that we have enough space for this audio */ + frames_free = + jack_ringbuffer_write_space(drv->pPlayPtr) / + drv->bytes_per_jack_output_frame; + frames = bytes / drv->bytes_per_output_frame; + TRACE("frames free == %ld, bytes = %lu\n", frames_free, bytes); + + TRACE("state = '%s'\n", DEBUGSTATE(drv->state)); + /* if we are currently STOPPED we should start playing now... + do this before the check for bytes == 0 since some clients like + to write 0 bytes the first time out */ + if(drv->state == STOPPED) + { + TRACE("currently STOPPED, transitioning to PLAYING\n"); + drv->state = PLAYING; + } + + /* handle the case where the user calls this routine with 0 bytes */ + if(bytes == 0 || frames_free < 1) + { + TRACE("no room left\n"); + TIMER("finish (nothing to do, buffer is full)\n"); + releaseDriver(drv); + return 0; /* indicate that we couldn't write any bytes */ + } + + frames = min(frames, frames_free); + long jack_bytes = frames * drv->bytes_per_jack_output_frame; + if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes)) + { + ERR("couldn't allocate enough space for the buffer\n"); + releaseDriver(drv); + return 0; + } + /* adjust bytes to be how many client bytes we're actually writing */ + bytes = frames * drv->bytes_per_output_frame; + + /* convert from client samples to jack samples + we have to tell it how many samples there are, which is frames * channels */ + switch (drv->bits_per_channel) + { + case 8: + sample_move_char_float((sample_t *) drv->rw_buffer1, (unsigned char *) data, + frames * drv->num_output_channels); + break; + case 16: + sample_move_short_float((sample_t *) drv->rw_buffer1, (short *) data, + frames * drv->num_output_channels); + break; + } + + DEBUG("ringbuffer read space = %d, write space = %d\n", + jack_ringbuffer_read_space(drv->pPlayPtr), + jack_ringbuffer_write_space(drv->pPlayPtr)); + + jack_ringbuffer_write(drv->pPlayPtr, drv->rw_buffer1, jack_bytes); + DEBUG("wrote %lu bytes, %lu jack_bytes\n", bytes, jack_bytes); + + DEBUG("ringbuffer read space = %d, write space = %d\n", + jack_ringbuffer_read_space(drv->pPlayPtr), + jack_ringbuffer_write_space(drv->pPlayPtr)); + + drv->client_bytes += bytes; /* update client_bytes */ + + TIMER("finish\n"); + + DEBUG("returning bytes written of %ld\n", bytes); + + releaseDriver(drv); + return bytes; /* return the number of bytes we wrote out */ +} + +long +JACK_Read(int deviceID, unsigned char *data, unsigned long bytes) +{ + jack_driver_t *drv = getDriver(deviceID); + + long frames_available, frames; + + TIMER("start\n"); + + TRACE("deviceID(%d), bytes == %ld\n", deviceID, bytes); + + /* find out if there's any room to write this data */ + frames_available = + jack_ringbuffer_read_space(drv->pRecPtr) / + drv->bytes_per_jack_input_frame; + frames = bytes / drv->bytes_per_input_frame; + DEBUG("frames available = %ld, bytes = %lu\n", frames_available, bytes); + + TRACE("state = '%s'\n", DEBUGSTATE(drv->state)); + /* if we are currently STOPPED we should start recording now... */ + if(drv->state == STOPPED) + { + TRACE("currently STOPPED, transitioning to PLAYING\n"); + drv->state = PLAYING; + } + + /* handle the case where the user calls this routine with 0 bytes */ + if(bytes == 0 || frames_available < 1) + { + TRACE("no bytes in buffer\n"); + + TIMER("finish (nothing to do)\n"); + releaseDriver(drv); + return 0; + } + + frames = min(frames, frames_available); + long jack_bytes = frames * drv->bytes_per_jack_input_frame; + if(!ensure_buffer_size(&drv->rw_buffer1, &drv->rw_buffer1_size, jack_bytes)) + { + ERR("couldn't allocate enough space for the buffer\n"); + releaseDriver(drv); + return 0; + } + + DEBUG("ringbuffer read space = %d, write space = %d\n", + jack_ringbuffer_read_space(drv->pRecPtr), + jack_ringbuffer_write_space(drv->pRecPtr)); + + jack_ringbuffer_read(drv->pRecPtr, drv->rw_buffer1, + frames * drv->bytes_per_jack_input_frame); + + DEBUG("ringbuffer read space = %d, write space = %d\n", + jack_ringbuffer_read_space(drv->pRecPtr), + jack_ringbuffer_write_space(drv->pRecPtr)); + + unsigned int i; + for(i = 0; i < drv->num_output_channels; i++) + { + /* apply volume to the floating value */ + if(drv->volumeEffectType == dbAttenuation) + { + /* assume the volume setting is dB of attenuation, a volume of 0 */ + /* is 0dB attenuation */ + float volume = powf(10.0, -((float) drv->volume[i]) / 20.0); + float_volume_effect((sample_t *) drv->rw_buffer1 + i, + frames, volume, drv->num_output_channels); + } else + { + float_volume_effect((sample_t *) drv->rw_buffer1 + i, frames, + ((float) drv->volume[i] / 100.0), + drv->num_output_channels); + } + } + + /* convert from jack samples to client samples + we have to tell it how many samples there are, which is frames * channels */ + switch (drv->bits_per_channel) + { + case 8: + sample_move_float_char((unsigned char *) data, (sample_t *) drv->rw_buffer1, + frames * drv->num_input_channels); + break; + case 16: + sample_move_float_short((short *) data, (sample_t *) drv->rw_buffer1, + frames * drv->num_input_channels); + break; + } + + TIMER("finish\n"); + + long read_bytes = frames * drv->bytes_per_input_frame; + + DEBUG("returning bytes read of %ld\n", bytes); + + releaseDriver(drv); + return read_bytes; +} + +/* return ERR_SUCCESS for success */ +static int +JACK_SetVolumeForChannelFromDriver(jack_driver_t * drv, + unsigned int channel, unsigned int volume) +{ + /* TODO?: maybe we should have different volume levels for input & output */ + /* ensure that we have the channel we are setting volume for */ + if(channel > (drv->num_output_channels - 1)) + return 1; + + if(volume > 100) + volume = 100; /* check for values in excess of max */ + + drv->volume[channel] = volume; + return ERR_SUCCESS; +} + +/* return ERR_SUCCESS for success */ +int +JACK_SetVolumeForChannel(int deviceID, unsigned int channel, + unsigned int volume) +{ + jack_driver_t *drv = getDriver(deviceID); + int retval = JACK_SetVolumeForChannelFromDriver(drv, channel, volume); + releaseDriver(drv); + return retval; +} + +/* Set the volume */ +/* return 0 for success */ +/* NOTE: we check for invalid volume values */ +int +JACK_SetAllVolume(int deviceID, unsigned int volume) +{ + jack_driver_t *drv = getDriver(deviceID); + unsigned int i; + + TRACE("deviceID(%d), setting volume of %d\n", deviceID, volume); + + for(i = 0; i < drv->num_output_channels; i++) + { + if(JACK_SetVolumeForChannelFromDriver(drv, i, volume) != ERR_SUCCESS) + { + releaseDriver(drv); + return 1; + } + } + + releaseDriver(drv); + return ERR_SUCCESS; +} + +/* Return the current volume in the inputted pointers */ +/* NOTE: we check for null pointers being passed in just in case */ +void +JACK_GetVolumeForChannel(int deviceID, unsigned int channel, + unsigned int *volume) +{ + jack_driver_t *drv = getDriver(deviceID); + + /* ensure that we have the channel we are getting volume for */ + if(channel > (drv->num_output_channels - 1)) + { + ERR("asking for channel index %d but we only have %ld channels\n", channel, drv->num_output_channels); + releaseDriver(drv); + return; + } + + if(volume) + *volume = drv->volume[channel]; + +#if VERBOSE_OUTPUT + if(volume) + { + TRACE("deviceID(%d), returning volume of %d for channel %d\n", + deviceID, *volume, channel); + } + else + { + TRACE("volume is null, can't dereference it\n"); + } +#endif + + releaseDriver(drv); +} + + +/* linear means 0 volume is silence, 100 is full volume */ +/* dbAttenuation means 0 volume is 0dB attenuation */ +/* Bio2jack defaults to linear */ +enum JACK_VOLUME_TYPE +JACK_SetVolumeEffectType(int deviceID, enum JACK_VOLUME_TYPE type) +{ + enum JACK_VOLUME_TYPE retval; + jack_driver_t *drv = getDriver(deviceID); + + TRACE("setting type of '%s'\n", + (type == dbAttenuation ? "dbAttenuation" : "linear")); + + retval = drv->volumeEffectType; + drv->volumeEffectType = type; + + releaseDriver(drv); + return retval; +} + + +/* Controls the state of the playback(playing, paused, ...) */ +int +JACK_SetState(int deviceID, enum status_enum state) +{ + jack_driver_t *drv = getDriver(deviceID); + + switch (state) + { + case PAUSED: + drv->state = PAUSED; + break; + case PLAYING: + drv->state = PLAYING; + break; + case STOPPED: + drv->state = STOPPED; + break; + default: + TRACE("unknown state of %d\n", state); + } + + TRACE("%s\n", DEBUGSTATE(drv->state)); + + releaseDriver(drv); + return 0; +} + +/* Retrieve the current state of the device */ +enum status_enum +JACK_GetState(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + enum status_enum return_val; + + return_val = drv->state; + releaseDriver(drv); + + TRACE("deviceID(%d), returning current state of %s\n", deviceID, + DEBUGSTATE(return_val)); + return return_val; +} + +/* Retrieve the number of bytes per second we are outputting */ +unsigned long +JACK_GetOutputBytesPerSecondFromDriver(jack_driver_t * drv) +{ + unsigned long return_val; + + return_val = drv->bytes_per_output_frame * drv->client_sample_rate; + +#if VERBOSE_OUTPUT + TRACE("deviceID(%d), return_val = %ld\n", drv->deviceID, return_val); +#endif + + return return_val; +} + +/* Retrieve the number of bytes per second we are outputting */ +unsigned long +JACK_GetOutputBytesPerSecond(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + unsigned long return_val; + + return_val = JACK_GetOutputBytesPerSecondFromDriver(drv); + releaseDriver(drv); + + return return_val; +} + +/* Retrieve the number of input bytes(from jack) per second we are outputting + to the user of bio2jack */ +static long +JACK_GetInputBytesPerSecondFromDriver(jack_driver_t * drv) +{ + long return_val; + + return_val = drv->bytes_per_input_frame * drv->client_sample_rate; +#if VERBOSE_OUTPUT + TRACE("drv->deviceID(%d), return_val = %ld\n", drv->deviceID, return_val); +#endif + + return return_val; +} + +/* Retrieve the number of input bytes(from jack) per second we are outputting + to the user of bio2jack */ +unsigned long +JACK_GetInputBytesPerSecond(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val = JACK_GetInputBytesPerSecondFromDriver(drv); + releaseDriver(drv); + +#if VERBOSE_OUTPUT + TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val); +#endif + + return return_val; +} + +/* Return the number of bytes we have buffered thus far for output */ +/* NOTE: convert from output bytes to input bytes in here */ +static long +JACK_GetBytesStoredFromDriver(jack_driver_t * drv) +{ + if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0) + return 0; + + /* leave at least one frame in the buffer at all times to prevent underruns */ + long return_val = + jack_ringbuffer_read_space(drv->pPlayPtr) - drv->jack_buffer_size; + if(return_val <= 0) + { + return_val = 0; + } else + { + /* adjust from jack bytes to client bytes */ + return_val = + return_val / drv->bytes_per_jack_output_frame * + drv->bytes_per_output_frame; + } + + return return_val; +} + +/* An approximation of how many bytes we have to send out to jack */ +/* that is computed as if we were sending jack a continuous stream of */ +/* bytes rather than chunks during discrete callbacks. */ +/* Return the number of bytes we have buffered thus far for output */ +/* NOTE: convert from output bytes to input bytes in here */ +unsigned long +JACK_GetBytesStored(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long retval = JACK_GetBytesStoredFromDriver(drv); + releaseDriver(drv); + TRACE("deviceID(%d), retval = %ld\n", deviceID, retval); + return retval; +} + +static unsigned long +JACK_GetBytesFreeSpaceFromDriver(jack_driver_t * drv) +{ + if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0) + return 0; + + /* leave at least one frame in the buffer at all times to prevent underruns */ + long return_val = jack_ringbuffer_write_space(drv->pPlayPtr) - drv->jack_buffer_size; + if(return_val <= 0) + { + return_val = 0; + } else + { + /* adjust from jack bytes to client bytes */ + return_val = + return_val / drv->bytes_per_jack_output_frame * + drv->bytes_per_output_frame; + } + + return return_val; +} + +/* Return the number of bytes we can write to the device */ +unsigned long +JACK_GetBytesFreeSpace(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + unsigned long return_val; + + return_val = JACK_GetBytesFreeSpaceFromDriver(drv); + releaseDriver(drv); + + TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val); + + return return_val; +} + +/* bytes of space used in the input buffer */ +unsigned long +JACK_GetBytesUsedSpace(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val; + + if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0) + { + return_val = 0; + } else + { + /* adjust from jack bytes to client bytes */ + return_val = + jack_ringbuffer_read_space(drv->pRecPtr) / + drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame; + } + + releaseDriver(drv); + + if(return_val < 0) + return_val = 0; + TRACE("deviceID(%d), retval == %ld\n", deviceID, return_val); + + return return_val; +} + +/* Get the current position of the driver, either in bytes or */ +/* in milliseconds */ +/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to + input vs. output channel count */ +static long +JACK_GetPositionFromDriver(jack_driver_t * drv, enum pos_enum position, + int type) +{ + long return_val = 0; + struct timeval now; + long elapsedMS; + double sec2msFactor = 1000; + + char *type_str = "UNKNOWN type"; + + /* if we are reset we should return a position of 0 */ + if(drv->state == RESET) + { + TRACE("we are currently RESET, returning 0\n"); + return 0; + } + + if(type == WRITTEN) + { + type_str = "WRITTEN"; + return_val = drv->client_bytes; + } else if(type == WRITTEN_TO_JACK) + { + type_str = "WRITTEN_TO_JACK"; + return_val = drv->written_client_bytes; + } else if(type == PLAYED) /* account for the elapsed time for the played_bytes */ + { + type_str = "PLAYED"; + return_val = drv->played_client_bytes; + gettimeofday(&now, 0); + + elapsedMS = TimeValDifference(&drv->previousTime, &now); /* find the elapsed milliseconds since last JACK_Callback() */ + + TRACE("elapsedMS since last callback is '%ld'\n", elapsedMS); + + /* account for the bytes played since the last JACK_Callback() */ + /* NOTE: [Xms * (Bytes/Sec)] * (1 sec/1,000ms) */ + /* NOTE: don't do any compensation if no data has been sent to jack since the last callback */ + /* as this would result a bogus computed result */ + if(drv->clientBytesInJack != 0) + { + return_val += (long) ((double) elapsedMS * + ((double) JACK_GetOutputBytesPerSecondFromDriver(drv) / + sec2msFactor)); + } else + { + TRACE("clientBytesInJack == 0\n"); + } + } + + /* add on the offset */ + return_val += drv->position_byte_offset; + + /* convert byte position to milliseconds value if necessary */ + if(position == MILLISECONDS) + { + if(JACK_GetOutputBytesPerSecondFromDriver(drv) != 0) + { + return_val = (long) (((double) return_val / + (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) * + (double) sec2msFactor); + } else + { + return_val = 0; + } + } + + TRACE("drv->deviceID(%d), type(%s), return_val = %ld\n", drv->deviceID, + type_str, return_val); + + return return_val; +} + +/* Get the current position of the driver, either in bytes or */ +/* in milliseconds */ +/* NOTE: this is position relative to input bytes, output bytes may differ greatly due to input vs. output channel count */ +long +JACK_GetPosition(int deviceID, enum pos_enum position, int type) +{ + jack_driver_t *drv = getDriver(deviceID); + long retval = JACK_GetPositionFromDriver(drv, position, type); + releaseDriver(drv); + TRACE("retval == %ld\n", retval); + return retval; +} + +// Set position always applies to written bytes +// NOTE: we must apply this instantly because if we pass this as a message +// to the callback we risk the user sending us audio data in the mean time +// and there is no need to send this as a message, we don't modify any +// internal variables +void +JACK_SetPositionFromDriver(jack_driver_t * drv, enum pos_enum position, + long value) +{ + double sec2msFactor = 1000; +#if TRACE_ENABLE + long input_value = value; +#endif + + /* convert the incoming value from milliseconds into bytes */ + if(position == MILLISECONDS) + { + value = (long) (((double) value * + (double) JACK_GetOutputBytesPerSecondFromDriver(drv)) / + sec2msFactor); + } + + /* ensure that if the user asks for the position */ + /* they will at this instant get the correct position */ + drv->position_byte_offset = value - drv->client_bytes; + + TRACE("deviceID(%d) input_value of %ld %s, new value of %ld, setting position_byte_offset to %ld\n", + drv->deviceID, input_value, (position == MILLISECONDS) ? "ms" : "bytes", + value, drv->position_byte_offset); +} + +// Set position always applies to written bytes +// NOTE: we must apply this instantly because if we pass this as a message +// to the callback we risk the user sending us audio data in the mean time +// and there is no need to send this as a message, we don't modify any +// internal variables +void +JACK_SetPosition(int deviceID, enum pos_enum position, long value) +{ + jack_driver_t *drv = getDriver(deviceID); + JACK_SetPositionFromDriver(drv, position, value); + releaseDriver(drv); + + TRACE("deviceID(%d) value of %ld\n", drv->deviceID, value); +} + +/* Return the number of bytes per frame, or (output_channels * bits_per_channel) / 8 */ +unsigned long +JACK_GetBytesPerOutputFrame(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val = drv->bytes_per_output_frame; + releaseDriver(drv); + TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val); + return return_val; +} + +/* Return the number of bytes per frame, or (input_channels * bits_per_channel) / 8 */ +unsigned long +JACK_GetBytesPerInputFrame(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val = drv->bytes_per_input_frame; + releaseDriver(drv); + TRACE("deviceID(%d), return_val = %ld\n", deviceID, return_val); + return return_val; +} + +/* Return the number of output bytes we buffer max */ +long +JACK_GetMaxOutputBufferedBytes(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val; + + if(drv->pPlayPtr == 0 || drv->bytes_per_jack_output_frame == 0) return_val = 0; + + /* adjust from jack bytes to client bytes */ + return_val = + (jack_ringbuffer_read_space(drv->pPlayPtr) + + jack_ringbuffer_write_space(drv->pPlayPtr)) / + drv->bytes_per_jack_output_frame * drv->bytes_per_output_frame; + + releaseDriver(drv); + + TRACE("return_val = %ld\n", return_val); + + return return_val; +} + +/* Return the number of input bytes we buffer max */ +long +JACK_GetMaxInputBufferedBytes(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val; + + if(drv->pRecPtr == 0 || drv->bytes_per_jack_input_frame == 0) return_val = 0; + + /* adjust from jack bytes to client bytes */ + return_val = + (jack_ringbuffer_read_space(drv->pRecPtr) + + jack_ringbuffer_write_space(drv->pRecPtr)) / + drv->bytes_per_jack_input_frame * drv->bytes_per_input_frame; + + releaseDriver(drv); + + TRACE("return_val = %ld\n", return_val); + + return return_val; +} + +/* Get the number of output channels */ +int +JACK_GetNumOutputChannels(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + int return_val = drv->num_output_channels; + releaseDriver(drv); + TRACE("getting num_output_channels of %d\n", return_val); + return return_val; +} + +/* Get the number of input channels */ +int +JACK_GetNumInputChannels(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + int return_val = drv->num_input_channels; + releaseDriver(drv); + TRACE("getting num_input_channels of %d\n", return_val); + return return_val; +} + +/* Get the number of samples per second, the sample rate */ +long +JACK_GetSampleRate(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + int return_val = drv->client_sample_rate; + releaseDriver(drv); + TRACE("getting sample_rate of %d\n", return_val); + return return_val; +} + +void +JACK_CleanupDriver(jack_driver_t * drv) +{ + TRACE("\n"); + /* things that need to be reset both in JACK_Init & JACK_CloseDevice */ + drv->client = 0; + drv->in_use = FALSE; + drv->state = CLOSED; + drv->jack_sample_rate = 0; + drv->output_sample_rate_ratio = 1.0; + drv->input_sample_rate_ratio = 1.0; + drv->jackd_died = FALSE; + gettimeofday(&drv->previousTime, 0); /* record the current time */ + gettimeofday(&drv->last_reconnect_attempt, 0); +} + +/* Initialize the jack porting library to a clean state */ +void +JACK_Init(void) +{ + jack_driver_t *drv; + int x, y; + + if(init_done) + { + TRACE("not initing twice\n"); + return; + } + + init_done = 1; + + TRACE("\n"); + + pthread_mutex_lock(&device_mutex); + + /* initialize the device structures */ + for(x = 0; x < MAX_OUTDEVICES; x++) + { + drv = &outDev[x]; + + pthread_mutex_init(&drv->mutex, NULL); + + getDriver(x); + + memset(drv, 0, sizeof(jack_driver_t)); + drv->volumeEffectType = linear; + drv->deviceID = x; + + for(y = 0; y < MAX_OUTPUT_PORTS; y++) /* make all volume 25% as a default */ + drv->volume[y] = 25; + + JACK_CleanupDriver(drv); + JACK_ResetFromDriver(drv); + releaseDriver(drv); + } + + client_name = 0; /* initialize the name to null */ + do_sample_rate_conversion = TRUE; /* default to on */ + JACK_SetClientName("bio2jack"); + + pthread_mutex_unlock(&device_mutex); + + TRACE("finished\n"); +} + +/* Get the latency, in frames, of jack */ +long +JACK_GetJackOutputLatency(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val = 0; + + if(drv->client && drv->num_output_channels) + return_val = jack_port_get_total_latency(drv->client, drv->output_port[0]); + + TRACE("got latency of %ld frames\n", return_val); + + releaseDriver(drv); + return return_val; +} + +/* Get the latency, in frames, of jack */ +long +JACK_GetJackInputLatency(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val = 0; + + if(drv->client && drv->num_input_channels) + return_val = jack_port_get_total_latency(drv->client, drv->input_port[0]); + + TRACE("got latency of %ld frames\n", return_val); + + releaseDriver(drv); + return return_val; +} + +/* bytes that jack requests during each callback */ +unsigned long +JACK_GetJackBufferedBytes(int deviceID) +{ + jack_driver_t *drv = getDriver(deviceID); + long return_val; + + if(drv->bytes_per_jack_output_frame == 0) + { + return_val = 0; + } else + { + /* adjust from jack bytes to client bytes */ + return_val = + drv->jack_buffer_size / drv->bytes_per_jack_output_frame * + drv->bytes_per_output_frame * drv->num_output_channels; + } + + releaseDriver(drv); + return return_val; +} + +/* value = TRUE, perform sample rate conversion */ +void +JACK_DoSampleRateConversion(bool value) +{ + do_sample_rate_conversion = value; +} + +/* FIXME: put the filename of the resample library header file with the decoders in here */ +/* consider mapping them in the bio2jack.h header file since its useless to the user unless */ +/* they can figure out wtf the settings on */ +void +JACK_SetSampleRateConversionFunction(int converter) +{ + preferred_src_converter = converter; +} + +/* set the client name that will be reported to jack when we open a */ +/* connection via JACK_OpenDevice() */ +void +JACK_SetClientName(char *name) +{ + if(name) + { + if(client_name) free(client_name); + + /* jack_client_name_size() is the max length of a client name, including + the terminating null. */ + int size = strlen(name) + 1; /* take into account the terminating null */ + if(size > jack_client_name_size()) + size = jack_client_name_size(); + + client_name = malloc(size); + if(client_name) + snprintf(client_name, size, "%s", name); + else + ERR("unable to allocate %d bytes for client_name\n", size); + } +} + +void +JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode) +{ + port_connection_mode = mode; +} diff --git a/src/plugins/Output/jack/bio2jack.h b/src/plugins/Output/jack/bio2jack.h new file mode 100644 index 000000000..f81a7c777 --- /dev/null +++ b/src/plugins/Output/jack/bio2jack.h @@ -0,0 +1,145 @@ +/* + * Copyright 2003-2004 Chris Morgan <cmorgan@alum.wpi.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _H_JACK_OUT_H +#define _H_JACK_OUT_H + +#include <jack/jack.h> + +#ifdef __cplusplus +extern "C" { +#else +#define bool long +#endif + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define ERR_SUCCESS 0 +#define ERR_OPENING_JACK 1 +#define ERR_RATE_MISMATCH 2 +#define ERR_BYTES_PER_OUTPUT_FRAME_INVALID 3 +#define ERR_BYTES_PER_INPUT_FRAME_INVALID 4 +#define ERR_TOO_MANY_OUTPUT_CHANNELS 5 +#define ERR_PORT_NAME_OUTPUT_CHANNEL_MISMATCH 6 +#define ERR_PORT_NOT_FOUND 7 +#define ERR_TOO_MANY_INPUT_CHANNELS 8 +#define ERR_PORT_NAME_INPUT_CHANNEL_MISMATCH 9 + +enum status_enum { PLAYING, PAUSED, STOPPED, CLOSED, RESET }; +enum pos_enum { BYTES, MILLISECONDS }; + +#define PLAYED 1 /* played out of the speakers(estimated value but should be close */ +#define WRITTEN_TO_JACK 2 /* amount written out to jack */ +#define WRITTEN 3 /* amount written to the bio2jack device */ + +/**********************/ +/* External functions */ +void JACK_Init(void); /* call this before any other bio2jack calls */ +void JACK_DoSampleRateConversion(bool value); /* whether the next device that's Open()d should do + sample rate conversion if necessary */ +void JACK_SetSampleRateConversionFunction(int converter); /* which SRC converter function should be used + for the next Open()d device */ +int JACK_Open(int *deviceID, unsigned int bits_per_sample, unsigned long *rate, int channels); /* Note: defaults to 0 input channels + if you need input (record) use OpenEx + instead */ +int JACK_OpenEx(int *deviceID, unsigned int bits_per_channel, + unsigned long *rate, + unsigned int input_channels, unsigned int output_channels, + const char **jack_port_name, unsigned int jack_port_name_count, + unsigned long jack_port_flags); +int JACK_Close(int deviceID); /* return 0 for success */ +void JACK_Reset(int deviceID); /* free all buffered data and reset several values in the device */ +long JACK_Write(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes written */ +long JACK_Read(int deviceID, unsigned char *data, unsigned long bytes); /* returns the number of bytes read */ + +/* state setting values */ +/* set/get the written/played/buffered value based on a byte or millisecond input value */ +long JACK_GetPosition(int deviceID, enum pos_enum position, int type); +void JACK_SetPosition(int deviceID, enum pos_enum position, long value); + +long JACK_GetJackLatency(int deviceID); /* deprectated, you probably want JACK_GetJackOutputLatency */ +long JACK_GetJackOutputLatency(int deviceID); /* return the output latency in frames */ +long JACK_GetJackInputLatency(int deviceID); /* return the input latency in frames */ + +int JACK_SetState(int deviceID, enum status_enum state); /* playing, paused, stopped */ +enum status_enum JACK_GetState(int deviceID); + +long JACK_GetMaxOutputBufferedBytes(int deviceID); +long JACK_GetMaxInputBufferedBytes(int deviceID); + +/* bytes that jack requests during each callback */ +unsigned long JACK_GetJackBufferedBytes(int deviceID); + +/* Properties of the jack driver */ + +/* linear means 0 volume is silence, 100 is full volume */ +/* dbAttenuation means 0 volume is 0dB attenuation */ +/* Bio2jack defaults to linear */ +/* Note: volume controls only effect output channels for now */ +enum JACK_VOLUME_TYPE { linear, dbAttenuation }; +enum JACK_VOLUME_TYPE JACK_SetVolumeEffectType(int deviceID, + enum JACK_VOLUME_TYPE type); + +int JACK_SetAllVolume(int deviceID, unsigned int volume); /* returns 0 on success */ +int JACK_SetVolumeForChannel(int deviceID, unsigned int channel, unsigned int volume); +void JACK_GetVolumeForChannel(int deviceID, unsigned int channel, unsigned int *volume); + + +unsigned long JACK_GetOutputBytesPerSecond(int deviceID); /* bytes_per_output_frame * sample_rate */ +unsigned long JACK_GetInputBytesPerSecond(int deviceID); /* bytes_per_input_frame * sample_rate */ +unsigned long JACK_GetBytesStored(int deviceID); /* bytes currently buffered in the output buffer */ +unsigned long JACK_GetBytesFreeSpace(int deviceID); /* bytes of free space in the output buffer */ +unsigned long JACK_GetBytesUsedSpace(int deviceID); /* bytes of space used in the input buffer */ +unsigned long JACK_GetBytesPerOutputFrame(int deviceID); +unsigned long JACK_GetBytesPerInputFrame(int deviceID); + +/* Note: these will probably be removed in a future release */ +int JACK_GetNumInputChannels(int deviceID); +int JACK_GetNumOutputChannels(int deviceID); + +long JACK_GetSampleRate(int deviceID); /* samples per second */ + +void JACK_SetClientName(char *name); /* sets the name that bio2jack will use when + creating a new jack client. name_%pid%_%deviceID%%counter% + will be used + NOTE: this defaults to name = bio2jack + NOTE: we limit the size of the client name to + jack_client_name_size() */ + +enum JACK_PORT_CONNECTION_MODE +{ + CONNECT_ALL, /* connect to all avaliable ports */ + CONNECT_OUTPUT, /* connect only to the ports we need for output */ + CONNECT_NONE /* don't connect to any ports */ +}; + +/* set the mode for port connections */ +/* defaults to CONNECT_ALL */ +void JACK_SetPortConnectionMode(enum JACK_PORT_CONNECTION_MODE mode); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef JACK_OUT_H */ diff --git a/src/plugins/Output/jack/jack.pro b/src/plugins/Output/jack/jack.pro new file mode 100644 index 000000000..a943a5b3b --- /dev/null +++ b/src/plugins/Output/jack/jack.pro @@ -0,0 +1,33 @@ +include(../../plugins.pri) + +HEADERS += outputjackfactory.h \ + outputjack.h \ + bio2jack.h + +SOURCES += outputjackfactory.cpp \ + outputjack.cpp \ + bio2jack.c + +TARGET=$$PLUGINS_PREFIX/Output/jack +QMAKE_CLEAN =$$PLUGINS_PREFIX/Output/libjack.so + + +INCLUDEPATH += ../../../qmmp +QMAKE_LIBDIR += ../../../../lib +CONFIG += release \ +warn_on \ +thread \ +plugin \ +link_pkgconfig +TEMPLATE = lib +LIBS += -lqmmp +PKGCONFIG += jack samplerate +#TRANSLATIONS = translations/jack_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/jack/outputjack.cpp b/src/plugins/Output/jack/outputjack.cpp new file mode 100644 index 000000000..c8d3ebe86 --- /dev/null +++ b/src/plugins/Output/jack/outputjack.cpp @@ -0,0 +1,208 @@ +#include <QObject> +#include <QApplication> +#include <QtGlobal> +#include <QDir> +#include <QSettings> + +#include "outputjack.h" +#include "constants.h" +#include "buffer.h" +#include "visual.h" + +#include <stdio.h> +#include <string.h> + +void OutputJACK::stop() +{ + m_userStop = TRUE; +} + +void OutputJACK::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 OutputJACK::written() +{ + return m_totalWritten; +} + +void OutputJACK::seek(long pos) +{ + recycler()->mutex()->lock (); + recycler()->clear(); + recycler()->mutex()->unlock(); + + m_totalWritten = (pos * m_bps); + m_currentSeconds = -1; +} + +OutputJACK::OutputJACK(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) +{ + JACK_Init(); +} + +OutputJACK::~OutputJACK() +{ + uninitialize(); +} + +void OutputJACK::configure(long freq, int chan, int prec, int brate) +{ + qDebug("OutputJACK: configure"); + m_precision = prec; + m_channels = chan; + m_frequency = freq; + m_bps = freq * chan * (prec / 8); + if(JACK_Open(&jack_device, prec, (unsigned long *)&freq, chan)) + { + m_configure = FALSE; + return; + } + else + m_configure = TRUE; + + qDebug("OutputJACK: configure end"); +} + +void OutputJACK::pause() +{ + m_pause = (m_pause) ? FALSE : TRUE; + { + int state = m_pause ? OutputState::Paused: OutputState::Playing; + dispatch(OutputState((OutputState::Type) state)); + } + +} + +bool OutputJACK::initialize() +{ + m_inited = m_pause = m_play = m_userStop = FALSE; + m_currentSeconds = -1; + m_totalWritten = 0; + if (m_inited) + m_inited = TRUE; + m_inited = TRUE; + m_configure = FALSE; + jack_options_t options = JackNullOption; + jack_status_t status; + jack_client_t *client = jack_client_open ("test_qmmp", options, &status, NULL); + if (client == NULL) + { + qDebug("jack_client_open() failed."); + if (status & JackServerFailed) + { + qDebug("Unable to connect to JACK server."); + } + return FALSE; + } + jack_client_close (client); + return TRUE; +} + +long OutputJACK::latency() +{ + ulong used = 0; + return used; +} + +void OutputJACK::run() +{ + mutex()->lock (); + if (! m_inited) + { + mutex()->unlock(); + return; + } + + m_play = TRUE; + Buffer *b = 0; + bool done = FALSE; + long m = 0; + unsigned long l; + + dispatch(OutputState::Playing); + + mutex()->unlock(); + while (!done&&m_configure) + { + 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 = int(b->nbytes); + unsigned char *buf = b->data; + m_totalWritten += l; + while (l > 0) + { + m = JACK_Write(jack_device, (unsigned char*)buf, l); + if (!m) + usleep(2000); + usleep(((m/m_channels)*100000)/m_frequency); + l-=m; + buf+=m; + } + + status(); + dispatchVisual(b, m_totalWritten, m_channels, m_precision); + } + recycler()->mutex()->lock (); + recycler()->done(); + recycler()->mutex()->unlock(); + b = 0; + mutex()->unlock(); + } + mutex()->lock (); + m_play = FALSE; + dispatch(OutputState::Stopped); + mutex()->unlock(); +} + +void OutputJACK::uninitialize() +{ + if (!m_inited) + return; + m_inited = FALSE; + m_inited = m_pause = m_play = m_userStop = FALSE; + m_currentSeconds = -1; + m_totalWritten = 0; + if (m_configure) + JACK_Close(jack_device); + dispatch(OutputState::Stopped); +} + diff --git a/src/plugins/Output/jack/outputjack.h b/src/plugins/Output/jack/outputjack.h new file mode 100644 index 000000000..e01ced315 --- /dev/null +++ b/src/plugins/Output/jack/outputjack.h @@ -0,0 +1,49 @@ +#ifndef OUTPUTJACK_H +#define OUTPUTJACK_H + +class OutputJACK; + +#include <output.h> +#include <QObject> +extern "C" +{ +#include <jack/jack.h> +} + +#include "bio2jack.h" + +class OutputJACK : public Output +{ + Q_OBJECT +public: + OutputJACK(QObject * parent = 0, bool useVolume = TRUE); + ~OutputJACK(); + 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); + +private: + // thread run function + void run(); + // helper functions + void status(); + QString audio_device; + bool m_inited, m_configure, m_pause, m_play, m_userStop; + long m_totalWritten, m_currentSeconds, m_bps; + int m_rate, m_frequency, m_channels, m_precision, jack_device; + bool do_select; + int audio_fd; +}; + + +#endif + diff --git a/src/plugins/Output/jack/outputjackfactory.cpp b/src/plugins/Output/jack/outputjackfactory.cpp new file mode 100644 index 000000000..b41ba4487 --- /dev/null +++ b/src/plugins/Output/jack/outputjackfactory.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2007 by Zhuravlev Uriy * + * stalkerg@gmail.com * + * * + * 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 "outputjack.h" +#include "outputjackfactory.h" + + +const OutputProperties OutputJACKFactory::properties() const +{ + OutputProperties properties; + properties.name = tr("JACK Plugin"); + properties.hasAbout = TRUE; + properties.hasSettings = TRUE; + return properties; +} + +Output* OutputJACKFactory::create(QObject* parent, bool volume) +{ + return new OutputJACK(parent); +} + +void OutputJACKFactory::showSettings(QWidget*) +{ +} + +void OutputJACKFactory::showAbout(QWidget *parent) +{ +QMessageBox::about (parent, tr("About Jack Output Plugin"), + tr("Qmmp Jack Output Plugin")+"\n"+ + tr("Writen by: Yuriy Zhuravlev <slalkerg@gmail.com>")); +} + +QTranslator *OutputJACKFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/jack_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(OutputJACKFactory) diff --git a/src/plugins/Output/jack/outputjackfactory.h b/src/plugins/Output/jack/outputjackfactory.h new file mode 100644 index 000000000..cc27990f1 --- /dev/null +++ b/src/plugins/Output/jack/outputjackfactory.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * 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 OUTPUTJACKFACTORY_H +#define OUTPUTJACKFACTORY_H + + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <output.h> +#include <outputfactory.h> + + +class OutputJACKFactory : 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/jack/translations/jack_plugin_ru.qm b/src/plugins/Output/jack/translations/jack_plugin_ru.qm Binary files differnew file mode 100644 index 000000000..4050034bc --- /dev/null +++ b/src/plugins/Output/jack/translations/jack_plugin_ru.qm diff --git a/src/plugins/Output/jack/translations/jack_plugin_ru.ts b/src/plugins/Output/jack/translations/jack_plugin_ru.ts new file mode 100644 index 000000000..6f5057529 --- /dev/null +++ b/src/plugins/Output/jack/translations/jack_plugin_ru.ts @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!DOCTYPE TS><TS version="1.1" language="ru"> +<context> + <name>OutputJACKFactory</name> + <message> + <location filename="../outputjackfactory.cpp" line="29"/> + <source>JACK Plugin</source> + <translation>Модуль JACK</translation> + </message> + <message> + <location filename="../outputjackfactory.cpp" line="44"/> + <source>About Jack Output Plugin</source> + <translation>О модуле вывода Jack</translation> + </message> + <message> + <location filename="../outputjackfactory.cpp" line="45"/> + <source>Qmmp Jack Output Plugin</source> + <translation>Модуль вывода Jack для Qmmp</translation> + </message> + <message> + <location filename="../outputjackfactory.cpp" line="46"/> + <source>Writen by: Yuriy Zhuravlev <slalkerg@gmail.com></source> + <translation>Разработчик: Юрий Журавлёв <slalkerg@gmail.com></translation> + </message> +</context> +</TS> diff --git a/src/plugins/Output/jack/translations/translations.qrc b/src/plugins/Output/jack/translations/translations.qrc new file mode 100644 index 000000000..af9447328 --- /dev/null +++ b/src/plugins/Output/jack/translations/translations.qrc @@ -0,0 +1,6 @@ +<!DOCTYPE RCC> +<RCC version="1.0"> + <qresource> + <file>jack_plugin_ru.qm</file> + </qresource> +</RCC> diff --git a/src/plugins/Output/oss/CMakeLists.txt b/src/plugins/Output/oss/CMakeLists.txt new file mode 100644 index 000000000..8ab6f8d88 --- /dev/null +++ b/src/plugins/Output/oss/CMakeLists.txt @@ -0,0 +1,67 @@ +project(liboss) + +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}/../../../) + + + +include_directories(${JACK_INCLUDE_DIR} ${JACK_INCLUDE_DIR}) +link_directories(${SAMPLERATE_LINK_DIR} ${SAMPLERATE_LINK_DIR}) + +ADD_DEFINITIONS(${JACK_CFLAGS}) +ADD_DEFINITIONS(${SAMPLERATE_CFLAGS}) + + +SET(liboss_SRCS + outputossfactory.cpp + outputoss.cpp + settingsdialog.cpp +) + +SET(liboss_MOC_HDRS + outputossfactory.h + outputoss.h + settingsdialog.h +) + +#SET(libjack_RCCS translations/translations.qrc) + +QT4_ADD_RESOURCES(libjack_RCC_SRCS ${libjack_RCCS}) + +QT4_WRAP_CPP(liboss_MOC_SRCS ${liboss_MOC_HDRS}) + +SET(liboss_UIS + settingsdialog.ui +) + +QT4_WRAP_UI(liboss_UIS_H ${liboss_UIS}) + +ADD_LIBRARY(oss SHARED ${liboss_SRCS} ${liboss_MOC_SRCS} ${liboss_UIS_H}) +target_link_libraries(oss ${QT_LIBRARIES} -lqmmp ) +install(TARGETS oss 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/oss/oss.pro b/src/plugins/Output/oss/oss.pro new file mode 100644 index 000000000..e401f14b0 --- /dev/null +++ b/src/plugins/Output/oss/oss.pro @@ -0,0 +1,36 @@ +include(../../plugins.pri) +FORMS += settingsdialog.ui + +HEADERS += outputossfactory.h \ + outputoss.h \ + settingsdialog.h + + +SOURCES += outputossfactory.cpp \ + outputoss.cpp \ + settingsdialog.cpp + +TARGET=$$PLUGINS_PREFIX/Output/oss +QMAKE_CLEAN =$$PLUGINS_PREFIX/Output/liboss.so + + +INCLUDEPATH += ../../../qmmp +QMAKE_LIBDIR += ../../../../lib +CONFIG += release \ +warn_on \ +thread \ +plugin + +TEMPLATE = lib +LIBS += -lqmmp + +#TRANSLATIONS = translations/oss_plugin_ru.ts \ +# translations/oss_plugin_uk_UA.ts \ +# translations/oss_plugin_zh_CN.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/oss/outputoss.cpp b/src/plugins/Output/oss/outputoss.cpp new file mode 100644 index 000000000..9c52ac777 --- /dev/null +++ b/src/plugins/Output/oss/outputoss.cpp @@ -0,0 +1,505 @@ +/*************************************************************************** + * Copyright (C) 2007 by Uriy Zhuravlev stalkerg@gmail.com * + * * + * Copyright (c) 2000-2001 Brad Hughes bhughes@trolltech.com * + * * + * 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 <QApplication> + +#include "outputoss.h" +#include "constants.h" +#include "buffer.h" +#include "visual.h" + +#include <stdio.h> +#include <string.h> +#include <QtGlobal> +#include <QSettings> +#include <QDir> + +#include <iostream> + +//extern Q_EXPORT QApplication* qApp; + + +void OutputOSS::stop() +{ + m_userStop = TRUE; +} + +void OutputOSS::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 OutputOSS::written() +{ + return m_totalWritten; +} + +void OutputOSS::seek(long pos) +{ + recycler()->mutex()->lock(); + recycler()->clear(); + recycler()->mutex()->unlock(); + + m_totalWritten = (pos * m_bps); + m_currentSeconds = -1; +} + + +#include <unistd.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <sys/time.h> + +#if defined(__FreeBSD__) +# include <sys/soundcard.h> +#elif defined(__linux__) +# include <linux/soundcard.h> +#elif defined(__bsdi__) +# include <sys/soundcard.h> +#endif + + +OutputOSS::OutputOSS(QObject * parent) + : 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), + do_select(TRUE), + m_audio_fd(-1), m_mixer_fd(-1) +{ +QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); +m_master = true; +m_audio_device = settings.value("OSS/device","/dev/dsp").toString(); +m_mixer_device = settings.value("OSS/mixer_device","/dev/mixer").toString(); +openMixer(); +} + +OutputOSS::~OutputOSS() +{ + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + if (m_mixer_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } +} + +void OutputOSS::configure(long freq, int chan, int prec, int rate) +{ + // we need to configure + if (freq != m_frequency || chan != m_channels || prec != m_precision) { + // we have already configured, but are changing settings... + // reset the device + resetDSP(); + + m_frequency = freq; + m_channels = chan; + m_precision = prec; + + m_bps = freq * chan * (prec / 8); + + int p; + switch(prec) { + default: + case 16: +#if defined(AFMT_S16_NE) + p = AFMT_S16_NE; +#else + p = AFMT_S16_LE; +#endif + break; + + case 8: + p = AFMT_S8; + break; + + } + + ioctl(m_audio_fd, SNDCTL_DSP_SETFMT, &p); + ioctl(m_audio_fd, SNDCTL_DSP_SAMPLESIZE, &prec); + int stereo = (chan > 1) ? 1 : 0; + ioctl(m_audio_fd, SNDCTL_DSP_STEREO, &stereo); + ioctl(m_audio_fd, SNDCTL_DSP_SPEED, &freq); + } + + m_rate = rate; +} + + +void OutputOSS::reset() +{ + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + + m_audio_fd = open(m_audio_device.toAscii(), O_WRONLY, 0); + + if (m_audio_fd < 0) + { + error(QString("OSSOutput: failed to open output device '%1'"). + arg(m_audio_device)); + return; + } + + int flags; + if ((flags = fcntl(m_audio_fd, F_GETFL, 0)) > 0) + { + flags &= O_NDELAY; + fcntl(m_audio_fd, F_SETFL, flags); + } + + fd_set afd; + FD_ZERO(&afd); + FD_SET(m_audio_fd, &afd); + struct timeval tv; + tv.tv_sec = 0l; + tv.tv_usec = 50000l; + do_select = (select(m_audio_fd + 1, 0, &afd, 0, &tv) > 0); + + if (m_audio_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } + openMixer(); +} + +void OutputOSS::openMixer() +{ + if (m_mixer_fd != -1) + return; + + if ((m_mixer_fd = open(m_mixer_device.toAscii(), O_RDWR)) == -1) + { + return; + } + if (m_audio_fd < 0) + { + error(QString("OSSOutput: failed to open mixer device '%1'"). + arg(m_mixer_device)); + return; + } +} + +void OutputOSS::pause() +{ + m_pause = (m_pause) ? FALSE : TRUE; +} + +void OutputOSS::post() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_POST, &unused); +} + +void OutputOSS::sync() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_SYNC, &unused); +} + + +void OutputOSS::resetDSP() +{ + if (m_audio_fd < 1) + return; + + int unused; + ioctl(m_audio_fd, SNDCTL_DSP_RESET, &unused); +} + + +bool OutputOSS::initialize() +{ + m_inited = m_pause = m_play = m_userStop = FALSE; + + + reset(); + if (m_audio_fd < 0) + return FALSE; + if (m_mixer_fd < 0) + return FALSE; + + + m_currentSeconds = -1; + m_totalWritten = 0; + stat = OutputState::Stopped; + + m_inited = TRUE; + return TRUE; +} + +void OutputOSS::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; + resetDSP(); + if (m_audio_fd > 0) + { + close(m_audio_fd); + m_audio_fd = -1; + } + if (m_audio_fd > 0) + { + close(m_mixer_fd); + m_mixer_fd = -1; + } + + qDebug("OutputOSS: uninitialize"); + dispatch(OutputState::Stopped); +} + +long OutputOSS::latency() +{ + ulong used = 0; + + if (! m_pause) + { + if (ioctl(m_audio_fd, SNDCTL_DSP_GETODELAY, &used) == -1) + used = 0; + } + + return used; +} + +void OutputOSS::run() +{ + mutex()->lock(); + + if (! m_inited) + { + mutex()->unlock(); + + return; + } + + m_play = TRUE; + + mutex()->unlock(); + + fd_set afd; + struct timeval tv; + Buffer *b = 0; + bool done = FALSE; + unsigned long n = 0, m = 0, l = 0; + + dispatch(OutputState::Playing); + + FD_ZERO(&afd); + + while (! done) { + mutex()->lock(); + + recycler()->mutex()->lock(); + + done = m_userStop; + + while (! done && (recycler()->empty() || m_pause)) { + post(); + + mutex()->unlock(); + + { + stat = m_pause ? OutputState::Paused : OutputState::Buffering; + OutputState e((OutputState::Type) stat); + dispatch(e); + } + + 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(); + + FD_ZERO(&afd); + FD_SET(m_audio_fd, &afd); + // nice long poll timeout + tv.tv_sec = 5l; + tv.tv_usec = 0l; + + if (b && + (! do_select || (select(m_audio_fd + 1, 0, &afd, 0, &tv) > 0 && + FD_ISSET(m_audio_fd, &afd)))) { + l = qMin(int(2048), int(b->nbytes - n)); + if (l > 0) { + m = write(m_audio_fd, b->data + n, l); + n += m; + + status(); + dispatchVisual(b, m_totalWritten, m_channels, m_precision); + } else { + // force buffer change + n = b->nbytes; + m = 0; + } + } + + m_totalWritten += m; + + if (n == b->nbytes) { + recycler()->mutex()->lock(); + recycler()->done(); + recycler()->mutex()->unlock(); + + b = 0; + n = 0; + } + + mutex()->unlock(); + } + + mutex()->lock(); + + if (! m_userStop) + sync(); + resetDSP(); + + m_play = FALSE; + + dispatch(OutputState::Stopped); + mutex()->unlock(); +} + + +void OutputOSS::setVolume(int l, int r) +{ + int v, devs; + long cmd; + + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == false)) + cmd = SOUND_MIXER_WRITE_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == true)) + cmd = SOUND_MIXER_WRITE_VOLUME; + else + { + //close(mifd); + return; + } + v = (r << 8) | l; + ioctl(m_mixer_fd, cmd, &v); +} + +void OutputOSS::volume(int *ll,int *rr) +{ + *ll = 0; + *rr = 0; + int cmd; + int v, devs; + + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == 0)) + cmd = SOUND_MIXER_READ_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == 1)) + cmd = SOUND_MIXER_READ_VOLUME; + else + return; + + ioctl(m_mixer_fd, cmd, &v); + *ll = (v & 0xFF00) >> 8; + *rr = (v & 0x00FF); + + *ll = (*ll > 100) ? 100 : *ll; + *rr = (*rr > 100) ? 100 : *rr; + *ll = (*ll < 0) ? 0 : *ll; + *rr = (*rr < 0) ? 0 : *rr; +} + + +void OutputOSS::checkVolume() +{ + long ll = 0, lr = 0, cmd; + int v, devs; + + /* + * We dont show any errors if this fails, as this is called + * rather often + */ +// if (!open_mixer_device()) { + ioctl(m_mixer_fd, SOUND_MIXER_READ_DEVMASK, &devs); + if ((devs & SOUND_MASK_PCM) && (m_master == 0)) + cmd = SOUND_MIXER_READ_PCM; + else if ((devs & SOUND_MASK_VOLUME) && (m_master == 1)) + cmd = SOUND_MIXER_READ_VOLUME; + else + return; + + ioctl(m_mixer_fd, cmd, &v); + ll = (v & 0xFF00) >> 8; + lr = (v & 0x00FF); + + ll = (ll > 100) ? 100 : ll; + lr = (lr > 100) ? 100 : lr; + ll = (ll < 0) ? 0 : ll; + lr = (lr < 0) ? 0 : lr; + if (bl!=ll || br!=lr) + { + bl = ll; + br = lr; + dispatchVolume(ll,lr); + } +// } +} + + diff --git a/src/plugins/Output/oss/outputoss.h b/src/plugins/Output/oss/outputoss.h new file mode 100644 index 000000000..a2f260057 --- /dev/null +++ b/src/plugins/Output/oss/outputoss.h @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2007 by Uriy Zhuravlev stalkerg@gmail.com * + * * + * Copyright (c) 2000-2001 Brad Hughes bhughes@trolltech.com * + * * + * 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 OUTPUTOSS_H +#define OUTPUTOSS_H + +class OutputOSS; + +#include <output.h> +#include <QObject> + +class OutputOSS : public Output +{ +Q_OBJECT +public: + OutputOSS(QObject * parent = 0); + virtual ~OutputOSS(); + + bool isInitialized() const { return m_inited; } + bool initialize(); + 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 resetDSP(); + void status(); + void post(); + void sync(); + void openMixer(); + + QString m_audio_device, m_mixer_device; + + bool m_inited, m_pause, m_play, m_userStop, m_master; + long m_totalWritten, m_currentSeconds, m_bps; + int stat; + int m_rate, m_frequency, m_channels, m_precision; + + bool do_select; + int m_audio_fd, m_mixer_fd; + long bl, br; +}; + + +#endif diff --git a/src/plugins/Output/oss/outputossfactory.cpp b/src/plugins/Output/oss/outputossfactory.cpp new file mode 100644 index 000000000..3fa8c07a2 --- /dev/null +++ b/src/plugins/Output/oss/outputossfactory.cpp @@ -0,0 +1,70 @@ +/*************************************************************************** + * Copyright (C) 2007 by Zhuravlev Uriy * + * stalkerg@gmail.com * + * * + * 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 "outputoss.h" +#include "outputossfactory.h" + + +const QString& OutputOSSFactory::name() const +{ + static QString name(tr("OSS Plugin")); + return name; +} + +Output* OutputOSSFactory::create(QObject* parent,bool) +{ + return new OutputOSS(parent); +} + +const OutputProperties OutputOSSFactory::properties() const +{ + OutputProperties properties; + properties.name = name(); + properties.hasAbout = TRUE; + properties.hasSettings = TRUE; + return properties; +} + +void OutputOSSFactory::showSettings(QWidget* parent) +{ + SettingsDialog *s = new SettingsDialog(parent); + s -> show(); +} + +void OutputOSSFactory::showAbout(QWidget *parent) +{ +QMessageBox::about (parent, tr("About OSS Output Plugin"), + tr("Qmmp OSS Output Plugin")+"\n"+ + tr("Writen by: Yuriy Zhuravlev <slalkerg@gmail.com>")+"\n"+ + tr("Based on code by:Brad Hughes <bhughes@trolltech.com>")); +} + +QTranslator *OutputOSSFactory::createTranslator(QObject *parent) +{ + QTranslator *translator = new QTranslator(parent); + QString locale = QLocale::system().name(); + translator->load(QString(":/oss_plugin_") + locale); + return translator; +} + +Q_EXPORT_PLUGIN(OutputOSSFactory) diff --git a/src/plugins/Output/oss/outputossfactory.h b/src/plugins/Output/oss/outputossfactory.h new file mode 100644 index 000000000..03166bb94 --- /dev/null +++ b/src/plugins/Output/oss/outputossfactory.h @@ -0,0 +1,48 @@ +/*************************************************************************** + * 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 OUTPUTOSSFACTORY_H +#define OUTPUTOSSFACTORY_H + + +#include <QObject> +#include <QString> +#include <QIODevice> +#include <QWidget> + +#include <output.h> +#include <outputfactory.h> + + +class OutputOSSFactory : public QObject, + OutputFactory +{ +Q_OBJECT +Q_INTERFACES(OutputFactory); + +public: + const QString& name() const; + Output* create(QObject* parent,bool); + void showSettings(QWidget* parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); + const OutputProperties properties() const; +}; + +#endif diff --git a/src/plugins/Output/oss/settingsdialog.cpp b/src/plugins/Output/oss/settingsdialog.cpp new file mode 100644 index 000000000..8d75b06eb --- /dev/null +++ b/src/plugins/Output/oss/settingsdialog.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2007 by Zhuravlev Uriy * + * stalkerg@gmail.com * + * * + * 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> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog ( QWidget *parent ) + : QDialog ( parent ) +{ + ui.setupUi ( this ); + setAttribute ( Qt::WA_DeleteOnClose ); + connect(ui.okButton, SIGNAL(clicked()), SLOT(writeSettings())); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("OSS"); + ui.lineEdit->insert(settings.value("device","/dev/dsp").toString()); + ui.lineEdit_2->insert(settings.value("mixer_device","/dev/mixer").toString()); + ui.bufferSpinBox->setValue(settings.value("buffer_time",500).toInt()); + ui.periodSpinBox->setValue(settings.value("period_time",100).toInt()); + + settings.endGroup(); +} + + +SettingsDialog::~SettingsDialog() +{} + + + +void SettingsDialog::writeSettings() +{ + qDebug("SettingsDialog (OSS):: writeSettings()"); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.beginGroup("OSS"); + settings.setValue("device", ui.lineEdit->text()); + settings.setValue("buffer_time",ui.bufferSpinBox->value()); + settings.setValue("period_time",ui.periodSpinBox->value()); + settings.setValue("mixer_device", ui.lineEdit_2->text()); + settings.endGroup(); + accept(); +} + + diff --git a/src/plugins/Output/oss/settingsdialog.h b/src/plugins/Output/oss/settingsdialog.h new file mode 100644 index 000000000..fd75c5698 --- /dev/null +++ b/src/plugins/Output/oss/settingsdialog.h @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright (C) 2007 by Zhuravlev Uriy * + * stalkerg@gmail.com * + * * + * 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> + + +#include "ui_settingsdialog.h" + +/** + @author Yuriy Zhuravlev <stalkerg@gmail.com> +*/ +class SettingsDialog : public QDialog +{ +Q_OBJECT +public: + SettingsDialog(QWidget *parent = 0); + + ~SettingsDialog(); + +private slots: + void writeSettings(); + +private: + Ui::SettingsDialog ui; + +}; + +#endif diff --git a/src/plugins/Output/oss/settingsdialog.ui b/src/plugins/Output/oss/settingsdialog.ui new file mode 100644 index 000000000..ce1c40894 --- /dev/null +++ b/src/plugins/Output/oss/settingsdialog.ui @@ -0,0 +1,309 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>422</width> + <height>334</height> + </rect> + </property> + <property name="windowTitle" > + <string>OSS Plugin Settings</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <property name="horizontalSpacing" > + <number>6</number> + </property> + <property name="verticalSpacing" > + <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="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Audio device</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QLineEdit" name="lineEdit" > + <property name="text" > + <string/> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2" > + <property name="title" > + <string>Mixer device</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <property name="horizontalSpacing" > + <number>6</number> + </property> + <property name="verticalSpacing" > + <number>6</number> + </property> + <item row="0" column="0" > + <widget class="QLineEdit" name="lineEdit_2" > + <property name="text" > + <string/> + </property> + </widget> + </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="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox_3" > + <property name="title" > + <string>Soundcard</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <property name="horizontalSpacing" > + <number>6</number> + </property> + <property name="verticalSpacing" > + <number>6</number> + </property> + <item row="3" 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="minimum" > + <number>20</number> + </property> + <property name="maximum" > + <number>5000</number> + </property> + <property name="value" > + <number>100</number> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QSpinBox" name="bufferSpinBox" > + <property name="minimum" > + <number>200</number> + </property> + <property name="maximum" > + <number>10000</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> + <item row="2" column="1" > + <widget class="QCheckBox" name="checkBox" > + <property name="text" > + <string/> + </property> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>PCM over Master</string> + </property> + </widget> + </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/oss/translations/oss_plugin_cs.ts b/src/plugins/Output/oss/translations/oss_plugin_cs.ts new file mode 100644 index 000000000..424d320e7 --- /dev/null +++ b/src/plugins/Output/oss/translations/oss_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>OutputOSSFactory</name> + <message> + <location filename="../outputossfactory.cpp" line="30"/> + <source>OSS Plugin</source> + <translation>Plugin OSS</translation> + </message> + <message> + <location filename="../outputossfactory.cpp" line="47"/> + <source>About OSS Output Plugin</source> + <translation>O pluginu OSS</translation> + </message> + <message> + <location filename="../outputossfactory.cpp" line="48"/> + <source>Qmmp OSS Output Plugin</source> + <translation>Výstupní plugin Qmmp OSS</translation> + </message> + <message> + <location filename="../outputossfactory.cpp" line="49"/> + <source>Writen by: Yuriy Zhuravlev <slalkerg@gmail.com></source> + <translation>Autor: Jurij Žuravljov <slalkerg@gmail.com></translation> + </message> + <message> + <location filename="../outputossfactory.cpp" line="50"/> + <source>Based on code by:Brad Hughes <bhughes@trolltech.com></source> + <translation>Založeno na kódu Brada Hughese <bhughes@trolltech.com></translation> + </message> +</context> +<context> + <name>SettingsDialog</name> + <message> + <location filename="../ui_settingsdialog.h" line="202"/> + <source>OSS Plugin Settings</source> + <translation>Nastavení pluginu OSS</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="207"/> + <source>Device Settings</source> + <translation>Nastavení zařízení</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="203"/> + <source>Audio device</source> + <translation>Zvukové zařízení</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="205"/> + <source>Mixer device</source> + <translation>Ovládání hlasitosti</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="213"/> + <source>Advanced Settings</source> + <translation>Pokročilá nastavení</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="208"/> + <source>Soundcard</source> + <translation>Zvuková karta</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="209"/> + <source>Buffer time (ms):</source> + <translation>Velikost bufferu (ms):</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="210"/> + <source>Period time (ms):</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="212"/> + <source>PCM over Master</source> + <translation type="unfinished"></translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="214"/> + <source>Cancel</source> + <translation>Zrušit</translation> + </message> + <message> + <location filename="../ui_settingsdialog.h" line="215"/> + <source>OK</source> + <translation>OK</translation> + </message> +</context> +</TS> diff --git a/src/plugins/Visual/CMakeLists.txt b/src/plugins/Visual/CMakeLists.txt new file mode 100644 index 000000000..df35bdf14 --- /dev/null +++ b/src/plugins/Visual/CMakeLists.txt @@ -0,0 +1,8 @@ +SET(USE_ANALYZER TRUE CACHE BOOL "enable/disable analyzer plugin") + +IF(USE_ANALYZER) +MESSAGE( STATUS "ANALYZER ON") +add_subdirectory(analyzer) +ELSE(USE_ANALYZER) +MESSAGE( STATUS "ANALYZER OFF") +ENDIF(USE_ANALYZER) diff --git a/src/plugins/Visual/Visual.pro b/src/plugins/Visual/Visual.pro new file mode 100644 index 000000000..e30196780 --- /dev/null +++ b/src/plugins/Visual/Visual.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS += analyzer diff --git a/src/plugins/Visual/analyzer/CMakeLists.txt b/src/plugins/Visual/analyzer/CMakeLists.txt new file mode 100644 index 000000000..7de2ca687 --- /dev/null +++ b/src/plugins/Visual/analyzer/CMakeLists.txt @@ -0,0 +1,70 @@ +project(libanalyzer) + +cmake_minimum_required(VERSION 2.4.0) + + + +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(libanalyzer_SRCS + analyzer.cpp + colorwidget.cpp + settingsdialog.cpp + visualanalyzerfactory.cpp + fft.c +) + +SET(libanalyzer_MOC_HDRS + analyzer.h + colorwidget.h + fft.h + inlines.h + settingsdialog.h + visualanalyzerfactory.h +) + +#SET(libanalyzer_RCCS translations/translations.qrc) + +#QT4_ADD_RESOURCES(libanalyzer_RCC_SRCS ${libanalyzer_RCCS}) + +QT4_WRAP_CPP(libanalyzer_MOC_SRCS ${libanalyzer_MOC_HDRS}) + +# user interface + + +SET(libanalyzer_UIS + settingsdialog.ui +) + +QT4_WRAP_UI(libanalyzer_UIS_H ${libanalyzer_UIS}) +# Don't forget to include output directory, otherwise +# the UI file won't be wrapped! +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +ADD_LIBRARY(analyzer SHARED ${libanalyzer_SRCS} ${libanalyzer_MOC_SRCS} ${libanalyzer_UIS_H} + ${libanalyzer_RCC_SRCS}) +target_link_libraries(analyzer ${QT_LIBRARIES} -lqmmp) +install(TARGETS analyzer DESTINATION ${LIB_DIR}/qmmp/Visual PERMISSIONS OWNER_WRITE OWNER_READ OWNER_EXECUTE GROUP_EXECUTE GROUP_READ WORLD_EXECUTE WORLD_READ) + diff --git a/src/plugins/Visual/analyzer/analyzer.cpp b/src/plugins/Visual/analyzer/analyzer.cpp new file mode 100644 index 000000000..efcb8b7d7 --- /dev/null +++ b/src/plugins/Visual/analyzer/analyzer.cpp @@ -0,0 +1,308 @@ +/*************************************************************************** + * 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 <QTimer> +#include <QSettings> +#include <QPainter> +#include <QMenu> +#include <QActionGroup> + +#include <buffer.h> +#include <constants.h> +#include <output.h> +#include <math.h> +#include <stdlib.h> + +//#include "skin.h" +#include "fft.h" +#include "inlines.h" +#include "analyzer.h" + + +Analyzer::Analyzer (QWidget *parent) + : Visual (parent), m_fps ( 20 ) +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + restoreGeometry(settings.value("Analyzer/geometry").toByteArray()); + setFixedSize(2*300-30,105); + m_pixmap = QPixmap (75,20); + m_timer = new QTimer (this); + connect(m_timer, SIGNAL (timeout()), this, SLOT (timeout())); + m_nodes.clear(); + + clear(); + setWindowTitle (tr("Qmmp Analyzer")); + + double peaks_speed[] = { 0.05, 0.1, 0.2, 0.4, 0.8 }; + double analyzer_speed[] = { 1.2, 1.8, 2.2, 2.8, 2.4 }; + int intervals[] = { 20 , 40 , 100 , 200 }; + + m_peaks_falloff = + peaks_speed[settings.value("Analyzer/peaks_falloff", 3).toInt()-1]; + m_analyzer_falloff = + analyzer_speed[settings.value("Analyzer/analyzer_falloff", 3).toInt()-1]; + m_show_peaks = settings.value("Analyzer/show_peaks", TRUE).toBool(); + m_timer->setInterval(intervals[settings.value("Analyzer/refresh_rate", 2).toInt() - 1]); + m_color1.setNamedColor(settings.value("Analyzer/color1", "Green").toString()); + m_color2.setNamedColor(settings.value("Analyzer/color2", "Yellow").toString()); + m_color3.setNamedColor(settings.value("Analyzer/color3", "Red").toString()); + m_bgColor.setNamedColor(settings.value("Analyzer/bg_color", "Black").toString()); + m_peakColor.setNamedColor(settings.value("Analyzer/peak_color", "Cyan").toString()); +} + +Analyzer::~Analyzer() +{ + while (!m_nodes.isEmpty()) + m_nodes.removeFirst(); +} + +void Analyzer::clear() +{ + while (!m_nodes.isEmpty()) + m_nodes.removeFirst(); + for ( int i = 0; i< 75; ++i ) + { + m_intern_vis_data[i] = 0; + m_peaks[i] = 0; + } + update(); +} + +void Analyzer::add ( Buffer *b, unsigned long w, int c, int p ) +{ + if (!m_timer->isActive ()) + return; + long len = b->nbytes, cnt; + short *l = 0, *r = 0; + + len /= c; + len /= ( p / 8 ); + if ( len > 512 ) + len = 512; + cnt = len; + + if ( c == 2 ) + { + l = new short[len]; + r = new short[len]; + + if ( p == 8 ) + stereo16_from_stereopcm8 ( l, r, b->data, cnt ); + else if ( p == 16 ) + stereo16_from_stereopcm16 ( l, r, ( short * ) b->data, cnt ); + } + else if ( c == 1 ) + { + l = new short[len]; + + if ( p == 8 ) + mono16_from_monopcm8 ( l, b->data, cnt ); + else if ( p == 16 ) + mono16_from_monopcm16 ( l, ( short * ) b->data, cnt ); + } + else + len = 0; + + if (len) + m_nodes.append (new VisualNode (l, r, len, w)); +} + +void Analyzer::timeout() +{ + VisualNode *node = 0; + + if ( /*playing &&*/ output()) + { + //output()->mutex()->lock (); + //long olat = output()->latency(); + //long owrt = output()->written(); + //output()->mutex()->unlock(); + + //long synctime = owrt < olat ? 0 : owrt - olat; + + mutex()->lock (); + VisualNode *prev = 0; + while ((!m_nodes.isEmpty())) + { + node = m_nodes.takeFirst(); + /*if ( node->offset > synctime ) + break;*/ + + if (prev) + delete prev; + prev = node; + } + mutex()->unlock(); + node = prev; + } + + if (!node) + return; + process (node); + delete node; + update(); +} + +void Analyzer::paintEvent (QPaintEvent * e) +{ + QPainter painter (this); + painter.fillRect(e->rect(),m_bgColor); + draw(&painter); +} + +void Analyzer::hideEvent (QHideEvent *) +{ + m_timer->stop(); +} + +void Analyzer::showEvent (QShowEvent *) +{ + m_timer->start(); +} + +void Analyzer::closeEvent (QCloseEvent *event) +{ + //save geometry + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.setValue("Analyzer/geometry", saveGeometry()); + Visual::closeEvent(event); //removes visualization before class deleting +} + +bool Analyzer::process (VisualNode *node) +{ + static fft_state *state = 0; + if ( !state ) + state = fft_init(); + + short dest_l[256]; + short dest_r[256]; + + const int xscale_short[] = + { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 11, 15, 20, 27, + 36, 47, 62, 82, 107, 141, 184, 255 + }; + + if ( node ) + { + //i = node->length; + calc_freq ( dest_l, node->left ); + if (node->right) + calc_freq ( dest_r, node->right ); + } + else + return FALSE; + const double y_scale = 3.60673760222; /* 20.0 / log(256) */ + int yl,yr, j; + + for (int i = 0; i < 19; i++) + { + yl = yr = 0; + + for ( j = xscale_short[i]; j < xscale_short[i + 1]; j++ ) + { + if ( dest_l[j] > yl ) + yl = dest_l[j]; + if ( dest_r[j] > yr && node->right) + yr = dest_r[j]; + } + yl >>= 7; + int magnitude_l = 0; + int magnitude_r = 0; + if (node->right) + { + yr >>= 7; + } + if (yl) + { + magnitude_l = int(log (yl) * y_scale); + if ( magnitude_l > 15 ) + magnitude_l = 15; + if ( magnitude_l < 0 ) + magnitude_l = 0; + } + if (yr && node->right) + { + magnitude_r = int(log (yr) * y_scale); + if ( magnitude_r > 15 ) + magnitude_r = 15; + if ( magnitude_r < 0 ) + magnitude_r = 0; + } + + m_intern_vis_data[i] -= m_analyzer_falloff; + m_intern_vis_data[i] = magnitude_l > m_intern_vis_data[i] + ? magnitude_l : m_intern_vis_data[i]; + if (node->right) + { + m_intern_vis_data[37-i] -= m_analyzer_falloff; + m_intern_vis_data[37-i] = magnitude_r > m_intern_vis_data[37-i] + ? magnitude_r : m_intern_vis_data[37-i]; + } + + if (m_show_peaks) + { + m_peaks[i] -= m_peaks_falloff; + m_peaks[i] = magnitude_l > m_peaks[i] + ? magnitude_l : m_peaks[i]; + if (node->right) + { + m_peaks[37-i] -= m_peaks_falloff; + m_peaks[37-i] = magnitude_r > m_peaks[37-i] + ? magnitude_r : m_peaks[37-i]; + } + } + } + return TRUE; +} + +void Analyzer::draw (QPainter *p) +{ + QBrush brush(Qt::SolidPattern); + for (int j = 0; j < 19; ++j) + { + for (int i = 0; i <= m_intern_vis_data[j]; ++i) + { + if (i <= 5) + brush.setColor(m_color1); + else if (i > 5 && i <= 10) + brush.setColor(m_color2); + else + brush.setColor(m_color3); + p->fillRect (j*15+1, height() - i*7, 12, 4, brush); + } + + for (int i = 0; i <= m_intern_vis_data[19+j]; ++i) + { + if (i <= 5) + brush.setColor(m_color1); + else if (i > 5 && i <= 10) + brush.setColor(m_color2); + else + brush.setColor(m_color3); + p->fillRect ((j+19)*15+1, height() - i*7, 12, 4, brush); + } + + if (m_show_peaks) + { + p->fillRect (j*15+1, height() - int(m_peaks[j])*7, 12, 4, m_peakColor); + p->fillRect ((j+19)*15+1, height() - int(m_peaks[j+19])*7, 12, 4, m_peakColor); + } + } +} diff --git a/src/plugins/Visual/analyzer/analyzer.h b/src/plugins/Visual/analyzer/analyzer.h new file mode 100644 index 000000000..ab08d3af0 --- /dev/null +++ b/src/plugins/Visual/analyzer/analyzer.h @@ -0,0 +1,102 @@ +/*************************************************************************** + * 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 ANALYZER_H +#define ANALYZER_H + +#include <QWidget> +#include <QResizeEvent> +#include <visual.h> +#include <constants.h> +#include <QDir> + +class QSettings; +class QTimer; +class QMenu; +class QActionGroup; + +class Buffer; + + +class VisualNode +{ +public: + VisualNode(short *l, short *r, unsigned long n, unsigned long o) + : left(l), right(r), length(n), offset(o) + { + // left and right are allocated and then passed to this class + // the code that allocated left and right should give up all ownership + } + + ~VisualNode() + { + delete [] left; + delete [] right; + } + + short *left, *right; + long length, offset; +}; + +class Analyzer : public Visual +{ + Q_OBJECT + +public: + Analyzer( QWidget *parent = 0); + virtual ~Analyzer(); + + void add(Buffer *, unsigned long, int, int); + void clear(); + void paintEvent( QPaintEvent * ); + +protected: + virtual void hideEvent (QHideEvent *); + virtual void showEvent (QShowEvent *); + virtual void closeEvent (QCloseEvent *); + +public slots: + void timeout(); + +private slots: + void updateSettings(); + +private: + bool process(VisualNode *node); + void draw(QPainter *p); + QPixmap m_pixmap; + QPixmap m_bg; + QList <VisualNode*> m_nodes; + QTimer *m_timer; + int m_fps; + double m_intern_vis_data[75]; + double m_peaks[75]; + double m_peaks_falloff; + double m_analyzer_falloff; + bool m_show_peaks; + //colors + QColor m_color1; + QColor m_color2; + QColor m_color3; + QColor m_bgColor; + QColor m_peakColor; +}; + + +#endif diff --git a/src/plugins/Visual/analyzer/analyzer.pro b/src/plugins/Visual/analyzer/analyzer.pro new file mode 100644 index 000000000..05d95a1ff --- /dev/null +++ b/src/plugins/Visual/analyzer/analyzer.pro @@ -0,0 +1,35 @@ +include(../../plugins.pri) + +TARGET=$$PLUGINS_PREFIX/Visual/analyzer +QMAKE_CLEAN =$$PLUGINS_PREFIX/Visual/libanalyzer.so + + +#FORMS += detailsdialog.ui +HEADERS += analyzer.h \ + fft.h \ + visualanalyzerfactory.h \ + inlines.h \ + colorwidget.h \ + settingsdialog.h +SOURCES += analyzer.cpp \ + fft.c \ + visualanalyzerfactory.cpp \ + colorwidget.cpp \ + settingsdialog.cpp +INCLUDEPATH += ../../../qmmp +CONFIG += release \ +warn_on \ +plugin +TEMPLATE = lib +QMAKE_LIBDIR += ../../../../lib +LIBS += -lqmmp -L/usr/lib -I/usr/include +#TRANSLATIONS = translations/ffmpeg_plugin_ru.ts +#RESOURCES = translations/translations.qrc + +isEmpty(LIB_DIR){ + LIB_DIR = /lib +} +target.path = $$LIB_DIR/qmmp/Visual +INSTALLS += target +FORMS += settingsdialog.ui + diff --git a/src/plugins/Visual/analyzer/colorwidget.cpp b/src/plugins/Visual/analyzer/colorwidget.cpp new file mode 100644 index 000000000..7117a6cac --- /dev/null +++ b/src/plugins/Visual/analyzer/colorwidget.cpp @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright (C) 2005 by Ilya Kotov * + * qmmeter_freedevelop@mail.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 "colorwidget.h" + + +ColorWidget::ColorWidget(QWidget *parent) : QFrame(parent) +{ + setFrameShape(QFrame::Box); + setAutoFillBackground(TRUE); +} + + +ColorWidget::~ColorWidget() +{} + +void ColorWidget::mousePressEvent( QMouseEvent *) +{ + QColor color = QColorDialog::getColor(); + if (color.isValid()) + { + QPalette palette; + palette.setColor(this->backgroundRole(), color); + this->setPalette(palette); + } +} + +void ColorWidget::setColor(QString c) +{ + QPalette palette; + palette.setColor(this->backgroundRole(), c); + this->setPalette(palette); +} + +QString ColorWidget::colorName() +{ + QPalette palette; + palette = this->palette(); + return (palette.color(this->backgroundRole())).name(); +} diff --git a/src/plugins/Visual/analyzer/colorwidget.h b/src/plugins/Visual/analyzer/colorwidget.h new file mode 100644 index 000000000..c145c1035 --- /dev/null +++ b/src/plugins/Visual/analyzer/colorwidget.h @@ -0,0 +1,50 @@ +/*************************************************************************** + * Copyright (C) 2005 by Ilya Kotov * + * qmmeter_freedevelop@mail.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 COLORWIDGET_H +#define COLORWIDGET_H + +#include <QFrame> +#include <QColorDialog> +#include <QPaintEvent> + +/** +@author user +*/ +class ColorWidget : public QFrame +{ + Q_OBJECT +public: + ColorWidget(QWidget *parent = 0); + + ~ColorWidget(); + + QString colorName(); + +public slots: + void setColor (QString); + + +protected: + virtual void mousePressEvent ( QMouseEvent *); + + +}; + +#endif diff --git a/src/plugins/Visual/analyzer/fft.c b/src/plugins/Visual/analyzer/fft.c new file mode 100644 index 000000000..7ca1978a5 --- /dev/null +++ b/src/plugins/Visual/analyzer/fft.c @@ -0,0 +1,296 @@ +/* fft.c: Iterative implementation of a FFT + * Copyright (C) 1999 Richard Boulton <richard@tartarus.org> + * Convolution stuff by Ralph Loader <suckfish@ihug.co.nz> + * + * 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. + */ + +/* + * TODO + * Remove compiling in of FFT_BUFFER_SIZE? (Might slow things down, but would + * be nice to be able to change size at runtime.) + * Finish making / checking thread-safety. + * More optimisations. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "fft.h" + +//#include <glib.h> +#include <stdlib.h> +#include <math.h> +#ifndef PI +#ifdef M_PI +#define PI M_PI +#else +#define PI 3.14159265358979323846 /* pi */ +#endif +#endif + +/* ########### */ +/* # Structs # */ +/* ########### */ + +struct _struct_fft_state { + /* Temporary data stores to perform FFT in. */ + float real[FFT_BUFFER_SIZE]; + float imag[FFT_BUFFER_SIZE]; +}; + +/* ############################# */ +/* # Local function prototypes # */ +/* ############################# */ + +static void fft_prepare(const sound_sample * input, float *re, float *im); +static void fft_calculate(float *re, float *im); +static void fft_output(const float *re, const float *im, float *output); +static int reverseBits(unsigned int initial); + +/* #################### */ +/* # Global variables # */ +/* #################### */ + +/* Table to speed up bit reverse copy */ +static unsigned int bitReverse[FFT_BUFFER_SIZE]; + +/* The next two tables could be made to use less space in memory, since they + * overlap hugely, but hey. */ +static float sintable[FFT_BUFFER_SIZE / 2]; +static float costable[FFT_BUFFER_SIZE / 2]; + +/* ############################## */ +/* # Externally called routines # */ +/* ############################## */ + +/* --------- */ +/* FFT stuff */ +/* --------- */ + +/* + * Initialisation routine - sets up tables and space to work in. + * Returns a pointer to internal state, to be used when performing calls. + * On error, returns NULL. + * The pointer should be freed when it is finished with, by fft_close(). + */ +fft_state * +fft_init(void) +{ + fft_state *state; + unsigned int i; + + state = (fft_state *) malloc(sizeof(fft_state)); + if (!state) + return NULL; + + for (i = 0; i < FFT_BUFFER_SIZE; i++) { + bitReverse[i] = reverseBits(i); + } + for (i = 0; i < FFT_BUFFER_SIZE / 2; i++) { + float j = 2 * PI * i / FFT_BUFFER_SIZE; + costable[i] = cos(j); + sintable[i] = sin(j); + } + + return state; +} + +/* + * Do all the steps of the FFT, taking as input sound data (as described in + * sound.h) and returning the intensities of each frequency as floats in the + * range 0 to ((FFT_BUFFER_SIZE / 2) * 32768) ^ 2 + * + * FIXME - the above range assumes no frequencies present have an amplitude + * larger than that of the sample variation. But this is false: we could have + * a wave such that its maximums are always between samples, and it's just + * inside the representable range at the places samples get taken. + * Question: what _is_ the maximum value possible. Twice that value? Root + * two times that value? Hmmm. Think it depends on the frequency, too. + * + * The input array is assumed to have FFT_BUFFER_SIZE elements, + * and the output array is assumed to have (FFT_BUFFER_SIZE / 2 + 1) elements. + * state is a (non-NULL) pointer returned by fft_init. + */ +void +fft_perform(const sound_sample * input, float *output, fft_state * state) +{ + /* Convert data from sound format to be ready for FFT */ + fft_prepare(input, state->real, state->imag); + + /* Do the actual FFT */ + fft_calculate(state->real, state->imag); + + /* Convert the FFT output into intensities */ + fft_output(state->real, state->imag, output); +} + +/* + * Free the state. + */ +void +fft_close(fft_state * state) +{ + if (state) + free(state); +} + +/* ########################### */ +/* # Locally called routines # */ +/* ########################### */ + +/* + * Prepare data to perform an FFT on + */ +static void +fft_prepare(const sound_sample * input, float *re, float *im) +{ + unsigned int i; + float *realptr = re; + float *imagptr = im; + + /* Get input, in reverse bit order */ + for (i = 0; i < FFT_BUFFER_SIZE; i++) { + *realptr++ = input[bitReverse[i]]; + *imagptr++ = 0; + } +} + +/* + * Take result of an FFT and calculate the intensities of each frequency + * Note: only produces half as many data points as the input had. + * This is roughly a consequence of the Nyquist sampling theorm thingy. + * (FIXME - make this comment better, and helpful.) + * + * The two divisions by 4 are also a consequence of this: the contributions + * returned for each frequency are split into two parts, one at i in the + * table, and the other at FFT_BUFFER_SIZE - i, except for i = 0 and + * FFT_BUFFER_SIZE which would otherwise get float (and then 4* when squared) + * the contributions. + */ +static void +fft_output(const float *re, const float *im, float *output) +{ + float *outputptr = output; + const float *realptr = re; + const float *imagptr = im; + float *endptr = output + FFT_BUFFER_SIZE / 2; + +#ifdef DEBUG + unsigned int i, j; +#endif + + while (outputptr <= endptr) { + *outputptr = (*realptr * *realptr) + (*imagptr * *imagptr); + outputptr++; + realptr++; + imagptr++; + } + /* Do divisions to keep the constant and highest frequency terms in scale + * with the other terms. */ + *output /= 4; + *endptr /= 4; + +#ifdef DEBUG + printf("Recalculated input:\n"); + for (i = 0; i < FFT_BUFFER_SIZE; i++) { + float val_real = 0; + float val_imag = 0; + for (j = 0; j < FFT_BUFFER_SIZE; j++) { + float fact_real = cos(-2 * j * i * PI / FFT_BUFFER_SIZE); + float fact_imag = sin(-2 * j * i * PI / FFT_BUFFER_SIZE); + val_real += fact_real * re[j] - fact_imag * im[j]; + val_imag += fact_real * im[j] + fact_imag * re[j]; + } + printf("%5d = %8f + i * %8f\n", i, + val_real / FFT_BUFFER_SIZE, val_imag / FFT_BUFFER_SIZE); + } + printf("\n"); +#endif +} + +/* + * Actually perform the FFT + */ +static void +fft_calculate(float *re, float *im) +{ + unsigned int i, j, k; + unsigned int exchanges; + float fact_real, fact_imag; + float tmp_real, tmp_imag; + unsigned int factfact; + + /* Set up some variables to reduce calculation in the loops */ + exchanges = 1; + factfact = FFT_BUFFER_SIZE / 2; + + /* Loop through the divide and conquer steps */ + for (i = FFT_BUFFER_SIZE_LOG; i != 0; i--) { + /* In this step, we have 2 ^ (i - 1) exchange groups, each with + * 2 ^ (FFT_BUFFER_SIZE_LOG - i) exchanges + */ + /* Loop through the exchanges in a group */ + for (j = 0; j != exchanges; j++) { + /* Work out factor for this exchange + * factor ^ (exchanges) = -1 + * So, real = cos(j * PI / exchanges), + * imag = sin(j * PI / exchanges) + */ + fact_real = costable[j * factfact]; + fact_imag = sintable[j * factfact]; + + /* Loop through all the exchange groups */ + for (k = j; k < FFT_BUFFER_SIZE; k += exchanges << 1) { + int k1 = k + exchanges; + /* newval[k] := val[k] + factor * val[k1] + * newval[k1] := val[k] - factor * val[k1] + **/ +#ifdef DEBUG + printf("%d %d %d\n", i, j, k); + printf("Exchange %d with %d\n", k, k1); + printf("Factor %9f + i * %8f\n", fact_real, fact_imag); +#endif + /* FIXME - potential scope for more optimization here? */ + tmp_real = fact_real * re[k1] - fact_imag * im[k1]; + tmp_imag = fact_real * im[k1] + fact_imag * re[k1]; + re[k1] = re[k] - tmp_real; + im[k1] = im[k] - tmp_imag; + re[k] += tmp_real; + im[k] += tmp_imag; +#ifdef DEBUG + for (k1 = 0; k1 < FFT_BUFFER_SIZE; k1++) { + printf("%5d = %8f + i * %8f\n", k1, real[k1], imag[k1]); + } +#endif + } + } + exchanges <<= 1; + factfact >>= 1; + } +} + +static int +reverseBits(unsigned int initial) +{ + unsigned int reversed = 0, loop; + for (loop = 0; loop < FFT_BUFFER_SIZE_LOG; loop++) { + reversed <<= 1; + reversed += (initial & 1); + initial >>= 1; + } + return reversed; +} diff --git a/src/plugins/Visual/analyzer/fft.h b/src/plugins/Visual/analyzer/fft.h new file mode 100644 index 000000000..431afa365 --- /dev/null +++ b/src/plugins/Visual/analyzer/fft.h @@ -0,0 +1,45 @@ +/* fft.h: Header for iterative implementation of a FFT + * Copyright (C) 1999 Richard Boulton <richard@tartarus.org> + * + * 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 _FFT_H_ +#define _FFT_H_ + +#define FFT_BUFFER_SIZE_LOG 9 + +#define FFT_BUFFER_SIZE (1 << FFT_BUFFER_SIZE_LOG) + +/* sound sample - should be an signed 16 bit value */ +typedef short int sound_sample; + +#ifdef __cplusplus +extern "C" { +#endif + +/* FFT library */ + typedef struct _struct_fft_state fft_state; + fft_state *fft_init(void); + void fft_perform(const sound_sample * input, float *output, + fft_state * state); + void fft_close(fft_state * state); + + + +#ifdef __cplusplus +} +#endif +#endif /* _FFT_H_ */ diff --git a/src/plugins/Visual/analyzer/inlines.h b/src/plugins/Visual/analyzer/inlines.h new file mode 100644 index 000000000..3efccf0de --- /dev/null +++ b/src/plugins/Visual/analyzer/inlines.h @@ -0,0 +1,505 @@ +// Copyright (c) 2000-2001 Brad Hughes <bhughes@trolltech.com> +// +// Use, modification and distribution is allowed without limitation, +// warranty, or liability of any kind. +// + +#ifndef INLINES_H +#define INLINES_H + +#include "fft.h" + +// *fast* convenience functions +static inline void +calc_freq(short* dest, short *src) +{ + static fft_state *state = NULL; + float tmp_out[257]; + int i; + + if (!state) + state = fft_init(); + + fft_perform(src, tmp_out, state); + + for (i = 0; i < 256; i++) + dest[i] = ((int) sqrt(tmp_out[i + 1])) >> 8; +} + +static inline void +calc_mono_freq(short dest[2][256], short src[2][512], int nch) +{ + int i; + short *d, *sl, *sr, tmp[512]; + + if (nch == 1) + calc_freq(dest[0], src[0]); + else + { + d = tmp; + sl = src[0]; + sr = src[1]; + for (i = 0; i < 512; i++) + { + *(d++) = (*(sl++) + *(sr++)) >> 1; + } + calc_freq(dest[0], tmp); + } +} + +static inline void stereo16_from_stereopcm8(register short *l, + register short *r, + register uchar *c, + long cnt) +{ + while (cnt >= 4l) + { + l[0] = c[0]; + r[0] = c[1]; + l[1] = c[2]; + r[1] = c[3]; + l[2] = c[4]; + r[2] = c[5]; + l[3] = c[6]; + r[3] = c[7]; + l += 4; + r += 4; + c += 8; + cnt -= 4l; + } + + if (cnt > 0l) + { + l[0] = c[0]; + r[0] = c[1]; + if (cnt > 1l) + { + l[1] = c[2]; + r[1] = c[3]; + if (cnt > 2l) + { + l[2] = c[4]; + r[2] = c[5]; + } + } + } +} + + +static inline void stereo16_from_stereopcm16(register short *l, + register short *r, + register short *s, + long cnt) +{ + while (cnt >= 4l) + { + l[0] = s[0]; + r[0] = s[1]; + l[1] = s[2]; + r[1] = s[3]; + l[2] = s[4]; + r[2] = s[5]; + l[3] = s[6]; + r[3] = s[7]; + l += 4; + r += 4; + s += 8; + cnt -= 4l; + } + + if (cnt > 0l) + { + l[0] = s[0]; + r[0] = s[1]; + if (cnt > 1l) + { + l[1] = s[2]; + r[1] = s[3]; + if (cnt > 2l) + { + l[2] = s[4]; + r[2] = s[5]; + } + } + } +} + + +static inline void mono16_from_monopcm8(register short *l, + register uchar *c, + long cnt) +{ + while (cnt >= 4l) + { + l[0] = c[0]; + l[1] = c[1]; + l[2] = c[2]; + l[3] = c[3]; + l += 4; + c += 4; + cnt -= 4l; + } + + if (cnt > 0l) + { + l[0] = c[0]; + if (cnt > 1l) + { + l[1] = c[1]; + if (cnt > 2l) + { + l[2] = c[2]; + } + } + } +} + + +static inline void mono16_from_monopcm16(register short *l, + register short *s, + long cnt) +{ + while (cnt >= 4l) + { + l[0] = s[0]; + l[1] = s[1]; + l[2] = s[2]; + l[3] = s[3]; + l += 4; + s += 4; + cnt -= 4l; + } + + if (cnt > 0l) + { + l[0] = s[0]; + if (cnt > 1l) + { + l[1] = s[1]; + if (cnt > 2l) + { + l[2] = s[2]; + } + } + } +} + + +static inline void fast_short_set(register short *p, + short v, + long c) +{ + while (c >= 4l) + { + p[0] = v; + p[1] = v; + p[2] = v; + p[3] = v; + p += 4; + c -= 4l; + } + + if (c > 0l) + { + p[0] = v; + if (c > 1l) + { + p[1] = v; + if (c > 2l) + { + p[2] = v; + } + } + } +} + +#ifdef FFTW +static inline void fast_real_set(register fftw_real *p, + fftw_real v, + long c) +{ + while (c >= 4l) + { + p[0] = v; + p[1] = v; + p[2] = v; + p[3] = v; + p += 4; + c -= 4l; + } + + if (c > 0l) + { + p[0] = v; + if (c > 1l) + { + p[1] = v; + if (c > 2l) + { + p[2] = v; + } + } + } +} + +static inline void fast_complex_set(register fftw_complex *p, + fftw_complex v, + long c) +{ + while (c >= 4l) + { + p[0] = v; + p[1] = v; + p[2] = v; + p[3] = v; + p += 4; + c -= 4l; + } + + if (c > 0l) + { + p[0] = v; + if (c > 1l) + { + p[1] = v; + if (c > 2l) + { + p[2] = v; + } + } + } +} + + +static inline void fast_real_set_from_short(register fftw_real *d, + register short *s, + long c) +{ + while (c >= 4l) + { + d[0] = fftw_real(s[0]); + d[1] = fftw_real(s[1]); + d[2] = fftw_real(s[2]); + d[3] = fftw_real(s[3]); + d += 4; + s += 4; + c -= 4l; + } + + if (c > 0l) + { + d[0] = fftw_real(s[0]); + if (c > 1l) + { + d[1] = fftw_real(s[1]); + if (c > 2l) + { + d[2] = fftw_real(s[2]); + } + } + } +} + +static inline void fast_complex_set_from_short(register fftw_complex *d, + register short *s, + long c) +{ + while (c >= 4l) + { + d[0].re = fftw_real(s[0]); + d[0].im = 0; + d[1].re = fftw_real(s[1]); + d[1].im = 0; + d[2].re = fftw_real(s[2]); + d[2].im = 0; + d[3].re = fftw_real(s[3]); + d[3].im = 0; + d += 4; + s += 4; + c -= 4l; + } + + if (c > 0l) + { + d[0].re = fftw_real(s[0]); + d[0].im = 0; + if (c > 1l) + { + d[1].re = fftw_real(s[1]); + d[1].im = 0; + if (c > 2l) + { + d[2].re = fftw_real(s[2]); + d[2].im = 0; + } + } + } +} + + +static inline void fast_real_avg_from_shorts(register fftw_real *d, + register short *s1, + register short *s2, + long c) +{ + fftw_real t0, t1, t2, t3; + while (c >= 4l) + { + t0 = (s1[0] + s2[0]) / 2; + t1 = (s1[1] + s2[1]) / 2; + t2 = (s1[2] + s2[2]) / 2; + t3 = (s1[3] + s2[3]) / 2; + d[0] = t0; + d[1] = t1; + d[2] = t2; + d[3] = t3; + d += 4; + s1 += 4; + s2 += 4; + c -= 4l; + } + + if (c > 0l) + { + d[0] = fftw_real((s1[0] + s2[0]) / 2); + if (c > 1l) + { + d[1] = fftw_real((s1[1] + s2[1]) / 2); + if (c > 2l) + { + d[2] = fftw_real((s1[2] + s2[2]) / 2); + } + } + } +} + +static inline void fast_complex_avg_from_shorts(register fftw_complex *d, + register short *s1, + register short *s2, + long c) +{ + fftw_real t0, t1, t2, t3; + while (c >= 4l) + { + t0 = (s1[0] + s2[0]) / 2; + t1 = (s1[1] + s2[1]) / 2; + t2 = (s1[2] + s2[2]) / 2; + t3 = (s1[3] + s2[3]) / 2; + d[0].re = t0; + d[0].im = 0; + d[1].re = t1; + d[1].im = 0; + d[2].re = t2; + d[2].im = 0; + d[3].re = t3; + d[3].im = 0; + d += 4; + s1 += 4; + s2 += 4; + c -= 4l; + } + + if (c > 0l) + { + d[0].re = fftw_real((s1[0] + s2[0]) / 2); + d[0].im = 0; + if (c > 1l) + { + d[1].re = fftw_real((s1[1] + s2[1]) / 2); + d[1].im = 0; + if (c > 2l) + { + d[2].re = fftw_real((s1[2] + s2[2]) / 2); + d[2].im = 0; + } + } + } +} + + +static inline fftw_complex fftw_complex_from_real( fftw_real re ) +{ + fftw_complex c; + + c.re = re; + c.im = 0; + + return c; +} + +static inline void fast_reals_set(register fftw_real *p1, + register fftw_real *p2, + fftw_real v, + long c) +{ + while (c >= 4l) + { + p1[0] = v; + p1[1] = v; + p1[2] = v; + p1[3] = v; + p2[0] = v; + p2[1] = v; + p2[2] = v; + p2[3] = v; + p1 += 4; + p2 += 4; + c -= 4l; + } + + if (c > 0l) + { + p1[0] = v; + p2[0] = v; + if (c > 1l) + { + p1[1] = v; + p2[1] = v; + if (c > 2l) + { + p1[2] = v; + p2[2] = v; + } + } + } +} + +static inline void fast_complex_set(register fftw_complex *p1, + register fftw_complex *p2, + fftw_complex v, + long c) +{ + while (c >= 4l) + { + p1[0] = v; + p1[1] = v; + p1[2] = v; + p1[3] = v; + p2[0] = v; + p2[1] = v; + p2[2] = v; + p2[3] = v; + p1 += 4; + p2 += 4; + c -= 4l; + } + + if (c > 0l) + { + p1[0] = v; + p2[0] = v; + if (c > 1l) + { + p1[1] = v; + p2[1] = v; + if (c > 2l) + { + p1[2] = v; + p2[2] = v; + } + } + } +} +#endif // FFTW + +#endif // INLINES_H diff --git a/src/plugins/Visual/analyzer/settingsdialog.cpp b/src/plugins/Visual/analyzer/settingsdialog.cpp new file mode 100644 index 000000000..badadc190 --- /dev/null +++ b/src/plugins/Visual/analyzer/settingsdialog.cpp @@ -0,0 +1,62 @@ +/*************************************************************************** + * 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 <QSettings> +#include <QDir> + +#include "settingsdialog.h" + +SettingsDialog::SettingsDialog(QWidget *parent) + : QDialog(parent) +{ + ui.setupUi(this); + setAttribute(Qt::WA_DeleteOnClose, TRUE); + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + ui.analyzerComboBox->setCurrentIndex(settings.value("Analyzer/analyzer_falloff", 3).toInt()-1); + ui.peaksCheckBox->setChecked(settings.value("Analyzer/show_peaks", TRUE).toBool()); + ui.peaksComboBox->setCurrentIndex(settings.value("Analyzer/peaks_falloff", 3).toInt()-1); + ui.fpsComboBox->setCurrentIndex(settings.value("Analyzer/refresh_rate", 2).toInt()-1); + ui.colorWidget1->setColor(settings.value("Analyzer/color1", "Green").toString()); + ui.colorWidget2->setColor(settings.value("Analyzer/color2", "Yellow").toString()); + ui.colorWidget3->setColor(settings.value("Analyzer/color3", "Red").toString()); + ui.bgColorWidget->setColor(settings.value("Analyzer/bg_color", "Black").toString()); + ui.peakColorWidget->setColor(settings.value("Analyzer/peak_color", "Cyan").toString()); + connect (ui.okButton, SIGNAL(clicked()),SLOT(writeSettings())); +} + + +SettingsDialog::~SettingsDialog() +{ +} + +void SettingsDialog::writeSettings() +{ + QSettings settings(QDir::homePath()+"/.qmmp/qmmprc", QSettings::IniFormat); + settings.setValue("Analyzer/analyzer_falloff", ui.analyzerComboBox->currentIndex() + 1); + settings.setValue("Analyzer/peaks_falloff", ui.peaksComboBox->currentIndex() + 1); + settings.setValue("Analyzer/refresh_rate", ui.fpsComboBox->currentIndex() + 1); + settings.setValue("Analyzer/show_peaks", ui.peaksCheckBox->isChecked()); + settings.setValue("Analyzer/color1", ui.colorWidget1->colorName()); + settings.setValue("Analyzer/color2", ui.colorWidget2->colorName()); + settings.setValue("Analyzer/color3", ui.colorWidget3->colorName()); + settings.setValue("Analyzer/bg_color", ui.bgColorWidget->colorName()); + settings.setValue("Analyzer/peak_color", ui.peakColorWidget->colorName()); + accept(); +} diff --git a/src/plugins/Visual/analyzer/settingsdialog.h b/src/plugins/Visual/analyzer/settingsdialog.h new file mode 100644 index 000000000..b7c466477 --- /dev/null +++ b/src/plugins/Visual/analyzer/settingsdialog.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * 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 SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include <QDialog> + +#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 writeSettings(); + +private: + Ui::SettingsDialog ui; + +}; + +#endif diff --git a/src/plugins/Visual/analyzer/settingsdialog.ui b/src/plugins/Visual/analyzer/settingsdialog.ui new file mode 100644 index 000000000..4ddd391a3 --- /dev/null +++ b/src/plugins/Visual/analyzer/settingsdialog.ui @@ -0,0 +1,403 @@ +<ui version="4.0" > + <class>SettingsDialog</class> + <widget class="QDialog" name="SettingsDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>292</width> + <height>327</height> + </rect> + </property> + <property name="windowTitle" > + <string>Analyzer Plugin Settings</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" colspan="3" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>General</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QCheckBox" name="peaksCheckBox" > + <property name="layoutDirection" > + <enum>Qt::LeftToRight</enum> + </property> + <property name="text" > + <string>Show peaks</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_10" > + <property name="text" > + <string>Analyzer falloff:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="analyzerComboBox" > + <property name="currentIndex" > + <number>2</number> + </property> + <item> + <property name="text" > + <string>Slowest</string> + </property> + </item> + <item> + <property name="text" > + <string>Slow</string> + </property> + </item> + <item> + <property name="text" > + <string>Medium</string> + </property> + </item> + <item> + <property name="text" > + <string>Fast</string> + </property> + </item> + <item> + <property name="text" > + <string>Fastest</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_9" > + <property name="text" > + <string>Peaks falloff:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QComboBox" name="peaksComboBox" > + <property name="currentIndex" > + <number>2</number> + </property> + <item> + <property name="text" > + <string>Slowest</string> + </property> + </item> + <item> + <property name="text" > + <string>Slow</string> + </property> + </item> + <item> + <property name="text" > + <string>Medium</string> + </property> + </item> + <item> + <property name="text" > + <string>Fast</string> + </property> + </item> + <item> + <property name="text" > + <string>Fastest</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_11" > + <property name="text" > + <string>Refresh rate:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QComboBox" name="fpsComboBox" > + <property name="currentIndex" > + <number>0</number> + </property> + <item> + <property name="text" > + <string>50 FPS</string> + </property> + </item> + <item> + <property name="text" > + <string>25 FPS</string> + </property> + </item> + <item> + <property name="text" > + <string>10 FPS</string> + </property> + </item> + <item> + <property name="text" > + <string>5 FPS</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" colspan="3" > + <widget class="QGroupBox" name="groupBox" > + <property name="sizePolicy" > + <sizepolicy vsizetype="MinimumExpanding" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Colors</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_4" > + <property name="text" > + <string>Peaks:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="ColorWidget" native="1" name="peakColorWidget" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="label_6" > + <property name="text" > + <string>Analyzer #1:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="ColorWidget" native="1" name="colorWidget1" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>Background:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="ColorWidget" native="1" name="bgColorWidget" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QLabel" name="label_7" > + <property name="text" > + <string>Analyzer #2:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="ColorWidget" native="1" name="colorWidget2" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </widget> + </item> + <item row="2" column="0" colspan="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>111</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="2" > + <widget class="QLabel" name="label_8" > + <property name="text" > + <string>Analyzer #3:</string> + </property> + <property name="alignment" > + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="2" column="3" > + <widget class="ColorWidget" native="1" name="colorWidget3" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" > + <size> + <width>95</width> + <height>29</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" > + <widget class="QPushButton" name="okButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>16777215</width> + <height>30</height> + </size> + </property> + <property name="text" > + <string>&OK</string> + </property> + </widget> + </item> + <item row="2" column="2" > + <widget class="QPushButton" name="cancelButton" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Minimum" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="maximumSize" > + <size> + <width>16777215</width> + <height>30</height> + </size> + </property> + <property name="text" > + <string>&Cancel</string> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>ColorWidget</class> + <extends>QWidget</extends> + <header>colorwidget.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>cancelButton</sender> + <signal>clicked()</signal> + <receiver>SettingsDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>220</x> + <y>286</y> + </hint> + <hint type="destinationlabel" > + <x>276</x> + <y>309</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/src/plugins/Visual/analyzer/visualanalyzerfactory.cpp b/src/plugins/Visual/analyzer/visualanalyzerfactory.cpp new file mode 100644 index 000000000..0e110b2cd --- /dev/null +++ b/src/plugins/Visual/analyzer/visualanalyzerfactory.cpp @@ -0,0 +1,57 @@ +/*************************************************************************** + * 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 "visualanalyzerfactory.h" +#include "analyzer.h" + +const VisualProperties VisualAnalyzerFactory::properties() const +{ + VisualProperties properties; + properties.name = tr("Analyzer Plugin"); + return properties; +}; + +Visual *VisualAnalyzerFactory::create(QWidget *parent) +{ + return new Analyzer(parent); +}; + +void VisualAnalyzerFactory::showSettings(QWidget *parent) +{ + SettingsDialog *s = new SettingsDialog(parent); + s -> show(); +}; + +void VisualAnalyzerFactory::showAbout(QWidget *parent) +{ + QMessageBox::about (parent, tr("About Analyzer Visual Plugin"), + tr("Qmmp Analyzer Visual Plugin")+"\n"+ + tr("Writen by: Ilya Kotov <forkotov02@hotmail.ru>")); +}; + +QTranslator *VisualAnalyzerFactory::createTranslator(QObject *parent) +{ + return 0; +}; + +Q_EXPORT_PLUGIN(VisualAnalyzerFactory) diff --git a/src/plugins/Visual/analyzer/visualanalyzerfactory.h b/src/plugins/Visual/analyzer/visualanalyzerfactory.h new file mode 100644 index 000000000..54f52bfe2 --- /dev/null +++ b/src/plugins/Visual/analyzer/visualanalyzerfactory.h @@ -0,0 +1,46 @@ +/*************************************************************************** + * 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 VISUALANALYZERFACTORY_H +#define VISUALANALYZERFACTORY_H + + +#include <QObject> + +#include <visualfactory.h> +#include <visual.h> + +/** + @author Ilya Kotov <forkotov02@hotmail.ru> +*/ +class VisualAnalyzerFactory : public QObject, public VisualFactory +{ +Q_OBJECT +Q_INTERFACES(VisualFactory); + +public: + const VisualProperties properties() const; + Visual *create(QWidget *parent); + void showSettings(QWidget *parent); + void showAbout(QWidget *parent); + QTranslator *createTranslator(QObject *parent); +}; + + +#endif diff --git a/src/plugins/plugins.pri b/src/plugins/plugins.pri new file mode 100644 index 000000000..e109b7b46 --- /dev/null +++ b/src/plugins/plugins.pri @@ -0,0 +1,2 @@ +include(../../qmmp.pri) +PLUGINS_PREFIX=../../../../lib/qmmp
\ No newline at end of file diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 000000000..9be0dd332 --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,7 @@ + +SUBDIRS += Input \ + Output \ + Visual \ + Effect \ + General +TEMPLATE = subdirs |
