diff --git a/CMakeLists.txt b/CMakeLists.txt index 74d60dc..cdbd1cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ list(APPEND inspectrum_sources inputsource.cpp spectrogram.cpp spectrogramcontrols.cpp + waveformview.cpp ) INCLUDE(FindPkgConfig) diff --git a/mainwindow.cpp b/mainwindow.cpp index c66b29d..a5b2d33 100644 --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -32,6 +32,9 @@ MainWindow::MainWindow() dock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); addDockWidget(Qt::LeftDockWidgetArea, dock); + wave = new WaveformView(); + addDockWidget(Qt::BottomDockWidgetArea, wave); + connect(dock, SIGNAL(openFile(QString)), this, SLOT(openFile(QString))); connect(dock->sampleRate, SIGNAL(textChanged(QString)), this, SLOT(setSampleRate(QString))); connect(dock, SIGNAL(fftSizeChanged(int)), this, SLOT(setFFTSize(int))); @@ -78,6 +81,7 @@ void MainWindow::setFFTSize(int size) off_t sample = getCenterSample(); spectrogram.setFFTSize(size); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); + wave->viewChanged(getTopSample(), getBottomSample()); } void MainWindow::setZoomLevel(int zoom) @@ -85,6 +89,13 @@ void MainWindow::setZoomLevel(int zoom) off_t sample = getCenterSample(); spectrogram.setZoomLevel(zoom); scrollArea.verticalScrollBar()->setValue(getScrollPos(sample)); + wave->viewChanged(getTopSample(), getBottomSample()); +} + +off_t MainWindow::getTopSample() +{ + int height = scrollArea.height(); + return scrollArea.verticalScrollBar()->value() * spectrogram.getStride(); } off_t MainWindow::getCenterSample() @@ -93,6 +104,12 @@ off_t MainWindow::getCenterSample() return (scrollArea.verticalScrollBar()->value() + height / 2) * spectrogram.getStride(); } +off_t MainWindow::getBottomSample() +{ + int height = scrollArea.height(); + return (scrollArea.verticalScrollBar()->value() + height) * spectrogram.getStride(); +} + int MainWindow::getScrollPos(off_t sample) { int height = scrollArea.height(); @@ -104,4 +121,6 @@ void MainWindow::openFile(QString fileName) QString title="%1: %2"; this->setWindowTitle(title.arg(QApplication::applicationName(),fileName.section('/',-1,-1))); spectrogram.openFile(fileName); + wave->inputSourceChanged(spectrogram.inputSource); + wave->viewChanged(getTopSample(), getBottomSample()); } diff --git a/mainwindow.h b/mainwindow.h index bdc53a4..d2cecfb 100644 --- a/mainwindow.h +++ b/mainwindow.h @@ -23,6 +23,7 @@ #include #include "spectrogram.h" #include "spectrogramcontrols.h" +#include "waveformview.h" class MainWindow : public QMainWindow { @@ -45,7 +46,10 @@ private: QScrollArea scrollArea; Spectrogram spectrogram; SpectrogramControls *dock; + WaveformView *wave; + off_t getTopSample(); off_t getCenterSample(); + off_t getBottomSample(); int getScrollPos(off_t sample); }; diff --git a/spectrogram.h b/spectrogram.h index 91d2713..bc8bc4e 100644 --- a/spectrogram.h +++ b/spectrogram.h @@ -40,6 +40,7 @@ public: int getHeight(); int getStride(); + InputSource *inputSource = nullptr; public slots: void openFile(QString fileName); void setSampleRate(int rate); @@ -56,7 +57,6 @@ private: const int linesPerGraduation = 50; const int tileSize = 65536; // This must be a multiple of the maximum FFT size - InputSource *inputSource = nullptr; FFT *fft = nullptr; std::unique_ptr window; fftwf_complex *lineBuffer = nullptr; diff --git a/spectrogramcontrols.cpp b/spectrogramcontrols.cpp index 877ffc8..132d66d 100644 --- a/spectrogramcontrols.cpp +++ b/spectrogramcontrols.cpp @@ -42,7 +42,7 @@ SpectrogramControls::SpectrogramControls(const QString & title, QWidget * parent layout->addRow(new QLabel(tr("FFT size:")), fftSizeSlider); zoomLevelSlider = new QSlider(Qt::Horizontal, widget); - zoomLevelSlider->setRange(0, 5); + zoomLevelSlider->setRange(0, 10); zoomLevelSlider->setValue(0); layout->addRow(new QLabel(tr("Zoom:")), zoomLevelSlider); diff --git a/waveformview.cpp b/waveformview.cpp new file mode 100644 index 0000000..3be2e04 --- /dev/null +++ b/waveformview.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2015, Mike Walters + * + * This file is part of inspectrum. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "waveformview.h" +#include +#include + +WaveformView::WaveformView() +{ + for (int i = 0; i < 128; i++) { + colormap[i] = qRgb(i/2, i*1.5, i*1.5); + } + for (int i = 128; i < 256; i++) { + colormap[i] = qRgb(64 + (i-128)*1.5, 192+(i-128)/2, 192+(i-128)/2); + } +} + +void WaveformView::inputSourceChanged(InputSource *input) +{ + inputSource = input; + update(); +} + +void WaveformView::viewChanged(off_t firstSample, off_t lastSample) +{ + this->firstSample = firstSample; + this->lastSample = lastSample; + qDebug() << "viewChanged(" << firstSample << ", " << lastSample << ")"; + update(); +} + +void WaveformView::paintEvent(QPaintEvent *event) +{ + if (inputSource == nullptr) return; + if (lastSample - firstSample <= 0) return; + + QRect rect = QRect(0, 0, width(), height()); + QPainter painter(this); + painter.fillRect(rect, Qt::black); + + off_t length = lastSample - firstSample; + fftwf_complex *samples = new fftwf_complex[length]; + inputSource->getSamples(samples, firstSample, length); + for (int iq = 0; iq < 2; iq++) { + switch (iq) { + case 0: + painter.setPen(Qt::red); + break; + case 1: + painter.setPen(Qt::blue); + break; + } + int xprev = 0; + int yprev = 0; + for (off_t i = 0; i < length; i++) { + int x = (float)i / length * rect.width(); + int y = (samples[i][iq] * 50 * rect.height()/2) + rect.height()/2; + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x >= rect.width()-1) x = rect.width()-2; + if (y >= rect.height()-1) y = rect.height()-2; + + painter.drawLine(xprev, yprev, x, y); + xprev = x; + yprev = y; + } + } + + delete[] samples; +} \ No newline at end of file diff --git a/waveformview.h b/waveformview.h new file mode 100644 index 0000000..b91a215 --- /dev/null +++ b/waveformview.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2015, Mike Walters + * + * This file is part of inspectrum. + * + * 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 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include "inputsource.h" + +class WaveformView : public QDockWidget { + Q_OBJECT + +public: + WaveformView(); + +public slots: + void inputSourceChanged(InputSource *input); + void viewChanged(off_t firstSample, off_t lastSample); + +protected: + void paintEvent(QPaintEvent *event); + +private: + InputSource *inputSource = nullptr; + off_t firstSample = 0; + off_t lastSample = 0; + QRgb colormap[255]; +}; \ No newline at end of file