Git commit d5e6b9abef86023bcb492bac3df68cba083976b3 by Thomas Eschenbacher. Committed on 19/09/2026 at 17:11. Pushed by eschenbacher into branch 'master'.
sonagram plugin: implemented loading from file + added commands for loading and saving M +3 -0 CHANGES M +64 -1 doc/en/index.docbook M +2 -1 kwave/FileContext.cpp M +6 -3 libkwave/Plugin.h M +1 -1 libkwave/modules/RateConverter.cpp M +207 -81 plugins/sonagram/SonagramPlugin.cpp M +24 -0 plugins/sonagram/SonagramPlugin.h M +62 -55 plugins/sonagram/SonagramWindow.cpp M +30 -4 plugins/sonagram/SonagramWindow.h https://invent.kde.org/multimedia/kwave/-/commit/d5e6b9abef86023bcb492bac3df68cba083976b3 diff --git a/CHANGES b/CHANGES index 2e58c652..fac87b64 100644 --- a/CHANGES +++ b/CHANGES @@ -57,6 +57,9 @@ - suppress notice about changed sample format when switching to A-Law/U-Law - improved error handling * stringenter plugin: keep a history with the previous 20 commands + * sonagram plugin: + - implemented loading from file + - added commands for loading and saving 24.12.0 [2024-11-09] diff --git a/doc/en/index.docbook b/doc/en/index.docbook index e32776d5..0c1295d1 100644 --- a/doc/en/index.docbook +++ b/doc/en/index.docbook @@ -6862,7 +6862,7 @@ </listitem> </varlistentry> <varlistentry> - <term><emphasis role="bold">&i18n-plugin_lbl_parameters;</emphasis></term> + <term><emphasis role="bold">&i18n-plugin_lbl_parameters; (default mode)</emphasis></term> <listitem> <variablelist> <varlistentry> @@ -6963,6 +6963,69 @@ </variablelist> </listitem> </varlistentry> + + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_parameters; (load from file mode)</emphasis></term> + <listitem> + <variablelist> + <varlistentry> + <term><literal>load</literal></term> + <listitem> + <para> + If the first parameter is the word "<literal>load</literal>" + then the following parameter will be interpreted as a file name + for loading. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><replaceable>filename</replaceable></term> + <listitem> + <para> + Name of the file to load. + </para> + </listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + + <varlistentry> + <term><emphasis role="bold">&i18n-plugin_lbl_parameters; (save to file mode)</emphasis></term> + <listitem> + <variablelist> + <varlistentry> + <term><literal>save</literal></term> + <listitem> + <para> + If the first parameter is the word "<literal>save</literal>" + then the following parameters will be interpreted as an + index of the sonagram and a file name for saving. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><replaceable>index</replaceable></term> + <listitem> + <para> + The numeric index of the sonagram window that shows the sonagram to be saved. + This is an index which is determined from the internal list of currently + open sonagram windows, starting from zero. + </para> + </listitem> + </varlistentry> + <varlistentry> + <term><replaceable>filename</replaceable></term> + <listitem> + <para> + Name of the file to load. + </para> + </listitem> + </varlistentry> + </variablelist> + </listitem> + </varlistentry> + </variablelist> </sect1> diff --git a/kwave/FileContext.cpp b/kwave/FileContext.cpp index d36af6e5..b6062671 100644 --- a/kwave/FileContext.cpp +++ b/kwave/FileContext.cpp @@ -119,7 +119,8 @@ void Kwave::FileContext::use() //*************************************************************************** void Kwave::FileContext::release() { - Q_ASSERT(int(m_use_count) > 0); + Q_ASSERT(int(m_use_count) >= 0); + if (int(m_use_count) == 0) return; if (m_use_count.deref() == false) { disconnect(); deleteLater(); diff --git a/libkwave/Plugin.h b/libkwave/Plugin.h index 56ee842e..2adb57bf 100644 --- a/libkwave/Plugin.h +++ b/libkwave/Plugin.h @@ -258,9 +258,6 @@ namespace Kwave */ int execute(QStringList ¶ms); - /** emits a sigCommand() */ - void emitCommand(const QString &command); - /** increments the usage counter */ void use(); @@ -329,6 +326,12 @@ namespace Kwave /** decrements the usage counter */ void release(); + protected slots: + + /** emits a sigCommand() */ + void emitCommand(const QString &command); + + private slots: /** closes the progress dialog and the confirm/cancel proxy */ diff --git a/libkwave/modules/RateConverter.cpp b/libkwave/modules/RateConverter.cpp index 2ccd1401..233268b4 100644 --- a/libkwave/modules/RateConverter.cpp +++ b/libkwave/modules/RateConverter.cpp @@ -50,7 +50,7 @@ void Kwave::RateConverter::goOn() void Kwave::RateConverter::input(Kwave::SampleArray &data) { // shortcut for ratio == 1:1 - if (qFuzzyCompare(m_ratio, 0.0) || qFuzzyCompare(m_ratio, 1.0) || + if (qFuzzyIsNull(m_ratio) || qFuzzyCompare(m_ratio, 1.0) || data.isEmpty()) { output(data); return; diff --git a/plugins/sonagram/SonagramPlugin.cpp b/plugins/sonagram/SonagramPlugin.cpp index c2bdb5cc..d6206ec6 100644 --- a/plugins/sonagram/SonagramPlugin.cpp +++ b/plugins/sonagram/SonagramPlugin.cpp @@ -19,6 +19,7 @@ #include <errno.h> #include <math.h> +#include <qnumeric.h> #include <stdlib.h> #include <limits> @@ -62,6 +63,7 @@ KWAVE_PLUGIN(sonagram, SonagramPlugin) Kwave::SonagramPlugin::SonagramPlugin(QObject *parent, const QVariantList &args) :Kwave::Plugin(parent, args), + m_mode(MODE_VIEW), m_sonagram_window(nullptr), m_selection(nullptr), m_slices(0), m_fft_points(0), @@ -130,29 +132,60 @@ int Kwave::SonagramPlugin::interpreteParameters(QStringList ¶ms) QString param; // evaluate the parameter list - if (params.count() != 5) return -EINVAL; - - param = params[0]; - m_fft_points = param.toUInt(&ok); - if (!ok) return -EINVAL; - if (m_fft_points > MAX_FFT_POINTS) m_fft_points = MAX_FFT_POINTS; - - param = params[1]; - m_window_type = Kwave::WindowFunction::findFromName(param); - - param = params[2]; - m_color = (param.toUInt(&ok) != 0); - if (!ok) return -EINVAL; - - param = params[3]; - m_track_changes = (param.toUInt(&ok) != 0); - if (!ok) return -EINVAL; - - param = params[4]; - m_follow_selection = (param.toUInt(&ok) != 0); - if (!ok) return -EINVAL; + // we have different modes: + // 1) <fft points>, <window type>, <color>, + // <track changes>, <follow_selection>, + // ["window"|"view"] + // 2) "load", <filename>, ["window"|"view"] + // 3) "save", <filename> + + if ((params.count() >= 2) && (params[0] == _("load"))) { + // determine the last used window function + QStringList last_params = manager().defaultParams(name()); + param = last_params[1]; + m_window_type = Kwave::WindowFunction::findFromName(param); + + m_mode = MODE_LOAD; + m_fft_points = 0; + m_color = false; + m_track_changes = false; + m_follow_selection = false; + return 0; + } else if ((params.count() >= 3) && (params[0] == _("save"))) { + // nothing to do here, saving is done in start() + m_mode = MODE_SAVE; + return 0; + } else if (params.count() >= 5) { + m_mode = MODE_VIEW; + + param = params[0]; + m_fft_points = param.toUInt(&ok); + if (!ok) return -EINVAL; + if (m_fft_points > MAX_FFT_POINTS) m_fft_points = MAX_FFT_POINTS; + + param = params[1]; + m_window_type = Kwave::WindowFunction::findFromName(param); + + param = params[2]; + m_color = (param.toUInt(&ok) != 0); + if (!ok) return -EINVAL; + + param = params[3]; + m_track_changes = (param.toUInt(&ok) != 0); + if (!ok) return -EINVAL; + + param = params[4]; + m_follow_selection = (param.toUInt(&ok) != 0); + if (!ok) return -EINVAL; + + return 0; + } else { + qWarning("SonagramPlugin::interpreteParameters(): " + "invalid parameter list: %s", + DBG(params.join(_(", ")))); + } - return 0; + return -EINVAL; } //*************************************************************************** @@ -174,72 +207,95 @@ int Kwave::SonagramPlugin::start(QStringList ¶ms) // create an empty sonagram window m_sonagram_window = new(std::nothrow) - Kwave::SonagramWindow(parentWidget(), signalName()); + Kwave::SonagramWindow(parentWidget()); Q_ASSERT(m_sonagram_window); if (!m_sonagram_window) return -ENOMEM; + if (m_mode == MODE_LOAD) { + m_sonagram_window->setWindowTitle( + i18n("Sonagram loaded from %1", params[1])); + } else { + m_sonagram_window->setName(signalName()); + } // if the signal closes, close the sonagram window too QObject::connect(&manager(), SIGNAL(sigClosed()), m_sonagram_window, SLOT(close())); - // get the current selection - QVector<unsigned int> selected_channels; - sample_index_t offset = 0; - sample_index_t length = 0; - length = selection(&selected_channels, &offset, nullptr, true); - - // abort if nothing is selected - if (!length || selected_channels.isEmpty()) - return -EINVAL; - - // calculate the number of slices (width of image) - m_slices = Kwave::toUint(ceil(static_cast<double>(length) / - static_cast<double>(m_fft_points))); - if (m_slices > MAX_SLICES) m_slices = MAX_SLICES; - - /* limit selection to INT_MAX samples (limitation of the cache index) */ - if ((length / m_fft_points) >= SAMPLE_INDEX_MAX) { - Kwave::MessageBox::error(parentWidget(), - i18n("File or selection too large")); - return -EFBIG; - } + // forward the (menu) commands to the plugin + QObject::connect(m_sonagram_window, SIGNAL(sigCommand(QString)), + this, SLOT(emitCommand(QString))); + + if (m_mode == MODE_VIEW) { + // get the current selection + QVector<unsigned int> selected_channels; + sample_index_t offset = 0; + sample_index_t length = 0; + length = selection(&selected_channels, &offset, nullptr, true); + + // abort if nothing is selected + if (!length || selected_channels.isEmpty()) + return -EINVAL; + + // calculate the number of slices (width of image) + m_slices = Kwave::toUint(ceil(static_cast<double>(length) / + static_cast<double>(m_fft_points))); + if (m_slices > MAX_SLICES) m_slices = MAX_SLICES; + + /* limit selection to INT_MAX samples (limitation of the cache index) */ + if ((length / m_fft_points) >= SAMPLE_INDEX_MAX) { + Kwave::MessageBox::error(parentWidget(), + i18n("File or selection too large")); + return -EFBIG; + } - // create a selection tracker - m_selection = new(std::nothrow) Kwave::SelectionTracker( - &sig_mgr, offset, length, &selected_channels); - Q_ASSERT(m_selection); - if (!m_selection) return -ENOMEM; - - connect(m_selection, SIGNAL(sigTrackInserted(quint64)), - this, SLOT(slotTrackInserted(quint64))); - connect(m_selection, SIGNAL(sigTrackDeleted(quint64)), - this, SLOT(slotTrackDeleted(quint64))); - connect( - m_selection, - SIGNAL(sigInvalidated(quint64,sample_index_t,sample_index_t)), - this, - SLOT(slotInvalidated(quint64,sample_index_t,sample_index_t)) - ); - - // create a new empty image - createNewImage(m_slices, m_fft_points / 2); - - // set the overview - m_overview_cache = new(std::nothrow) - Kwave::OverViewCache(sig_mgr, offset, length, &selected_channels); - Q_ASSERT(m_overview_cache); - if (!m_overview_cache) return -ENOMEM; - - refreshOverview(); // <- this needs the m_overview_cache - - if (m_track_changes) { - // stay informed about changes in the signal - connect(m_overview_cache, SIGNAL(changed()), - this, SLOT(refreshOverview())); - } else { - // overview cache is no longer needed - delete m_overview_cache; - m_overview_cache = nullptr; + // create a selection tracker + m_selection = new(std::nothrow) Kwave::SelectionTracker( + &sig_mgr, offset, length, &selected_channels); + Q_ASSERT(m_selection); + if (!m_selection) return -ENOMEM; + + connect(m_selection, SIGNAL(sigTrackInserted(quint64)), + this, SLOT(slotTrackInserted(quint64))); + connect(m_selection, SIGNAL(sigTrackDeleted(quint64)), + this, SLOT(slotTrackDeleted(quint64))); + connect( + m_selection, + SIGNAL(sigInvalidated(quint64,sample_index_t,sample_index_t)), + this, + SLOT(slotInvalidated(quint64,sample_index_t,sample_index_t)) + ); + + // create a new empty image + createNewImage(m_slices, m_fft_points / 2); + + // set the overview + m_overview_cache = new(std::nothrow) + Kwave::OverViewCache(sig_mgr, offset, length, &selected_channels); + Q_ASSERT(m_overview_cache); + if (!m_overview_cache) return -ENOMEM; + + refreshOverview(); // <- this needs the m_overview_cache + + if (m_track_changes && (m_mode == MODE_VIEW)) { + // stay informed about changes in the signal + connect(m_overview_cache, SIGNAL(changed()), + this, SLOT(refreshOverview())); + } else { + // overview cache is no longer needed + delete m_overview_cache; + m_overview_cache = nullptr; + } + } else if ((m_mode == MODE_LOAD) && (params.count() >= 2)) { + // load the sonagram from a file + result = loadFromFile(params[1]); + if (result) return result; + } else if ((m_mode == MODE_SAVE) && (params.count() >= 3)) { + // save the sonagram to a file + bool ok = false; + quint64 id = params[1].toULongLong(&ok); + if (!ok) return -EINVAL; + result = saveToFile(id, params[2]); + return result; } // connect all needed signals @@ -254,7 +310,7 @@ int Kwave::SonagramPlugin::start(QStringList ¶ms) m_sonagram_window->setRate(signalRate()); m_sonagram_window->show(); - if (m_track_changes) { + if (m_track_changes && (m_mode == MODE_VIEW)) { QObject::connect(static_cast<QObject*>(&(manager())), SIGNAL(sigSignalNameChanged(QString)), m_sonagram_window, SLOT(setName(QString))); @@ -618,6 +674,76 @@ void Kwave::SonagramPlugin::windowDestroyed() release(); } +//*************************************************************************** +int Kwave::SonagramPlugin::loadFromFile(const QString &filename) +{ + qDebug("loading %s", DBG(filename)); + if (filename.isEmpty()) return -EINVAL; + + QImage image(filename); + if (image.isNull()) return -ENOENT; + + m_color = !image.isGrayscale(); + m_slices = image.width(); + m_fft_points = image.height() * 2; + + // try to find out the sample rate + if (m_sonagram_window) { + // try to find it in file meta data + bool ok = false; + double rate = m_image.text(_("x-kwave/sample-rate")).toDouble(&ok); + if (!ok) { + // fallback #1: use dpi setting + if (m_image.dotsPerMeterX() == m_image.dotsPerMeterY()) { + rate = m_image.dotsPerMeterX(); + ok = !qFuzzyIsNull(rate) && (rate >= 8000.0); + } + } + if (!ok) { + // fallback #2: use current signal + Kwave::SignalManager &mgr = signalManager(); + rate = mgr.rate(); + ok = !qFuzzyIsNull(rate) && (rate >= 8000.0); + } + if (!ok) { + // fallback #3: assume some default rate + rate = 44100.0; + } + m_sonagram_window->setRate(rate); + } + + m_image = std::move(image); + return 0; +} + +//*************************************************************************** +int Kwave::SonagramPlugin::saveToFile(quint64 index, const QString &filename) +{ + qDebug("saving sonagram #%llu to %s", index, DBG(filename)); + if (filename.isEmpty()) return -EINVAL; + + // find the sonagram window with the given index + Kwave::SonagramWindow *win = Kwave::SonagramWindow::fromIndex(index); + if (win == nullptr) return -EINVAL; + + // get a copy of the image + QImage image(win->image()); + + // attach some meta data to the image for loading it later + double rate = win->rate(); + if (qFuzzyIsNull(rate) || (rate < 8000.0)) + rate = signalRate(); + if (!qFuzzyIsNull(rate) && (rate >= 8000.0)) { + // try to save as file meta data + image.setText(_("x-kwave/sample-rate"), QString::number(rate)); + // also save as dpi as fallback + image.setDotsPerMeterX(Kwave::toInt(rate)); + image.setDotsPerMeterY(Kwave::toInt(rate)); + } + + return (image.save(filename, "BMP")) ? 0 : -EIO; +} + //*************************************************************************** #include "SonagramPlugin.moc" //*************************************************************************** diff --git a/plugins/sonagram/SonagramPlugin.h b/plugins/sonagram/SonagramPlugin.h index 86b47240..b11c3d07 100644 --- a/plugins/sonagram/SonagramPlugin.h +++ b/plugins/sonagram/SonagramPlugin.h @@ -205,8 +205,32 @@ namespace Kwave void createNewImage(const unsigned int width, const unsigned int height); + /** + * Loads a sonagram from a file. The file format is currently + * a simple bitmap with 8 bits depth. + * @param filename name of the file to load + * @return 0 if ok, or an error code if failed + */ + int loadFromFile(const QString &filename); + + /** + * Saves the data of an opened sonagram window to a file. + * The file format is currently a simple bitmap with 8 + * bits depth. + * @param index the index of the window + * @param filename name of the file to save + * @return 0 if ok, or an error code if failed + */ + int saveToFile(quint64 index, const QString &filename); + private: + enum { + MODE_VIEW = 0, + MODE_LOAD, + MODE_SAVE + } m_mode; + /** the main view of the plugin, a SonagramWindow */ Kwave::SonagramWindow *m_sonagram_window; diff --git a/plugins/sonagram/SonagramWindow.cpp b/plugins/sonagram/SonagramWindow.cpp index a4cea7b9..425e7b1b 100644 --- a/plugins/sonagram/SonagramWindow.cpp +++ b/plugins/sonagram/SonagramWindow.cpp @@ -23,6 +23,7 @@ #include <QBitmap> #include <QImage> #include <QLabel> +#include <QList> #include <QMenuBar> #include <QPointer> #include <QLayout> @@ -80,8 +81,11 @@ static const char *background[] = { "..........##########" }; +/** global list with all sonagram windows */ +static QList<Kwave::SonagramWindow *> g_windows; + //**************************************************************************** -Kwave::SonagramWindow::SonagramWindow(QWidget *parent, const QString &name) +Kwave::SonagramWindow::SonagramWindow(QWidget *parent) :KMainWindow(parent), m_status_time(nullptr), m_status_freq(nullptr), @@ -96,7 +100,6 @@ Kwave::SonagramWindow::SonagramWindow(QWidget *parent, const QString &name) m_yscale(nullptr), m_refresh_timer() { - for (unsigned int i = 0; i < 256; ++i) { m_histogram[i] = 0; } QWidget *mainwidget = new(std::nothrow) QWidget(this); @@ -120,9 +123,11 @@ Kwave::SonagramWindow::SonagramWindow(QWidget *parent, const QString &name) Q_ASSERT(file); if (!file) return ; -// bar->addAction(i18n("&Spectral Data"), spectral); -// file->addAction(i18n("&Import from Bitmap..."), this, SLOT(load())); - + file->addAction( + QIcon::fromTheme(_("document-import")), + i18n("&Import from Bitmap..."), + this, SLOT(load()) + ); file->addAction( QIcon::fromTheme(_("document-export")), i18n("&Export to Bitmap..."), @@ -187,8 +192,6 @@ Kwave::SonagramWindow::SonagramWindow(QWidget *parent, const QString &name) connect(&m_refresh_timer, SIGNAL(timeout()), this, SLOT(refresh_view())); - setName(name); - top_layout->setRowStretch(0, 100); top_layout->setRowStretch(1, 0); top_layout->setRowStretch(2, 0); @@ -207,9 +210,28 @@ Kwave::SonagramWindow::SonagramWindow(QWidget *parent, const QString &name) if ((h * 5 / 3) < w) h = (w * 3) / 5; resize(w, h); + g_windows.push_back(this); show(); } +//**************************************************************************** +Kwave::SonagramWindow::~SonagramWindow() +{ + g_windows.removeAll(this); +} + +//**************************************************************************** +quint64 Kwave::SonagramWindow::index() const +{ + return g_windows.indexOf(this); +} + +//**************************************************************************** +Kwave::SonagramWindow *Kwave::SonagramWindow::fromIndex(quint64 index) +{ + return (qsizetype(index) < g_windows.size()) ? g_windows[index] : nullptr; +} + //**************************************************************************** void Kwave::SonagramWindow::close() { @@ -230,7 +252,11 @@ void Kwave::SonagramWindow::save() dlg->setWindowTitle(i18n("Save Sonagram")); if (dlg->exec() == QDialog::Accepted) { QString filename = dlg->selectedUrl().toLocalFile(); - if (!filename.isEmpty()) m_image.save(filename, "BMP"); + if (!filename.isEmpty()) { + emit sigCommand(_("plugin:execute(sonagram,save,") + + QString::number(index()) + _(",") + + filename + _(")")); + } } delete dlg; } @@ -238,42 +264,21 @@ void Kwave::SonagramWindow::save() //**************************************************************************** void Kwave::SonagramWindow::load() { -// if (image) { -// QString filename = QFileDialog::getOpenFileName(this, QString(), -// "", "*.bmp"); -// printf ("loading %s\n", filename.local8Bit().data()); -// if (!filename.isNull()) { -// printf ("loading %s\n", filename.local8Bit().data()); -// QImage *newimage = new QImage (filename); -// Q_ASSERT(newimage); -// if (newimage) { -// if ((image->height() == newimage->height()) -// && (image->width() == newimage->width())) { -// -// for (int i = 0; i < x; i++) { -// for (int j = 0; j < points / 2; j++) { -// if (data[i]) { -// // data[i][j].real; -// } -// -// } -// } -// -// delete image; -// image = newimage; -// view->setImage (image); -// } else { -// char buf[128]; -// delete newimage; -// snprintf(buf, sizeof(buf), i18n("Bitmap must be %dx%d"), -// image->width(), image->height()); -// KMsgBox::message (this, "Info", buf, 2); -// } -// } else -// KMsgBox::message (this, i18n("Error"), -// i18n("Could not open Bitmap"), 2); -// } -// } + QPointer<Kwave::FileDialog> dlg = new(std::nothrow) Kwave::FileDialog( + _("kfiledialog:///kwave_sonagram_import"), + Kwave::FileDialog::OpenFile, QString(), + this, QUrl(), _("*.bmp") + ); + if (!dlg) return; + dlg->setWindowTitle(i18n("Load Sonagram")); + if (dlg->exec() == QDialog::Accepted) { + QString filename = dlg->selectedUrl().toLocalFile(); + if (!filename.isEmpty()) { + emit sigCommand(_("plugin:execute(sonagram,load,") + + filename + _(")")); + } + } + delete dlg; } //**************************************************************************** @@ -473,14 +478,16 @@ void Kwave::SonagramWindow::toSignal() void Kwave::SonagramWindow::translatePixels2TF(const QPoint p, double *ms, double *f) { + if (qFuzzyIsNull(m_rate)) { + if (ms) *ms = 0.0; + if (f) *f = 0.0; + } + if (ms) { - // get the time coordinate [0...(N_samples-1)* (1/f_sample) ] - if (!qFuzzyIsNull(m_rate)) { - *ms = static_cast<double>(p.x()) * - static_cast<double>(m_points) * 1000.0 / m_rate; - } else { - *ms = 0; - } + // get the time coordinate [0...(N_samples-1) * (1/f_sample) ] + double t = static_cast<double>(p.x()) * + static_cast<double>(m_points) * 1000.0 / m_rate; + *ms = (!qIsNaN(t) && !qIsInf(t)) ? t : 0.0; } if (f) { @@ -488,15 +495,16 @@ void Kwave::SonagramWindow::translatePixels2TF(const QPoint p, double py = (m_points >= 2) ? (m_points / 2) - 1 : 0; double y = py - p.y(); if (y < 0) y = 0; - *f = y / py * (m_rate / 2.0); + double freq = !qFuzzyIsNull(py) ? (y / py * (m_rate / 2.0)) : 0.0; + *f = (!qIsNaN(freq) && !qIsInf(freq)) ? freq : 0.0; } } //*************************************************************************** void Kwave::SonagramWindow::updateScaleWidgets() { - double ms; - double f; + double ms = 0.0; + double f = 0.0; translatePixels2TF(QPoint(m_image.width() - 1, 0), &ms, &f); @@ -531,7 +539,6 @@ void Kwave::SonagramWindow::cursorPosChanged(const QPoint pos) QStatusBar *status = statusBar(); Q_ASSERT(status); Q_ASSERT(m_points); - Q_ASSERT(!qFuzzyIsNull(m_rate)); if (!status) return; if (m_image.isNull()) return; if (!m_points) return; diff --git a/plugins/sonagram/SonagramWindow.h b/plugins/sonagram/SonagramWindow.h index c12ae50d..71fd8442 100644 --- a/plugins/sonagram/SonagramWindow.h +++ b/plugins/sonagram/SonagramWindow.h @@ -49,15 +49,24 @@ namespace Kwave /** * Constructor. * @param parent the parent widget - * @param name reference to the initial name of the signal (used for - * setting the window title, might be an empty string) */ - SonagramWindow(QWidget *parent, const QString &name); + SonagramWindow(QWidget *parent); /** * Destructor. */ - ~SonagramWindow() override = default; + ~SonagramWindow() override; + + /** Get the index of this instance */ + quint64 index() const; + + /** + * Get the instance of a sonagram window from index + * @param index zero based index within the list of open + * sonagram windows + * @return the SonagramWindow corresponding to the index or nullptr + */ + static SonagramWindow *fromIndex(quint64 index); /** * Sets a new sonagram image to display. @@ -80,6 +89,23 @@ namespace Kwave */ void insertSlice(const unsigned int slice_nr, const QByteArray &slice); + /** + * Returns the sample rate. + * @return the sample rate in samples per second + */ + double rate() const { return m_rate; } + + /** + * Returns the image, needed for saving + * @return the current QImage + */ + const QImage &image() const { return m_image; } + + signals: + + /** forward a sigCommand to the next layer */ + void sigCommand(const QString &command); + public slots: /** closes the sonagram window */
