diff --git a/.kdev4/kwave.kdev4 b/.kdev4/kwave.kdev4 index 56079565..4b7f3353 100644 --- a/.kdev4/kwave.kdev4 +++ b/.kdev4/kwave.kdev4 @@ -1,66 +1,67 @@ [Buildset] BuildItems=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\n\x00k\x00w\x00a\x00v\x00e) Number of Builditems=1 [Buildset][Builditem0] Itemname=kwave Itempath=./ Projectname=kwave [CMake] Build Directory Count=1 CMakeDir=/usr/share/cmake/Modules Current Build Directory Index=0 ProjectRootRelative=./ [CMake][CMake Build Directory 0] Build Directory Path=file:///var/tmp/kwave-build Build Type=Debug CMake Binary=file:///usr/bin/cmake Install Directory=file:///usr +Runtime=Host System [CustomDefinesAndIncludes][ProjectPath0] Defines=\x00\x00\x00\x02\x00\x00\x00\x18\x00_\x00_\x00F\x00U\x00N\x00C\x00T\x00I\x00O\x00N\x00_\x00_\x00\x00\x00\n\x00\x00\x00\x00\x18\x00_\x00_\x00F\x00U\x00N\x00C\x00T\x00I\x00O\x00N\x00_\x00_\x00\x00\x00\x10\x00_\x00_\x00L\x00I\x00N\x00E\x00_\x00_\x00\x00\x00\n\x00\x00\x00\x00\x10\x00_\x00_\x00L\x00I\x00N\x00E\x00_\x00_ Includes=\x00\x00\x00$\x00\x00\x00"\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00G\x00e\x00n\x00t\x00o\x00o\x00\x00\x00:\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00n\x00c\x00u\x00r\x00r\x00e\x00n\x00t\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00C\x00o\x00r\x00e\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00B\x00u\x00s\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00c\x00l\x00a\x00r\x00a\x00t\x00i\x00v\x00e\x00\x00\x006\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00\x00\x00J\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00D\x00e\x00s\x00i\x00g\x00n\x00e\x00r\x00C\x00o\x00m\x00p\x00o\x00n\x00e\x00n\x00t\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00G\x00u\x00i\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00N\x00e\x00t\x00w\x00o\x00r\x00k\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00\x00\x00F\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00O\x00p\x00e\x00n\x00G\x00L\x00E\x00x\x00t\x00e\x00n\x00s\x00i\x00o\x00n\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00H\x00e\x00a\x00d\x00e\x00r\x00s\x00\x00\x00D\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00l\x00a\x00t\x00f\x00o\x00r\x00m\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00P\x00r\x00i\x00n\x00t\x00S\x00u\x00p\x00p\x00o\x00r\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00m\x00l\x00\x00\x000\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00\x00\x00B\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00P\x00a\x00r\x00t\x00i\x00c\x00l\x00e\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00T\x00e\x00s\x00t\x00\x00\x00>\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00Q\x00u\x00i\x00c\x00k\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00c\x00r\x00i\x00p\x00t\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00q\x00l\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00S\x00v\x00g\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00T\x00e\x00s\x00t\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00U\x00i\x00T\x00o\x00o\x00l\x00s\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00e\x00b\x00K\x00i\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x004\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00\x00\x008\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x001\x001\x00E\x00x\x00t\x00r\x00a\x00s\x00\x00\x00,\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00X\x00m\x00l\x00P\x00a\x00t\x00t\x00e\x00r\x00n\x00s\x00\x00\x00.\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00I\x001\x008\x00n\x00/\x00\x00\x00@\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00A\x00d\x00d\x00o\x00n\x00s\x00/\x00\x00\x00<\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00M\x00u\x00l\x00t\x00i\x00m\x00e\x00d\x00i\x00a\x00/\x00\x00\x00J\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00q\x00t\x005\x00/\x00Q\x00t\x00M\x00u\x00l\x00t\x00i\x00m\x00e\x00d\x00i\x00a\x00W\x00i\x00d\x00g\x00e\x00t\x00s\x00/\x00\x00\x002\x00/\x00u\x00s\x00r\x00/\x00i\x00n\x00c\x00l\x00u\x00d\x00e\x00/\x00K\x00F\x005\x00/\x00K\x00S\x00e\x00r\x00v\x00i\x00c\x00e Path=. [Defines And Includes][Compiler] Name=GCC Path=gcc Type=GCC [Launch] Launch Configurations=Launch Configuration 0 [Launch][Launch Configuration 0] Configured Launch Modes=execute Configured Launchers=nativeAppLauncher Name=Kwave_Debug Type=Native Application [Launch][Launch Configuration 0][Data] Arguments= Debugger Shell= Dependencies=@Variant(\x00\x00\x00\t\x00\x00\x00\x00\x01\x00\x00\x00\x0b\x00\x00\x00\x00\x01\x00\x00\x00\n\x00k\x00w\x00a\x00v\x00e) Dependency Action=Install Display Demangle Names=true Display Static Members=true EnvironmentGroup=Kwave-Debug Executable=file:///var/tmp/kwave-install/usr/bin/kwave External Terminal=konsole --noclose --workdir %workdir -e %exe GDB Path=file:///usr/bin/gdb Project Target=kwave,kwave,kwave_core Remote GDB Config Script= Remote GDB Run Script= Remote GDB Shell Script= Start With=ApplicationOutput Use External Terminal=false Working Directory= isExecutable=true [MakeBuilder] Default Make Environment Profile=Kwave-Debug Number Of Jobs=5 [Project] VersionControlSupport=kdevsubversion diff --git a/kwave/MainWidget.cpp b/kwave/MainWidget.cpp index ab913362..9df9d31a 100644 --- a/kwave/MainWidget.cpp +++ b/kwave/MainWidget.cpp @@ -1,1350 +1,1349 @@ /*************************************************************************** MainWidget.cpp - main widget of the Kwave TopWidget ------------------- begin : 1999 copyright : (C) 1999 by Martin Wilz email : Martin Wilz ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include "libkwave/CodecManager.h" #include "libkwave/Drag.h" #include "libkwave/FileDrag.h" #include "libkwave/FileInfo.h" #include "libkwave/Label.h" #include "libkwave/LabelList.h" #include "libkwave/Logger.h" #include "libkwave/MessageBox.h" #include "libkwave/Parser.h" #include "libkwave/SignalManager.h" #include "libkwave/String.h" #include "libkwave/Utils.h" #include "libkwave/undo/UndoTransactionGuard.h" #include "libgui/FileDialog.h" #include "libgui/LabelPropertiesWidget.h" #include "libgui/OverViewWidget.h" #include "libgui/SignalWidget.h" #include "App.h" #include "FileContext.h" #include "MainWidget.h" /** * useful macro for command parsing */ #define CASE_COMMAND(x) } else if (parser.command() == _(x)) { /** * Limits the zoom to a minimum number of samples visible in one * screen. */ #define MINIMUM_SAMPLES_PER_SCREEN 5 /** * Default widht of the display in seconds when in streaming mode, * where no initial length information is available (guess: 5min) */ #define DEFAULT_DISPLAY_TIME (5 * 60.0) /** * File extension used for label lists */ #define LABEL_LIST_EXT _("*.label") /** * Filter for file dialogs for loading/saving labels */ #define LABEL_LIST_FILTER \ LABEL_LIST_EXT + _("|") + \ i18n("Kwave label list") + \ _(" (") + LABEL_LIST_EXT + _(")") //*************************************************************************** Kwave::MainWidget::MainWidget(QWidget *parent, Kwave::FileContext &context, const QSize &preferred_size) :QWidget(parent), Kwave::CommandHandler(), Kwave::Zoomable(), m_context(context), m_upper_dock(), m_lower_dock(), m_scroll_area(this), m_horizontal_scrollbar(0), m_signal_widget( &m_scroll_area, context.signalManager(), &m_upper_dock, &m_lower_dock ), m_overview(0), m_offset(0), m_zoom(1.0), m_preferred_size(preferred_size), m_delayed_update_timer(this) { // qDebug("MainWidget::MainWidget()"); setAcceptDrops(true); // enable drag&drop Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return; Kwave::PluginManager *plugin_manager = context.pluginManager(); Q_ASSERT(plugin_manager); if (!plugin_manager) return; plugin_manager->registerViewManager(&m_signal_widget); // topLayout: // - upper dock // - view port // - lower dock // - overview // - horizontal scroll bar QVBoxLayout *topLayout = new QVBoxLayout(this); Q_ASSERT(topLayout); if (!topLayout) return; topLayout->setSpacing(0); // -- upper dock -- topLayout->addLayout(&m_upper_dock); // -- view port with signal widget -- m_signal_widget.setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ); m_scroll_area.setFrameStyle(QFrame::NoFrame); m_scroll_area.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scroll_area.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); m_scroll_area.setWidgetResizable(true); m_scroll_area.setWidget(&m_signal_widget); topLayout->addWidget(&m_scroll_area); // -- lower dock -- topLayout->addLayout(&m_lower_dock); // -- overview widget -- m_overview = new Kwave::OverViewWidget(*signal_manager, this); Q_ASSERT(m_overview); if (!m_overview) return; m_overview->setMinimumHeight(m_overview->sizeHint().height()); m_overview->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); topLayout->addWidget(m_overview); connect(m_overview, SIGNAL(valueChanged(sample_index_t)), this, SLOT(setOffset(sample_index_t))); connect(m_overview, SIGNAL(sigCommand(QString)), this, SIGNAL(sigCommand(QString))); m_overview->metaDataChanged(signal_manager->metaData()); m_overview->hide(); // -- horizontal scrollbar -- m_horizontal_scrollbar = new QScrollBar(this); Q_ASSERT(m_horizontal_scrollbar); if (!m_horizontal_scrollbar) return; m_horizontal_scrollbar->setOrientation(Qt::Horizontal); topLayout->addWidget(m_horizontal_scrollbar); connect(m_horizontal_scrollbar, SIGNAL(valueChanged(int)), this, SLOT(horizontalScrollBarMoved(int))); m_horizontal_scrollbar->hide(); // -- playback position update -- Kwave::PlaybackController &playback = signal_manager->playbackController(); connect(&playback, SIGNAL(sigPlaybackPos(sample_index_t)), m_overview, SLOT(showCursor(sample_index_t))); connect(&playback, SIGNAL(sigPlaybackStopped()), m_overview, SLOT(showCursor())); // -- connect all signals from/to the signal widget -- connect(&m_signal_widget, SIGNAL(sigCommand(QString)), this, SIGNAL(sigCommand(QString))); connect(&m_signal_widget, SIGNAL(sigCursorChanged(sample_index_t)), m_overview, SLOT(showCursor(sample_index_t))); // -- connect all signals from/to the signal manager -- connect(signal_manager, SIGNAL(sigTrackInserted(uint,Kwave::Track*)), this, SLOT(slotTrackInserted(uint,Kwave::Track*))); connect(signal_manager, SIGNAL(sigTrackDeleted(uint,Kwave::Track*)), this, SLOT(slotTrackDeleted(uint,Kwave::Track*))); connect(signal_manager, SIGNAL(sigMetaDataChanged(Kwave::MetaDataList)), this, SLOT(updateViewRange())); // set up the timer for delayed view range update m_delayed_update_timer.setSingleShot(true); connect(&m_delayed_update_timer, SIGNAL(timeout()), this, SLOT(updateViewRange())); setLayout(topLayout); } //*************************************************************************** bool Kwave::MainWidget::isOK() { return (m_horizontal_scrollbar && m_overview); } //*************************************************************************** Kwave::MainWidget::~MainWidget() { Kwave::PluginManager *plugin_manager = m_context.pluginManager(); Q_ASSERT(plugin_manager); if (plugin_manager) plugin_manager->registerViewManager(0); } //*************************************************************************** void Kwave::MainWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (!m_context.isActive() && (m_context.app().guiType() == Kwave::App::GUI_TAB)) { // HACK: this is a workaround for stupid bug in Qt, which sends us a // resize event with bogus size, when we are in tab mode, just // before getting deactivated !? if (!m_delayed_update_timer.isActive()) m_delayed_update_timer.start(0); } else { // update immediately updateViewRange(); } } //*************************************************************************** void Kwave::MainWidget::dragEnterEvent(QDragEnterEvent *event) { if (!event) return; if ((event->proposedAction() != Qt::MoveAction) && (event->proposedAction() != Qt::CopyAction)) return; /* unsupported action */ if (Kwave::FileDrag::canDecode(event->mimeData())) event->acceptProposedAction(); } //*************************************************************************** void Kwave::MainWidget::dropEvent(QDropEvent *event) { if (!event) return; if (!event->mimeData()) return; Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return; if (signal_manager->isEmpty() && Kwave::Drag::canDecode(event->mimeData())) { sample_index_t pos = m_offset + pixels2samples(event->pos().x()); sample_index_t len = 0; if ((len = Kwave::Drag::decode(this, event->mimeData(), *signal_manager, pos))) { // set selection to the new area where the drop was done signal_manager->selectRange(pos, len); event->acceptProposedAction(); } else { QStringList formats = event->mimeData()->formats(); QString fmt = (!formats.isEmpty()) ? formats.first() : QString(); qWarning("MainWidget::dropEvent(%s): failed !", DBG(fmt)); event->ignore(); } } } //*************************************************************************** void Kwave::MainWidget::wheelEvent(QWheelEvent *event) { if (!event || !m_overview) return; // process only wheel events on the signal and overview frame, // not on the channel controls or scrollbars if (!m_scroll_area.geometry().contains(event->pos()) && !m_overview->geometry().contains(event->pos()) ) { event->ignore(); return; } switch (event->modifiers()) { case Qt::NoModifier: { // no modifier + => scroll left/right if (event->delta() > 0) executeCommand(_("view:scroll_left()")); else if (event->delta() < 0) executeCommand(_("view:scroll_right()")); event->accept(); break; } case Qt::ShiftModifier: // + => page up/down if (event->delta() > 0) executeCommand(_("view:scroll_prev()")); else if (event->delta() < 0) executeCommand(_("view:scroll_next()")); event->accept(); break; case Qt::ControlModifier: { // + => zoom in/out int x = qMax(m_signal_widget.mapToViewPort(event->globalPos()), 0); if (event->delta() > 0) executeCommand(_("view:zoom_in(%1)").arg(x)); else if (event->delta() < 0) executeCommand(_("view:zoom_out(%1)").arg(x)); event->accept(); break; } default: event->ignore(); } } //*************************************************************************** void Kwave::MainWidget::closeEvent(QCloseEvent *e) { m_context.closeFile() ? e->accept() : e->ignore(); } //*************************************************************************** void Kwave::MainWidget::slotTrackInserted(unsigned int index, Kwave::Track *track) { // qDebug("MainWidget::slotTrackInserted(%u, %p)", // index, static_cast(track)); Q_UNUSED(index); Q_UNUSED(track); // when the first track has been inserted, set some reasonable zoom Kwave::SignalManager *signal_manager = m_context.signalManager(); bool first_track = (signal_manager && (signal_manager->tracks() == 1)); if (first_track) zoomAll(); else updateViewRange(); } //*************************************************************************** void Kwave::MainWidget::slotTrackDeleted(unsigned int index, Kwave::Track *track) { // qDebug("MainWidget::slotTrackDeleted(%u)", index); Q_UNUSED(index); Q_UNUSED(track); updateViewRange(); } //*************************************************************************** double Kwave::MainWidget::zoom() const { return m_zoom; } //*************************************************************************** int Kwave::MainWidget::executeCommand(const QString &command) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return -EINVAL; if (!command.length()) return -EINVAL; Kwave::Parser parser(command); const sample_index_t visible_samples = visibleSamples(); const sample_index_t signal_length = signal_manager->length(); if (false) { // -- zoom -- CASE_COMMAND("view:zoom_selection") zoomSelection(); CASE_COMMAND("view:zoom_in") int x = parser.hasParams() ? parser.toInt() : -1; zoomIn(x); CASE_COMMAND("view:zoom_out") int x = parser.hasParams() ? parser.toInt() : -1; zoomOut(x); CASE_COMMAND("view:zoom_normal") zoomNormal(); CASE_COMMAND("view:zoom_all") zoomAll(); // -- navigation -- CASE_COMMAND("goto") sample_index_t offset = parser.toSampleIndex(); setOffset((offset > (visible_samples / 2)) ? (offset - (visible_samples / 2)) : 0); signal_manager->selectRange(offset, 0); CASE_COMMAND("view:scroll_right") const sample_index_t step = visible_samples / 10; setOffset(m_offset + step); CASE_COMMAND("view:scroll_left") const sample_index_t step = visible_samples / 10; setOffset((step < m_offset) ? (m_offset - step) : 0); CASE_COMMAND("view:scroll_start") setOffset(0); signal_manager->selectRange(0, 0); CASE_COMMAND("view:scroll_end") if (signal_length >= visible_samples) setOffset(signal_length - visible_samples); CASE_COMMAND("view:scroll_next") setOffset(m_offset + visible_samples); CASE_COMMAND("view:scroll_prev") setOffset((visible_samples < m_offset) ? (m_offset - visible_samples) : 0); CASE_COMMAND("view:scroll_next_label") sample_index_t ofs = Kwave::LabelList(signal_manager->metaData()).nextLabelRight( m_offset + (visible_samples / 2)); if (ofs > signal_length) ofs = signal_length - 1; setOffset((ofs > (visible_samples / 2)) ? (ofs - (visible_samples / 2)) : 0); CASE_COMMAND("view:scroll_prev_label") sample_index_t ofs = Kwave::LabelList(signal_manager->metaData()).nextLabelLeft( m_offset + (visible_samples / 2)); setOffset((ofs > (visible_samples / 2)) ? (ofs - (visible_samples / 2)) : 0); // -- selection -- CASE_COMMAND("selectall") signal_manager->selectRange(0, signal_manager->length()); CASE_COMMAND("selectnext") if (signal_manager->selection().length()) signal_manager->selectRange(signal_manager->selection().last() + 1, signal_manager->selection().length()); else signal_manager->selectRange(signal_manager->length() - 1, 0); CASE_COMMAND("selectprev") sample_index_t ofs = signal_manager->selection().first(); sample_index_t len = signal_manager->selection().length(); if (!len) len = 1; if (len > ofs) len = ofs; signal_manager->selectRange(ofs - len, len); CASE_COMMAND("selecttoleft") signal_manager->selectRange(0, signal_manager->selection().last() + 1); CASE_COMMAND("selecttoright") signal_manager->selectRange(signal_manager->selection().first(), signal_manager->length() - signal_manager->selection().first() ); CASE_COMMAND("selectvisible") signal_manager->selectRange(m_offset, visibleSamples()); CASE_COMMAND("selectnone") signal_manager->selectRange(m_offset, 0); // label handling CASE_COMMAND("label:add") sample_index_t pos = parser.toSampleIndex(); if (!parser.isDone()) { // 2 parameters: position + description QString description = parser.nextParam(); signal_manager->addLabel(pos, description); } else { // 1 parameter only: open dialog for editing the description addLabel(pos, QString()); } CASE_COMMAND("label:edit") int index = parser.toInt(); Kwave::LabelList labels(signal_manager->metaData()); if ((index >= labels.count()) || (index < 0)) return -EINVAL; Kwave::Label label = labels.at(index); labelProperties(label); CASE_COMMAND("label:load") QString filename = parser.nextParam(); return loadLabels(filename); CASE_COMMAND("label:save") QString filename = parser.nextParam(); return saveLabels(filename); // CASE_COMMAND("label:by_intensity") // markSignal(command); // CASE_COMMAND("label:to_pitch") // convertMarkstoPitch(command); // CASE_COMMAND("label:by_period") // markPeriods(command); } else { return (signal_manager) ? signal_manager->executeCommand(command) : -ENOSYS; } return 0; } //*************************************************************************** void Kwave::MainWidget::refreshHorizontalScrollBar() { if (!m_horizontal_scrollbar || !m_context.signalManager()) return; m_horizontal_scrollbar->blockSignals(true); // show/hide the overview widget if (!m_context.signalManager()->isEmpty() && !m_overview->isVisible()) m_overview->show(); if (m_context.signalManager()->isEmpty() && m_overview->isVisible()) m_overview->hide(); // adjust the limits of the horizontal scrollbar if (m_context.signalManager()->length() > 1) { // get the view information in samples sample_index_t length = m_context.signalManager()->length(); sample_index_t visible = visibleSamples(); if (visible > length) visible = length; // calculate the scrollbar ranges in scrollbar's units // // NOTE: we must take care of possible numeric overflows // as the scrollbar internally works with "int" and // the offsets we use for the samples might be bigger! // // [-------------------------------------------##############] // ^ ^ ^ // min max page // // max + page = x | x < INT_MAX (!) // x := length / f // page = x * (visible / length) = visible / f // max = length / f - page // pos = (m_offset / length) * x = m_offset / f const sample_index_t f = qMax( sample_index_t(1), sample_index_t((length + INT_MAX - 1) / INT_MAX) ); int page = Kwave::toInt(visible / f); int min = 0; int max = Kwave::toInt(length / f) - page; int pos = Kwave::toInt(m_offset / f); int single = qMax(1, (page / (10 * qApp->wheelScrollLines()))); if (page < single) page = single; // qDebug("width=%d, max=%d, page=%d, single=%d, pos=%d, visible=%d", // m_width, max, page, single, pos, visible); // qDebug(" last=%d", pos + visible - 1); m_horizontal_scrollbar->setRange(min, max); m_horizontal_scrollbar->setValue(pos); m_horizontal_scrollbar->setSingleStep(single); m_horizontal_scrollbar->setPageStep(page); } else { m_horizontal_scrollbar->setRange(0, 0); } m_horizontal_scrollbar->blockSignals(false); } //*************************************************************************** void Kwave::MainWidget::horizontalScrollBarMoved(int newval) { // new offset = pos * f sample_index_t length = m_context.signalManager()->length(); const sample_index_t f = qMax( sample_index_t(1), sample_index_t((length + INT_MAX - 1) / INT_MAX) ); const sample_index_t pos = newval * f; setOffset(pos); } //*************************************************************************** void Kwave::MainWidget::updateViewRange() { Kwave::SignalManager *signal_manager = m_context.signalManager(); sample_index_t total = (signal_manager) ? signal_manager->length() : 0; if (m_overview) { m_overview->setRange(m_offset, visibleSamples(), total); // show/hide the overview widget and the horizontal scroll bar if (!m_context.signalManager()->isEmpty()) { if (!m_overview->isVisible()) m_overview->show(); if (!m_horizontal_scrollbar->isVisible()) m_horizontal_scrollbar->show(); } if (m_context.signalManager()->isEmpty()) { if (m_overview->isVisible()) m_overview->hide(); if (m_horizontal_scrollbar->isVisible()) m_horizontal_scrollbar->hide(); } } // forward the zoom and offset to the signal widget and overview fixZoomAndOffset(m_zoom, m_offset); m_signal_widget.setZoomAndOffset(m_zoom, m_offset); refreshHorizontalScrollBar(); } //*************************************************************************** sample_index_t Kwave::MainWidget::ms2samples(double ms) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return 0; return static_cast( rint(ms * signal_manager->rate() / 1E3)); } //*************************************************************************** sample_index_t Kwave::MainWidget::pixels2samples(unsigned int pixels) const { if ((pixels == 0) || (m_zoom <= 0.0)) return 0; return static_cast(static_cast(pixels) * m_zoom); } //*************************************************************************** int Kwave::MainWidget::samples2pixels(sample_index_t samples) const { if (m_zoom <= 0.0) return 0; return Kwave::toInt(rint(static_cast(samples) / m_zoom)); } //*************************************************************************** int Kwave::MainWidget::visibleWidth() const { return m_signal_widget.visibleWidth(); } //*************************************************************************** sample_index_t Kwave::MainWidget::visibleSamples() const { return pixels2samples(visibleWidth() - 1) + 1; } //*************************************************************************** double Kwave::MainWidget::fullZoom() const { const int width = visibleWidth(); if (width <= 0) return 0.0; // no zoom info, window is not yet ready Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return 0.0; if (signal_manager->isEmpty()) return 0.0; // no zoom if no signal sample_index_t length = signal_manager->length(); if (!length) { // no length: streaming mode -> try to use "estimated length" // and add some extra, 10% should be ok Kwave::FileInfo info(signal_manager->metaData()); if (info.contains(Kwave::INF_ESTIMATED_LENGTH)) { // estimated length in samples length = info.get(Kwave::INF_ESTIMATED_LENGTH).toULongLong(); length += length / 10; } if (length <= 0) { // fallback: start with some default zoom, // use five minutes (just guessed) length = static_cast(ceil(DEFAULT_DISPLAY_TIME * signal_manager->rate())); } } if (!length) return 0; // still no length !? -> bail out // example: width = 100 [pixels] and length = 3 [samples] // -> samples should be at positions 0, 49.5 and 99 // -> 49.5 [pixels / sample] // -> zoom = 1 / 49.5 [samples / pixel] // => full zoom [samples/pixel] = (length - 1) / (width - 1) if (length < static_cast(width) * 2) { // zoom is small enough, no error compensation needed return (static_cast(length) - 1.0) / static_cast(width - 1); } else { // zoom is big, pre-compensate rounding errors return (static_cast(length) - 0.5) / static_cast(width - 1); } } //*************************************************************************** void Kwave::MainWidget::fixZoomAndOffset(double zoom, sample_index_t offset) { double max_zoom; double min_zoom; sample_index_t length; const double old_zoom = m_zoom; const sample_index_t old_offset = m_offset; const int width = visibleWidth(); m_zoom = zoom; m_offset = offset; Q_ASSERT(m_zoom >= 0.0); Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return; if (!width) return; length = signal_manager->length(); if (!length) { // in streaming mode we have to use a guessed length length = static_cast(ceil(width * fullZoom())); } if (!length) { m_offset = 0; return; }; // ensure that m_offset is [0...length-1] if (m_offset >= length) m_offset = (length - 1); // ensure that the zoom is in a reasonable range max_zoom = fullZoom(); min_zoom = static_cast(MINIMUM_SAMPLES_PER_SCREEN) / static_cast(width); // ensure that pixel coordinates are within signed int range min_zoom = qMax(min_zoom, static_cast(length) / double(INT_MAX)); if (m_zoom < min_zoom) m_zoom = min_zoom; if (m_zoom > max_zoom) m_zoom = max_zoom; // try to correct the offset if there is not enough data to fill // the current window // example: width=100 [pixels], length=3 [samples], // offset=1 [sample], zoom=1/49.5 [samples/pixel] (full) // -> current last displayed sample = length-offset // = 3 - 1 = 2 // -> available space = pixels2samples(width-1) + 1 // = (99/49.5) + 1 = 3 // -> decrease offset by 3 - 2 = 1 Q_ASSERT(length >= m_offset); if ((pixels2samples(width - 1) + 1) > (length - m_offset)) { // there is space after the signal -> move offset right sample_index_t shift = pixels2samples(width - 1) + 1 - (length - m_offset); if (shift >= m_offset) { m_offset = 0; } else { m_offset -= shift; } } // emit change in the zoom factor if (!qFuzzyCompare(m_zoom, old_zoom)) emit sigZoomChanged(m_zoom); if ((m_offset != old_offset) || !qFuzzyCompare(m_zoom, old_zoom)) { emit sigVisibleRangeChanged(m_offset, visibleSamples(), length); updateViewRange(); } } //*************************************************************************** void Kwave::MainWidget::setZoom(double new_zoom) { fixZoomAndOffset(new_zoom, m_offset); } //*************************************************************************** void Kwave::MainWidget::setOffset(sample_index_t new_offset) { fixZoomAndOffset(m_zoom, new_offset); } //*************************************************************************** void Kwave::MainWidget::scrollTo(sample_index_t pos) { sample_index_t visible = visibleSamples(); if ( (pos < (m_offset + (visible / 10))) || (pos > (m_offset + (visible / 2))) ) { // new position is out of range or too close to the border sample_index_t offset = (visible / 2); pos = (pos > offset) ? (pos - offset) : 0; if (pos != m_offset) { // check: is the difference >= 1 pixel ? sample_index_t diff = (pos > m_offset) ? (pos - m_offset) : (m_offset - pos); if (samples2pixels(diff) >= 1) setOffset(pos); } } } //*************************************************************************** void Kwave::MainWidget::zoomSelection() { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return; sample_index_t ofs = signal_manager->selection().offset(); sample_index_t len = signal_manager->selection().length(); if (len) { int width = visibleWidth(); m_offset = ofs; setZoom((static_cast(len)) / static_cast(width - 1)); } } //*************************************************************************** void Kwave::MainWidget::zoomAll() { setZoom(fullZoom()); } //*************************************************************************** void Kwave::MainWidget::zoomNormal() { const sample_index_t shift1 = visibleWidth() / 2; const sample_index_t shift2 = visibleSamples() / 2; m_offset = (m_offset + shift2 >= m_offset) ? (m_offset + shift2) : -shift2; m_offset = (m_offset > shift1) ? (m_offset - shift1) : 0; setZoom(1.0); } //*************************************************************************** void Kwave::MainWidget::zoomIn(int pos) { if (pos >= 0) { // position given, calculate shift: // ofs_old + (pos * m_zoom) := ofs_new + (pos * (m_zoom / 3)) // ofs_new = ofs_old + (pos * m_zoom) - (pos * (m_zoom / 3)) fixZoomAndOffset( m_zoom / 3, m_offset + Kwave::toInt(pos * (m_zoom * (2.0 / 3.0))) ); } else { // no position given, show centered fixZoomAndOffset( m_zoom / 3, m_offset + (visibleSamples() / 3) ); } } //*************************************************************************** void Kwave::MainWidget::zoomOut(int pos) { sample_index_t shift; if (pos >= 0) { // position given, calculate shift // ofs_old + (pos * m_zoom) := ofs_new + (pos * (m_zoom * 3)) // ofs_new = ofs_old + (pos * m_zoom) - (pos * (m_zoom * 3)) shift = Kwave::toInt(m_zoom * 2.0) * pos; } else { // no position given, show centered shift = visibleSamples(); } fixZoomAndOffset( m_zoom * 3, (m_offset > shift) ? (m_offset - shift) : 0 ); } //*************************************************************************** void Kwave::MainWidget::addLabel(sample_index_t pos, const QString &description) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return; // remember the last "modified" state, in case that we abort bool was_modified = signal_manager->isModified(); // create an own undo transaction Kwave::UndoTransactionGuard undo(*signal_manager, i18n("Add Label")); // add a new label, with undo Kwave::Label label = signal_manager->addLabel(pos, description); if (label.isNull()) { signal_manager->abortUndoTransaction(); if (!was_modified) signal_manager->setModified(false); return; } // edit the properties of the new label if (!labelProperties(label)) { // aborted or failed -> delete (without undo) int index = signal_manager->labelIndex(label); signal_manager->deleteLabel(index, false); signal_manager->abortUndoTransaction(); if (!was_modified) signal_manager->setModified(false); } } //**************************************************************************** int Kwave::MainWidget::loadLabels(const QString &filename) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return -1; QUrl url; if (!filename.length()) { QString name(filename); QPointer dlg = new (std::nothrow)Kwave::FileDialog( _("kfiledialog:///kwave_label_dir"), Kwave::FileDialog::OpenFile, LABEL_LIST_FILTER, this, QUrl(), LABEL_LIST_EXT); if (!dlg) return -1; dlg->setWindowTitle(i18n("Load Labels")); if (dlg->exec() != QDialog::Accepted) { delete dlg; return 0; } else { url = dlg->selectedUrl(); delete dlg; } } else { url = QUrl::fromUserInput(filename); } // create an own undo transaction Kwave::UndoTransactionGuard undo(*signal_manager, i18n("Load Labels")); return m_context.loadBatch(url); } //**************************************************************************** int Kwave::MainWidget::saveLabels(const QString &filename) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return false; QUrl url; url = QUrl(m_context.signalName()); if (!filename.length()) { QString name(filename); QPointer dlg = new(std::nothrow)Kwave::FileDialog( _("kfiledialog:///kwave_label_dir"), Kwave::FileDialog::SaveFile, LABEL_LIST_FILTER, this, url, LABEL_LIST_EXT); if (!dlg) return 0; dlg->setWindowTitle(i18n("Save Labels")); if (dlg->exec() != QDialog::Accepted) { delete dlg; return -1; } url = dlg->selectedUrl(); if (url.isEmpty()) { delete dlg; return 0; } delete dlg; // add an extension if necessary name = url.path(); if (!QFileInfo(name).suffix().length()) { url.setPath(name + _(".label")); name = url.path(); } // check if the file exists and ask before overwriting it // if it is not the old filename if (QFileInfo(name).exists()) { if (Kwave::MessageBox::warningYesNo(this, i18n("The file '%1' already exists.\n" "Do you really want to overwrite it?", name)) != KMessageBox::Yes) { return -1; } } } else { url = QUrl::fromUserInput(filename); } // now we have a file name -> save all labels... Kwave::Logger::log(this, Kwave::Logger::Info, _("saving labels to '") + url.toDisplayString() + _("'")); QFile file(url.path()); file.open(QIODevice::WriteOnly); QTextStream out(&file); Kwave::LabelList labels(signal_manager->metaData()); foreach (const Kwave::Label &label, labels) { sample_index_t pos = label.pos(); const QString name = Kwave::Parser::escape(label.name()); out << _("label:add(") << pos; if (name.length()) out << _(", ") << name; out << _(")") << endl; } file.close(); return 0; } //*************************************************************************** bool Kwave::MainWidget::labelProperties(Kwave::Label &label) { Kwave::SignalManager *signal_manager = m_context.signalManager(); Q_ASSERT(signal_manager); if (!signal_manager) return false; if (label.isNull()) return false; int index = signal_manager->labelIndex(label); Q_ASSERT(index >= 0); if (index < 0) return false; // try to modify the label. just in case that the user moves it // to a position where we already have one, catch this situation // and ask if he wants to abort, re-enter the label properties // dialog or just replace (remove) the label at the target position bool accepted; sample_index_t new_pos = label.pos(); QString new_name = label.name(); int old_index = -1; while (true) { // create and prepare the dialog Kwave::LabelPropertiesWidget *dlg = new Kwave::LabelPropertiesWidget(this); Q_ASSERT(dlg); if (!dlg) return false; dlg->setLabelIndex(index); dlg->setLabelPosition(new_pos, signal_manager->length(), signal_manager->rate()); dlg->setLabelName(new_name); // execute the dialog accepted = (dlg->exec() == QDialog::Accepted); if (!accepted) { // user pressed "cancel" delete dlg; break; } // if we get here the user pressed "OK" new_pos = dlg->labelPosition(); new_name = dlg->labelName(); dlg->saveSettings(); delete dlg; // check: if there already is a label at the new position // -> ask the user if he wants to overwrite that one if ((new_pos != label.pos()) && !signal_manager->findLabel(new_pos).isNull()) { int res = Kwave::MessageBox::warningYesNoCancel(this, i18n( "There already is a label at the position you have chosen.\n"\ "Do you want to replace it?")); if (res == KMessageBox::Yes) { // delete the label at the target position (with undo) Kwave::Label old = signal_manager->findLabel(new_pos); old_index = signal_manager->labelIndex(old); break; } if (res == KMessageBox::No) { // make another try -> re-enter the dialog continue; } // cancel -> abort the whole action accepted = false; break; } else { // ok, we can put it there break; } } if (accepted) { // shortcut: abort if nothing has changed if ((new_name == label.name()) && (new_pos == label.pos())) return true; Kwave::UndoTransactionGuard undo(*signal_manager, i18n("Modify Label")); // if there is a label at the target position, remove it first if (old_index >= 0) { signal_manager->deleteLabel(old_index, true); // this might have changed the current index! index = signal_manager->labelIndex(label); } // modify the label through the signal manager if (!signal_manager->modifyLabel(index, new_pos, new_name, true)) { // position is already occupied signal_manager->abortUndoTransaction(); return false; } // reflect the change in the passed label label.moveTo(new_pos); label.rename(new_name); // NOTE: moving might also change the index, so the complete // markers layer has to be refreshed } else signal_manager->abortUndoTransaction(); return accepted; } // ////**************************************************************************** // //void MainWidget::markSignal (const char *str) // //{ // // if (signalmanage) { // // Kwave::Label *newmark; // // // // Kwave::Parser parser (str); // // // // int level = (int) (parser.toDouble() / 100 * (1 << 23)); // // // // int len = signalmanage->getLength(); // // int *sam = signalmanage->getSignal(0)->getSample(); // ### @@@ ### // // LabelType *start = findMarkerType(parser.getNextParam()); // // LabelType *stop = findMarkerType (parser.getNextParam()); // // int time = (int) (parser.toDouble () * signalmanage->getRate() / 1000); // // // // printf ("%d %d\n", level, time); // // printf ("%s %s\n", start->name, stop->name); // // // // ProgressDialog *dialog = // // new ProgressDialog (len, "Searching for Signal portions..."); // // // // if (dialog && start && stop) { // // dialog->show(); // // // // newmark = new Kwave::Label(0, start); //generate initial Kwave::Label // // // // labels->inSort (newmark); // // // // for (int i = 0; i < len; i++) { // // if (qAbs(sam[i]) < level) { // // int j = i; // // while ((i < len) && (qAbs(sam[i]) < level)) i++; // // // // if (i - j > time) { // // //insert labels... // // newmark = new Kwave::Label(i, start); // // labels->inSort (newmark); // // // // if (start != stop) { // // newmark = new Kwave::Label(j, stop); // // labels->inSort (newmark); // // } // // } // // } // // dialog->setProgress (i); // // } // // // // newmark = new Kwave::Label(len - 1, stop); // // labels->inSort (newmark); // // // // refresh (); // // delete dialog; // // } // // } // //} // ////**************************************************************************** // //void MainWidget::markPeriods (const char *str) // //{ // // if (signalmanage) { // // Kwave::Parser parser (str); // // // // int high = signalmanage->getRate() / parser.toInt(); // // int low = signalmanage->getRate() / parser.toInt(); // // int octave = parser.toBool ("true"); // // double adjust = parser.toDouble (); // // // // for (int i = 0; i < AUTOKORRWIN; i++) // // autotable[i] = 1 - (((double)i * i * i) / (AUTOKORRWIN * AUTOKORRWIN * AUTOKORRWIN)); //generate static weighting function // // // // if (octave) for (int i = 0; i < AUTOKORRWIN; i++) weighttable[i] = 1; //initialise moving weight table // // // // Kwave::Label *newmark; // // int next; // // int len = signalmanage->getLength(); // // int *sam = signalmanage->getSignal(0)->getSample(); // ### @@@ ### // // LabelType *start = markertype; // // int cnt = findFirstMark (sam, len); // // // // ProgressDialog *dialog = new ProgressDialog (len - AUTOKORRWIN, "Correlating Signal to find Periods:"); // // if (dialog) { // // dialog->show(); // // // // newmark = new Kwave::Label(cnt, start); // // labels->inSort (newmark); // // // // while (cnt < len - 2*AUTOKORRWIN) { // // if (octave) // // next = findNextRepeatOctave (&sam[cnt], high, adjust); // // else // // next = findNextRepeat (&sam[cnt], high); // // // // if ((next < low) && (next > high)) { // // newmark = new Kwave::Label(cnt, start); // // // // labels->inSort (newmark); // // } // // if (next < AUTOKORRWIN) cnt += next; // // else // // if (cnt < len - AUTOKORRWIN) { // // int a = findFirstMark (&sam[cnt], len - cnt); // // if (a > 0) cnt += a; // // else cnt += high; // // } else cnt = len; // // // // dialog->setProgress (cnt); // // } // // // // delete dialog; // // // // refresh (); // // } // // } // //} // ////***************************************************************************** // //int findNextRepeat (int *sample, int high) // ////autocorellation of a windowed part of the sample // ////returns length of period, if found // //{ // // int i, j; // // double gmax = 0, max, c; // // int maxpos = AUTOKORRWIN; // // int down, up; //flags // // // // max = 0; // // for (j = 0; j < AUTOKORRWIN; j++) // // gmax += ((double)sample[j]) * sample [j]; // // // // //correlate signal with itself for finding maximum integral // // // // down = 0; // // up = 0; // // i = high; // // max = 0; // // while (i < AUTOKORRWIN) { // // c = 0; // // for (j = 0; j < AUTOKORRWIN; j++) c += ((double)sample[j]) * sample [i + j]; // // c = c * autotable[i]; //multiply window with weight for preference of high frequencies // // if (c > max) max = c, maxpos = i; // // i++; // // } // // return maxpos; // //} // ////***************************************************************************** // //int findNextRepeatOctave (int *sample, int high, double adjust = 1.005) // ////autocorellation of a windowed part of the sample // ////same as above only with an adaptive weighting to decrease fast period changes // //{ // // int i, j; // // double gmax = 0, max, c; // // int maxpos = AUTOKORRWIN; // // int down, up; //flags // // // // max = 0; // // for (j = 0; j < AUTOKORRWIN; j++) // // gmax += ((double)sample[j]) * sample [j]; // // // // //correlate signal with itself for finding maximum integral // // // // down = 0; // // up = 0; // // i = high; // // max = 0; // // while (i < AUTOKORRWIN) { // // c = 0; // // for (j = 0; j < AUTOKORRWIN; j++) c += ((double)sample[j]) * sample [i + j]; // // c = c * autotable[i] * weighttable[i]; // // //multiply window with weight for preference of high frequencies // // if (c > max) max = c, maxpos = i; // // i++; // // } // // // // for (int i = 0; i < AUTOKORRWIN; i++) weighttable[i] /= adjust; // // // // weighttable[maxpos] = 1; // // weighttable[maxpos + 1] = .9; // // weighttable[maxpos - 1] = .9; // // weighttable[maxpos + 2] = .8; // // weighttable[maxpos - 2] = .8; // // // // float buf[7]; // // // // for (int i = 0; i < 7; buf[i++] = .1) // // // // //low pass filter // // for (int i = high; i < AUTOKORRWIN - 3; i++) { // // buf[i % 7] = weighttable[i + 3]; // // weighttable[i] = (buf[0] + buf[1] + buf[2] + buf[3] + buf[4] + buf[5] + buf[6]) / 7; // // } // // // // return maxpos; // //} // //***************************************************************************** // //int findFirstMark (int *sample, int len) // ////finds first sample that is non-zero, or one that preceeds a zero crossing // //{ // // int i = 1; // // int last = sample[0]; // // int act = last; // // if ((last < 100) && (last > -100)) i = 0; // // else // // while (i < len) { // // act = sample[i]; // // if ((act < 0) && (last >= 0)) break; // // if ((act > 0) && (last <= 0)) break; // // last = act; // // i++; // // } // // return i; // //} //*************************************************************************** //*************************************************************************** diff --git a/libkwave/CodecManager.cpp b/libkwave/CodecManager.cpp index 6cd1fe7b..8d927821 100644 --- a/libkwave/CodecManager.cpp +++ b/libkwave/CodecManager.cpp @@ -1,242 +1,244 @@ /*************************************************************************** CodecManager.cpp - manager for Kwave's coders and decoders ------------------- begin : Mar 10 2002 copyright : (C) 2002 by Thomas Eschenbacher email : Thomas Eschenbacher ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include #include #include #include #include #include "libkwave/CodecManager.h" #include "libkwave/Decoder.h" #include "libkwave/Encoder.h" #include "libkwave/String.h" //*************************************************************************** /* static initializers */ QList Kwave::CodecManager::m_encoders; QList Kwave::CodecManager::m_decoders; //*************************************************************************** //*************************************************************************** Kwave::CodecManager::CodecManager() { } //*************************************************************************** Kwave::CodecManager::~CodecManager() { Q_ASSERT(m_encoders.isEmpty()); Q_ASSERT(m_decoders.isEmpty()); } //*************************************************************************** void Kwave::CodecManager::registerEncoder(Kwave::Encoder &encoder) { if (m_encoders.contains(&encoder)) return; /* already known */ m_encoders.append(&encoder); } //*************************************************************************** void Kwave::CodecManager::unregisterEncoder(Kwave::Encoder *encoder) { if (!m_encoders.contains(encoder)) return; /* unknown */ m_encoders.removeAll(encoder); } //*************************************************************************** void Kwave::CodecManager::registerDecoder(Kwave::Decoder &decoder) { if (m_decoders.contains(&decoder)) return; /* already known */ m_decoders.append(&decoder); } //*************************************************************************** void Kwave::CodecManager::unregisterDecoder(Kwave::Decoder *decoder) { if (!m_decoders.contains(decoder)) return; /* unknown */ m_decoders.removeAll(decoder); } //*************************************************************************** bool Kwave::CodecManager::canDecode(const QMimeType &mimetype) { foreach (Kwave::Decoder *d, m_decoders) if (d && d->supports(mimetype)) return true; return false; } //*************************************************************************** bool Kwave::CodecManager::canDecode(const QString &mimetype_name) { foreach (Kwave::Decoder *d, m_decoders) if (d && d->supports(mimetype_name)) return true; return false; } //*************************************************************************** QString Kwave::CodecManager::whatContains(const QUrl &url) { + const QString default_mime_type = QMimeType().name(); + foreach (Kwave::Decoder *d, m_decoders) { if (!d) continue; QString mime_type = d->whatContains(url); - if (mime_type != QMimeType().name()) return mime_type; + if (mime_type != default_mime_type) return mime_type; } foreach (Kwave::Encoder *e, m_encoders) { if (!e) continue; QString mime_type = e->whatContains(url); - if (mime_type != QMimeType().name()) return mime_type; + if (mime_type != default_mime_type) return mime_type; } QMimeDatabase db; return db.mimeTypeForUrl(url).name(); } //*************************************************************************** QStringList Kwave::CodecManager::encodingMimeTypes() { QStringList list; foreach (Kwave::Encoder *e, m_encoders) { if (!e) continue; foreach (const Kwave::CodecBase::MimeType &mime_type, e->mimeTypes()) { QString name = mime_type.name; if (list.isEmpty() || !list.contains(name)) list.append(name); } } return list; } //*************************************************************************** Kwave::Decoder *Kwave::CodecManager::decoder(const QString &mimetype_name) { foreach (Kwave::Decoder *d, m_decoders) if (d && d->supports(mimetype_name)) return d->instance(); return 0; } //*************************************************************************** Kwave::Decoder *Kwave::CodecManager::decoder(const QMimeType &mimetype) { return decoder(mimetype.name()); } //*************************************************************************** Kwave::Decoder *Kwave::CodecManager::decoder(const QMimeData *mime_data) { if (!mime_data) return 0; foreach (const QString &format, mime_data->formats()) { Kwave::Decoder *d = decoder(format); if (d) return d; } return 0; } //*************************************************************************** Kwave::Encoder *Kwave::CodecManager::encoder(const QString &mimetype_name) { foreach (Kwave::Encoder *e, m_encoders) if (e && e->supports(mimetype_name)) return e->instance(); return 0; } //*************************************************************************** QString Kwave::CodecManager::encodingFilter() { QStringList list; foreach (Kwave::Encoder *e, m_encoders) { // loop over all mime types that the encoder supports QList types = e->mimeTypes(); QListIterator ti(types); while (ti.hasNext()) { Kwave::CodecBase::MimeType type = ti.next(); QString extensions = type.patterns.join(_(" ")); // skip if extensions are already known/present if (!list.isEmpty() && list.join(_("\n")).contains(extensions)) continue; // otherwise append to the list QString entry = extensions; QString comment = type.description.replace( QRegExp(_("/")), _(",")); entry += _("|") + comment; list.append(entry + _(" (") + extensions + _(")")); } } list.sort(); QString str_list = list.join(_("\n")); Q_ASSERT(!str_list.contains(QLatin1Char('/'))); if (str_list.contains(QLatin1Char('/'))) { qWarning("CodecManager::encodingFilter() -> '%s'", DBG(str_list)); } return str_list; } //*************************************************************************** QString Kwave::CodecManager::decodingFilter() { QStringList list; QStringList all_extensions; foreach (Kwave::Decoder *d, m_decoders) { // loop over all mime types that the decoder supports QList types = d->mimeTypes(); QListIterator ti(types); while (ti.hasNext()) { Kwave::CodecBase::MimeType type = ti.next(); QString extensions = type.patterns.join(_(" ")); // skip if extensions are already known/present if (!list.isEmpty() && list.join(_("\n")).contains(extensions)) continue; // otherwise append to the list all_extensions += type.patterns; QString entry = extensions; QString comment = type.description.replace(QRegExp(_("/")), _(",")); entry += _("|") + comment; list.append(entry + _(" (") + extensions + _(")")); } } // builtin type for macro files all_extensions += _("*.kwave"); list.append(_("*.kwave|") + i18n("Kwave Macro Files") + _(" (*.kwave)")); // special entries for "all" and "all supported" list.sort(); list.prepend(_("*|") + i18n("All Files")); list.prepend(all_extensions.join(_(" ")) + _("|") + i18n("All Supported Files")); QString str_list = list.join(_("\n")); Q_ASSERT(!str_list.contains(QLatin1Char('/'))); if (str_list.contains(QLatin1Char('/'))) { qWarning("CodecManager::decodingFilter() -> '%s'", DBG(str_list)); } return str_list; } //*************************************************************************** //*************************************************************************** diff --git a/plugins/codec_ascii/AsciiCodecPlugin.cpp b/plugins/codec_ascii/AsciiCodecPlugin.cpp index bce0ba68..577caa08 100644 --- a/plugins/codec_ascii/AsciiCodecPlugin.cpp +++ b/plugins/codec_ascii/AsciiCodecPlugin.cpp @@ -1,61 +1,60 @@ /************************************************************************* AsciiCodecPlugin.cpp - import/export of ASCII data ------------------- begin : Sun Nov 28 2006 copyright : (C) 2006 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include -#include "libkwave/CodecManager.h" #include "libkwave/PluginManager.h" #include "AsciiCodecPlugin.h" #include "AsciiDecoder.h" #include "AsciiEncoder.h" // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::AsciiCodecPlugin::m_codec = EMPTY_CODEC; KWAVE_PLUGIN(codec_ascii, AsciiCodecPlugin) /***************************************************************************/ Kwave::AsciiCodecPlugin::AsciiCodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::AsciiCodecPlugin::~AsciiCodecPlugin() { } /***************************************************************************/ QList Kwave::AsciiCodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::AsciiCodecPlugin::createEncoder() { return singleEncoder(); } /***************************************************************************/ #include "AsciiCodecPlugin.moc" /***************************************************************************/ /***************************************************************************/ diff --git a/plugins/codec_audiofile/AudiofileCodecPlugin.cpp b/plugins/codec_audiofile/AudiofileCodecPlugin.cpp index c1b032d2..f5d085df 100644 --- a/plugins/codec_audiofile/AudiofileCodecPlugin.cpp +++ b/plugins/codec_audiofile/AudiofileCodecPlugin.cpp @@ -1,59 +1,58 @@ /************************************************************************* AudiofileCodecPlugin.cpp - import/export through libaudiofile ------------------- begin : Tue May 28 2002 copyright : (C) 2002 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include -#include "libkwave/CodecManager.h" #include "libkwave/PluginManager.h" #include "AudiofileCodecPlugin.h" #include "AudiofileDecoder.h" // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::AudiofileCodecPlugin::m_codec = EMPTY_CODEC; KWAVE_PLUGIN(codec_audiofile, AudiofileCodecPlugin) /***************************************************************************/ Kwave::AudiofileCodecPlugin::AudiofileCodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::AudiofileCodecPlugin::~AudiofileCodecPlugin() { } /***************************************************************************/ QList Kwave::AudiofileCodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::AudiofileCodecPlugin::createEncoder() { return QList(); /* not implemented */ } /***************************************************************************/ #include "AudiofileCodecPlugin.moc" /***************************************************************************/ /***************************************************************************/ diff --git a/plugins/codec_flac/FlacCodecPlugin.cpp b/plugins/codec_flac/FlacCodecPlugin.cpp index e0bae5cd..a6ce2378 100644 --- a/plugins/codec_flac/FlacCodecPlugin.cpp +++ b/plugins/codec_flac/FlacCodecPlugin.cpp @@ -1,61 +1,60 @@ /************************************************************************* FlacCodecPlugin.cpp - import/export of FLAC data ------------------- begin : Tue Feb 28 2004 copyright : (C) 2004 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include -#include "libkwave/CodecManager.h" #include "libkwave/PluginManager.h" #include "FlacCodecPlugin.h" #include "FlacDecoder.h" #include "FlacEncoder.h" // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::FlacCodecPlugin::m_codec = EMPTY_CODEC; KWAVE_PLUGIN(codec_flac, FlacCodecPlugin) /***************************************************************************/ Kwave::FlacCodecPlugin::FlacCodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::FlacCodecPlugin::~FlacCodecPlugin() { } /***************************************************************************/ QList Kwave::FlacCodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::FlacCodecPlugin::createEncoder() { return singleEncoder(); } /***************************************************************************/ #include "FlacCodecPlugin.moc" /***************************************************************************/ /***************************************************************************/ diff --git a/plugins/codec_mp3/MP3CodecPlugin.cpp b/plugins/codec_mp3/MP3CodecPlugin.cpp index bc92d390..52d2e126 100644 --- a/plugins/codec_mp3/MP3CodecPlugin.cpp +++ b/plugins/codec_mp3/MP3CodecPlugin.cpp @@ -1,95 +1,94 @@ /************************************************************************* MP3CodecPlugin.cpp - import and export of MP3 data ------------------- begin : Mon May 28 2012 copyright : (C) 2012 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" -#include "libkwave/CodecManager.h" #include "libkwave/String.h" #include "MP3CodecPlugin.h" #include "MP3Decoder.h" #include "MP3Encoder.h" #include "MP3EncoderDialog.h" KWAVE_PLUGIN(codec_mp3, MP3CodecPlugin) // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::MP3CodecPlugin::m_codec = EMPTY_CODEC; /***************************************************************************/ Kwave::MP3CodecPlugin::MP3CodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::MP3CodecPlugin::~MP3CodecPlugin() { } /***************************************************************************/ void Kwave::MP3CodecPlugin::load(QStringList ¶ms) { emitCommand(_("menu (plugin:setup(codec_mp3), Settings/%1)").arg( _(I18N_NOOP2("menu: /Settings/MP3 Encoder Setup", "MP3 Encoder Setup")))); Kwave::CodecPlugin::load(params); } //*************************************************************************** QStringList *Kwave::MP3CodecPlugin::setup(QStringList &previous_params) { Q_UNUSED(previous_params); // create the setup dialog MP3EncoderDialog *dialog = new MP3EncoderDialog(parentWidget()); Q_ASSERT(dialog); if (!dialog) return 0; QStringList *list = new QStringList(); Q_ASSERT(list); if (list && dialog->exec()) { // user has pressed "OK" dialog->save(); } else { // user pressed "Cancel" if (list) delete list; list = 0; } if (dialog) delete dialog; return list; } /***************************************************************************/ QList Kwave::MP3CodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::MP3CodecPlugin::createEncoder() { return singleEncoder(); } //*************************************************************************** #include "MP3CodecPlugin.moc" //*************************************************************************** //*************************************************************************** diff --git a/plugins/codec_ogg/OggCodecPlugin.cpp b/plugins/codec_ogg/OggCodecPlugin.cpp index 89b7b762..d336f08d 100644 --- a/plugins/codec_ogg/OggCodecPlugin.cpp +++ b/plugins/codec_ogg/OggCodecPlugin.cpp @@ -1,60 +1,58 @@ /************************************************************************* OggCodecPlugin.cpp - import/export of audio in an Ogg container ------------------- begin : Tue Sep 10 2002 copyright : (C) 2002 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #include -#include "libkwave/CodecManager.h" - #include "OggCodecPlugin.h" #include "OggDecoder.h" #include "OggEncoder.h" KWAVE_PLUGIN(codec_ogg, OggCodecPlugin) // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::OggCodecPlugin::m_codec = EMPTY_CODEC; /***************************************************************************/ Kwave::OggCodecPlugin::OggCodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::OggCodecPlugin::~OggCodecPlugin() { } /***************************************************************************/ QList Kwave::OggCodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::OggCodecPlugin::createEncoder() { return singleEncoder(); } //*************************************************************************** #include "OggCodecPlugin.moc" //*************************************************************************** //*************************************************************************** diff --git a/plugins/codec_wav/WavCodecPlugin.cpp b/plugins/codec_wav/WavCodecPlugin.cpp index afc06549..5e14e6ec 100644 --- a/plugins/codec_wav/WavCodecPlugin.cpp +++ b/plugins/codec_wav/WavCodecPlugin.cpp @@ -1,58 +1,56 @@ /************************************************************************* WavCodecPlugin.cpp - import/export of wav data ------------------- begin : Sun Mar 10 2002 copyright : (C) 2002 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" -#include "libkwave/CodecManager.h" - #include "WavCodecPlugin.h" #include "WavDecoder.h" #include "WavEncoder.h" KWAVE_PLUGIN(codec_wav, WavCodecPlugin) // static instance of the codec container Kwave::CodecPlugin::Codec Kwave::WavCodecPlugin::m_codec = EMPTY_CODEC; /***************************************************************************/ Kwave::WavCodecPlugin::WavCodecPlugin(QObject *parent, const QVariantList &args) :Kwave::CodecPlugin(parent, args, m_codec) { } /***************************************************************************/ Kwave::WavCodecPlugin::~WavCodecPlugin() { } /***************************************************************************/ QList Kwave::WavCodecPlugin::createDecoder() { return singleDecoder(); } /***************************************************************************/ QList Kwave::WavCodecPlugin::createEncoder() { return singleEncoder(); } //*************************************************************************** #include "WavCodecPlugin.moc" //*************************************************************************** //*************************************************************************** diff --git a/plugins/playback/PlayBack-OSS.cpp b/plugins/playback/PlayBack-OSS.cpp index 88396d3c..51027330 100644 --- a/plugins/playback/PlayBack-OSS.cpp +++ b/plugins/playback/PlayBack-OSS.cpp @@ -1,622 +1,622 @@ /*************************************************************************** PlayBack-OSS.cpp - playback device for standard linux OSS ------------------- begin : Sat May 19 2001 copyright : (C) 2001 by Thomas Eschenbacher email : Thomas.Eschenbacher@gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "config.h" #ifdef HAVE_OSS_SUPPORT #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libkwave/ByteOrder.h" #include "libkwave/Compression.h" #include "libkwave/SampleEncoderLinear.h" #include "libkwave/String.h" #include "libkwave/Utils.h" #include "libkwave/memcpy.h" #include "PlayBack-OSS.h" /** use at least 2^8 = 256 bytes for playback buffer !!! */ #define MIN_PLAYBACK_BUFFER 8 /** use at most 2^16 = 65536 bytes for playback buffer !!! */ #define MAX_PLAYBACK_BUFFER 16 /** highest available number of channels */ #define MAX_CHANNELS 7 // Linux 2.6.24 and above's OSS emulation supports 24 and 32 bit formats // but did not declare them in #ifndef AFMT_S24_LE #define AFMT_S24_LE 0x00008000 #endif #ifndef AFMT_S24_BE #define AFMT_S24_BE 0x00010000 #endif #ifndef AFMT_S32_LE #define AFMT_S32_LE 0x00001000 #endif #ifndef AFMT_S32_BE #define AFMT_S32_BE 0x00002000 #endif #ifndef SNDCTL_DSP_SPEED #define SNDCTL_DSP_SPEED SOUND_PCM_WRITE_RATE #endif #ifndef SNDCTL_DSP_CHANNELS #define SNDCTL_DSP_CHANNELS SOUND_PCM_WRITE_CHANNELS #endif #ifndef SOUND_PCM_SETFMT #define SOUND_PCM_SETFMT SOUND_PCM_WRITE_BITS #endif #ifndef SNDCTL_DSP_SETFMT #define SNDCTL_DSP_SETFMT SOUND_PCM_SETFMT #endif //*************************************************************************** Kwave::PlayBackOSS::PlayBackOSS() :Kwave::PlayBackDevice(), m_device_name(), m_handle(-1), m_rate(0), m_channels(0), m_bits(0), m_bufbase(0), m_buffer(), m_raw_buffer(), m_buffer_size(0), m_buffer_used(0), m_encoder(0), m_oss_version(-1) { } //*************************************************************************** Kwave::PlayBackOSS::~PlayBackOSS() { close(); } //*************************************************************************** QString Kwave::PlayBackOSS::open(const QString &device, double rate, unsigned int channels, unsigned int bits, unsigned int bufbase) { qDebug("PlayBackOSS::open(device=%s,rate=%0.1f,channels=%u," "bits=%u, bufbase=%u)", DBG(device.split(_("|")).at(0)), rate, channels, bits, bufbase); m_device_name = device; m_rate = rate; m_channels = channels; m_bits = bits; m_bufbase = bufbase; m_buffer_size = 0; m_buffer_used = 0; m_handle = 0; // prepeare for playback by opening the sound device // and initializing with the proper settings m_handle = ::open(m_device_name.toLocal8Bit(), O_WRONLY | O_NONBLOCK); if (m_handle == -1) { QString reason; switch (errno) { case ENOENT: case ENODEV: case ENXIO: case EIO: reason = i18n("I/O error. Maybe the driver\n"\ "is not present in your kernel or it is not\n"\ "properly configured."); break; case EBUSY: reason = i18n( "The device is busy. Maybe some other application is\n"\ "currently using it. Please try again later.\n"\ "(Hint: you might find out the name and process ID of\n"\ "the program by calling: \"fuser -v %1\"\n"\ "on the command line.)", m_device_name.section(QLatin1Char('|'), 0, 0)); break; default: reason = QString::fromLocal8Bit(strerror(errno)); } return reason; } // the device was opened in non-blocking mode to detect if it is // busy or not - but from now on we need blocking mode again ::fcntl(m_handle, F_SETFL, fcntl(m_handle, F_GETFL) & ~O_NONBLOCK); if (fcntl(m_handle, F_GETFL) & O_NONBLOCK) { // resetting O:NONBLOCK failed return i18n("The device '%1' cannot be opened "\ "in the correct mode.", m_device_name.section(QLatin1Char('|'), 0, 0)); } // query OSS driver version m_oss_version = 0x030000; #ifdef OSS_GETVERSION ioctl(m_handle, OSS_GETVERSION, &m_oss_version); #endif int format; switch (m_bits) { case 8: format = AFMT_U8; break; case 24: format = AFMT_S24_LE; break; case 32: format = AFMT_S32_LE; break; default: format = AFMT_S16_LE; } // number of bits per sample int oldformat = format; if ((ioctl(m_handle, SNDCTL_DSP_SETFMT, &format) == -1) || (format != oldformat)) { return i18n("%1 bits per sample are not supported", m_bits); } // channels selection if ((ioctl(m_handle, SNDCTL_DSP_CHANNELS, &m_channels) == -1) || (format != oldformat)) { return i18n("%1 channels playback is not supported", m_channels); } // playback rate, we allow up to 10% variation int int_rate = Kwave::toInt(m_rate); if ((ioctl(m_handle, SNDCTL_DSP_SPEED, &int_rate) == -1) || (int_rate < 0.9 * m_rate) || (int_rate > 1.1 * m_rate)) { return i18n("Playback rate %1 Hz is not supported", int_rate); } m_rate = int_rate; // buffer size Q_ASSERT(bufbase >= MIN_PLAYBACK_BUFFER); Q_ASSERT(bufbase <= MAX_PLAYBACK_BUFFER); if (bufbase < MIN_PLAYBACK_BUFFER) bufbase = MIN_PLAYBACK_BUFFER; if (bufbase > MAX_PLAYBACK_BUFFER) bufbase = MAX_PLAYBACK_BUFFER; if (ioctl(m_handle, SNDCTL_DSP_SETFRAGMENT, &bufbase) == -1) { return i18n("Unusable buffer size: %1", 1 << bufbase); } // get the real buffer size in bytes ioctl(m_handle, SNDCTL_DSP_GETBLKSIZE, &m_buffer_size); // create the sample encoder // we assume that OSS is always little endian if (m_encoder) delete m_encoder; switch (m_bits) { case 8: m_encoder = new Kwave::SampleEncoderLinear( Kwave::SampleFormat::Unsigned, 8, Kwave::LittleEndian); break; case 24: if (m_oss_version >= 0x040000) { m_encoder = new Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 24, Kwave::LittleEndian); break; } // else: /* FALLTHROUGH */ case 32: if (m_oss_version >= 0x040000) { m_encoder = new Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 32, Kwave::LittleEndian); break; } // else: /* FALLTHROUGH */ default: m_encoder = new Kwave::SampleEncoderLinear( Kwave::SampleFormat::Signed, 16, Kwave::LittleEndian); break; } Q_ASSERT(m_encoder); if (!m_encoder) return i18n("Out of memory"); // resize the raw buffer m_raw_buffer.resize(m_buffer_size); // resize our buffer (size in samples) and reset it m_buffer_size /= m_encoder->rawBytesPerSample(); if (!m_buffer.resize(m_buffer_size)) return i18n("Out of memory"); return QString(); } //*************************************************************************** int Kwave::PlayBackOSS::write(const Kwave::SampleArray &samples) { Q_ASSERT (m_buffer_used <= m_buffer_size); if (m_buffer_used > m_buffer_size) { qWarning("PlayBackOSS::write(): buffer overflow ?!"); m_buffer_used = m_buffer_size; flush(); return -EIO; } // number of samples left in the buffer unsigned int remaining = samples.size(); unsigned int offset = 0; while (remaining) { unsigned int length = remaining; if (m_buffer_used + length > m_buffer_size) length = m_buffer_size - m_buffer_used; MEMCPY(&(m_buffer[m_buffer_used]), &(samples[offset]), length * sizeof(sample_t)); m_buffer_used += length; offset += length; remaining -= length; // write buffer to device if it has become full if (m_buffer_used >= m_buffer_size) flush(); } return 0; } //*************************************************************************** void Kwave::PlayBackOSS::flush() { if (!m_buffer_used || !m_encoder) return; // nothing to do // convert into byte stream unsigned int bytes = m_buffer_used * m_encoder->rawBytesPerSample(); m_encoder->encode(m_buffer, m_buffer_used, m_raw_buffer); ssize_t res = 0; if (m_handle) res = ::write(m_handle, m_raw_buffer.data(), bytes); if (res < 0) perror(__FUNCTION__); m_buffer_used = 0; } //*************************************************************************** int Kwave::PlayBackOSS::close() { flush(); // close the device handle if (m_handle) ::close(m_handle); // get rid of the old encoder if (m_encoder) delete m_encoder; m_encoder = 0; return 0; } //*************************************************************************** static bool addIfExists(QStringList &list, const QString &name) { QFile file; if (name.contains(_("%1"))) { // test for the name without suffix first addIfExists(list, name.arg(_(""))); // loop over the list and try until a suffix does not exist for (unsigned int index=0; index < 64; index++) addIfExists(list, name.arg(index)); } else { // check a single name file.setFileName(name); if (!file.exists()) return false; if (!list.contains(name)) list.append(name); } return true; } //*************************************************************************** static void scanFiles(QStringList &list, const QString &dirname, const QString &mask) { QStringList files; QDir dir; dir.setPath(dirname); dir.setNameFilters(mask.split(QLatin1Char(' '))); dir.setFilter(QDir::Files | QDir::Writable | QDir::System); dir.setSorting(QDir::Name); files = dir.entryList(); for (QStringList::Iterator it=files.begin(); it != files.end(); ++it) { QString devicename = dirname + QDir::separator() + (*it); addIfExists(list, devicename); } } //*************************************************************************** static void scanDirectory(QStringList &list, const QString &dir) { scanFiles(list, dir, _("dsp*")); scanFiles(list, dir, _("*audio*")); scanFiles(list, dir, _("adsp*")); scanFiles(list, dir, _("dio*")); scanFiles(list, dir, _("pcm*")); } //*************************************************************************** QStringList Kwave::PlayBackOSS::supportedDevices() { QStringList list, dirlist; scanDirectory(list, _("/dev")); scanDirectory(list, _("/dev/snd")); scanDirectory(list, _("/dev/sound")); scanFiles(dirlist, _("/dev/oss"), _("[^.]*")); foreach (QString dir, dirlist) scanDirectory(list, dir); list.append(_("#EDIT#")); list.append(_("#SELECT#")); return list; } //*************************************************************************** QString Kwave::PlayBackOSS::fileFilter() { QString filter; if (filter.length()) filter += _("\n"); filter += _("dsp*|") + i18n("OSS playback device (dsp*)"); if (filter.length()) filter += _("\n"); filter += _("adsp*|") + i18n("ALSA playback device (adsp*)"); if (filter.length()) filter += _("\n"); filter += _("*|") + i18n("Any device (*)"); return filter; } //*************************************************************************** void Kwave::PlayBackOSS::format2mode(int format, int &compression, int &bits, Kwave::SampleFormat::Format &sample_format) const { switch (format) { case AFMT_MU_LAW: compression = Kwave::Compression::G711_ULAW; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_A_LAW: compression = Kwave::Compression::G711_ALAW; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_IMA_ADPCM: compression = Kwave::Compression::MS_ADPCM; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_U8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 8; break; case AFMT_S16_LE: case AFMT_S16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 16; break; case AFMT_S8: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 8; break; case AFMT_U16_LE: case AFMT_U16_BE: compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Unsigned; bits = 16; break; case AFMT_MPEG: compression = static_cast( Kwave::Compression::MPEG_LAYER_II); sample_format = Kwave::SampleFormat::Signed; bits = 16; break; #if 0 case AFMT_AC3: /* Dolby Digital AC3 */ compression = Kwave::SampleFormat::Unknown; sample_format = 0; bits = 16; break; #endif case AFMT_S24_LE: case AFMT_S24_BE: if (m_oss_version >= 0x040000) { compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 24; break; - } + } /* FALLTHROUGH */ case AFMT_S32_LE: case AFMT_S32_BE: if (m_oss_version >= 0x040000) { compression = Kwave::Compression::NONE; sample_format = Kwave::SampleFormat::Signed; bits = 32; break; - } + } /* FALLTHROUGH */ default: compression = -1; sample_format = Kwave::SampleFormat::Unknown; bits = -1; } } //*************************************************************************** int Kwave::PlayBackOSS::openDevice(const QString &device) { int fd = m_handle; // qDebug("PlayBackOSS::openDevice(%s)", device.local8Bit().data()); if (!device.length()) return -1; if (fd <= 0) { // open the device in case it's not already open fd = ::open(device.toLocal8Bit(), O_WRONLY | O_NONBLOCK); if (fd <= 0) { qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)", DBG(device), errno, strerror(errno)); } else { // we use blocking mode ::fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK); // query OSS driver version m_oss_version = -1; #ifdef OSS_GETVERSION ioctl(fd, OSS_GETVERSION, &m_oss_version); #endif } } if (fd <= 0) { qWarning("PlayBackOSS::openDevice('%s') - failed, errno=%d (%s)", DBG(device), errno, strerror(errno)); } return fd; } //*************************************************************************** QList Kwave::PlayBackOSS::supportedBits(const QString &device) { QList bits; bits.clear(); int err = -1; int mask = AFMT_QUERY; int fd; // qDebug("PlayBackOSS::supportedBits(%s)", device.local8Bit().data()); fd = openDevice(device); if (fd >= 0) { err = ::ioctl(fd, SNDCTL_DSP_GETFMTS, &mask); if (err < 0) { qWarning("PlayBackOSS::supportedBits() - "\ "SNDCTL_DSP_GETFMTS failed, "\ "fd=%d, result=%d, error=%d (%s)", fd, err, errno, strerror(errno)); } } // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); if (err < 0) return bits; // mask out all modes that do not match the current compression const int compression = Kwave::Compression::NONE; for (unsigned int bit=0; bit < (sizeof(mask) << 3); bit++) { if (!(mask & (1 << bit))) continue; // format is supported, split into compression, bits, sample format int c, b; Kwave::SampleFormat::Format s; format2mode(1 << bit, c, b, s); if (b < 0) continue; // unknown -> skip // take the mode if compression matches and it is not already known if ((c == compression) && !(bits.contains(b))) { bits += b; } } return bits; } //*************************************************************************** int Kwave::PlayBackOSS::detectChannels(const QString &device, unsigned int &min, unsigned int &max) { int fd, t, err = -1; min = 0; max = 0; fd = openDevice(device); if (fd < 0) return -1; // find the smalles number of tracks, limit to MAX_CHANNELS for (t=1; t < MAX_CHANNELS; t++) { int real_tracks = t; err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks); Q_ASSERT(real_tracks == t); if (err >= 0) { min = real_tracks; break; } } if (t >= MAX_CHANNELS) { // no minimum track number found :-o qWarning("no minimum track number found, err=%d", err); // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); return err; } // find the highest number of tracks, start from MAX_CHANNELS downwards for (t = MAX_CHANNELS; t >= Kwave::toInt(min); t--) { int real_tracks = t; err = ioctl(fd, SNDCTL_DSP_CHANNELS, &real_tracks); Q_ASSERT(real_tracks == t); if (err >= 0) { max = real_tracks; break; } } max = t; // qDebug("PlayBackOSS::detectTracks, min=%u, max=%u", min, max); // close the device if *we* opened it if ((fd != m_handle) && (fd >= 0)) ::close(fd); return 0; } #endif /* HAVE_OSS_SUPPORT */ //*************************************************************************** //***************************************************************************