This one just adds our own files to
src/gui/editors/pitchtracker/
since it doesn't touch any of your own stuff, I don't expect any
problems -- but if you notice any violations of the style guide, or
typical rosegarden architecture, of course let me know.
Cheers,
- Graham
Index: src/gui/editors/pitchtracker/PitchHistory.h
===================================================================
--- src/gui/editors/pitchtracker/PitchHistory.h (revision 0)
+++ src/gui/editors/pitchtracker/PitchHistory.h (revision 0)
@@ -0,0 +1,63 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PITCH_HISTORY_H
+#define _RG_PITCH_HISTORY_H
+
+#include "QList"
+
+/**
+ * \addtogroup Codicil
+ * \@{
+ * \brief Structure of vectors representing the performance's pitch history
+ *
+ * This is part of the Glasgow Center for Music Technology's
+ * "Rosegarden Codicil" project.
+ * http://www.n-ism.org/Projects/microtonalism.php
+ *
+ * The History structure will be created and maintained by
+ * the PitchTrackerView. A reference is passed to the
+ * PitchTrackerWidget which uses it to draw the graph.
+ *
+ * \author Nick Bailey n...@n-ism.org
+ * \date Apr 2010
+ */
+
+#include "base/Event.h"
+
+namespace Rosegarden {
+
+struct RealTime;
+
+class PitchHistory {
+ public:
+ void clear(void);
+
+ QList<double> m_detectFreqs;
+ QList<double> m_detectErrorsCents;
+ QList<bool> m_detectErrorsValid;
+ QList<timeT> m_detectTimes;
+ QList<RealTime> m_detectRealTimes;
+
+ QList<double> m_targetFreqs;
+ QList<RealTime> m_targetChangeTimes;
+};
+/**\@}*/
+
+}
+
+#endif
\ No newline at end of file
Index: src/gui/editors/pitchtracker/PitchTrackerView.cpp
===================================================================
--- src/gui/editors/pitchtracker/PitchTrackerView.cpp (revision 0)
+++ src/gui/editors/pitchtracker/PitchTrackerView.cpp (revision 0)
@@ -0,0 +1,353 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PitchTrackerView.h"
+#include "PitchGraphWidget.h"
+#include "PitchHistory.h"
+
+#include "sound/JackCaptureClient.h"
+#include "sound/PitchDetector.h"
+
+// Need to find actions for <<< and >>> transport buttons
+#include "gui/general/ActionFileClient.h"
+// Might need the default pitch analysis frame size and overlap
+#include "gui/configuration/PitchTrackerConfigurationPage.h"
+#include "misc/ConfigGroups.h"
+// to add PitchGraphWidget to display
+#include "gui/editors/notation/NotationWidget.h"
+// to get the notes in the composition
+#include "gui/editors/notation/NotationScene.h"
+#include "gui/editors/notation/NotationStaff.h"
+#include "document/RosegardenDocument.h"
+#include "base/ViewSegment.h"
+#include "base/RealTime.h"
+
+#include <QMessageBox>
+#include <QSettings>
+#include <QWidget>
+#include <QAction>
+#include <QToolBar>
+#include <QToolButton>
+
+
+namespace Rosegarden
+{
+
+
+/*************************************
+ CONSTRUCTOR, DESTRUCTOR, AND INIT
+ *************************************/
+PitchTrackerView::PitchTrackerView(RosegardenDocument *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent) :
+ NotationView(doc, segments, parent),
+ m_doc(doc),
+ m_jackCaptureClient(NULL),
+ m_jackConnected(false),
+ m_pitchDetector(NULL),
+ m_running(false)
+{
+ QSettings settings;
+ settings.beginGroup(PitchTrackerConfigGroup);
+ m_framesize = settings.value("framesize",
+ PitchDetector::defaultFrameSize).toInt();
+ m_stepsize = settings.value("stepsize",
+ PitchDetector::defaultStepSize).toInt();
+ // The frame size and step size are set from the default properties
+ // of PitchDetector or by reading the settings file. In principle,
+ // the latter method is open to attack by someone editing the settings
+ // file and entering some silly values to try and make the
+ // JackCaptureClient's memory allocation fail. So we should check that
+ // here and at least print a warning.
+ if (m_framesize > 65536 || m_framesize < 64 ||
+ m_stepsize > m_framesize || m_stepsize < m_framesize/16) {
+ std::cout << "PitchTrackerView: instantiation has these parameters: "
+ "Framesize: " << m_framesize <<
+ "Step size: " << m_stepsize <<
+ ". This seems rather unlikely; will continue anyway. "
+ "(fingers crossed!)" << std::endl;
+ }
+
+ // Find the current tuning index in use by the pitch tracker
+ int tuning = settings.value("tuning", 0).toInt();
+ // The static method Tuning::getTunings() returns a pointer to a
+ // std::vector of pointers to tunings. i.e.
+ // std::vector<Rosegarden::Accidentals::Tuning*>* getTunings(void);
+ // The one we want is obtained be dereferencing the return value of
+ // getTunings to obtain the std::vector, then indexing by the quantity
+ // retreived above. So the following looks nasty, but that's C++ for you.
+ const std::vector<Accidentals::Tuning*>* availableTunings =
+ Accidentals::Tuning::getTunings();
+ if (availableTunings) {
+ if (tuning < 0 ||
+ static_cast<unsigned int>(tuning) >= availableTunings->size()) {
+ // Illegal index into available tunings (how??) -> use default.
+ tuning = 0;
+ }
+ m_tuning = (*availableTunings)[tuning];
+ } else {
+ m_tuning = NULL;
+ std::cout << "WARNING: No available tunings!" << std::endl;
+ }
+
+ m_pitchGraphWidget = new PitchGraphWidget(m_history);
+ m_pitchGraphWidget->setTuning(m_tuning);
+ // m_notationWidget is owned by our parent, NotationView
+ m_notationWidget->addWidgetToBottom(m_pitchGraphWidget);
+
+ // jack capture client
+ m_jackCaptureClient =
+ new JackCaptureClient("Rosegarden PitchTracker",
+ m_framesize + m_stepsize);
+
+ if (m_jackCaptureClient->isConnected()) {
+ m_jackConnected = true;
+ } else {
+ QMessageBox::critical(this, "",
+ tr("Cannot connect to jack! "
+ "Ensure jack server is running and no other "
+ "tracker clients are open."));
+ return;
+ }
+
+ int sampleRate = m_jackCaptureClient->getSampleRate();
+
+ // pitch detector
+ int method = settings.value("method", 0).toInt();
+ if (method < 0 or method > PitchDetector::getMethods()->size()) method = 0;
+ PitchDetector::Method pdMethod = (*PitchDetector::getMethods())[method];
+ m_pitchDetector = new PitchDetector(m_framesize, m_stepsize, sampleRate);
+ m_pitchDetector->setMethod(pdMethod);
+
+ setSegments(doc, segments);
+}
+
+PitchTrackerView::~PitchTrackerView()
+{
+ if (m_pitchDetector) delete m_pitchDetector;
+ if (m_jackCaptureClient) delete m_jackCaptureClient;
+}
+
+void
+PitchTrackerView::setSegments(RosegardenDocument *document,
+ std::vector<Segment *> segments)
+{
+ // m_document is owned by our parent, NotationView
+ if (m_document) {
+ disconnect(m_document, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotPointerPositionChanged(timeT)));
+ }
+ m_document = document;
+
+ // update GUI
+
+ connect(m_document, SIGNAL(pointerPositionChanged(timeT)),
+ this, SLOT(slotUpdateValues(timeT)));
+
+ connect(this, SIGNAL( play() ),
+ this, SLOT( slotStartTracker() ));
+ connect(this, SIGNAL( stop() ),
+ this, SLOT( slotStopTracker() ));
+
+ // Any other jumping around in the score will invalidate the pitch
+ // graph, so let's just erase it and let it start again.
+
+ connect(this, SIGNAL( stepBackward() ),
+ this, SLOT( slotPlaybackJump() ));
+ connect(this, SIGNAL( stepForward() ),
+ this, SLOT( slotPlaybackJump() ));
+ connect(this, SIGNAL( rewindPlayback() ),
+ this, SLOT( slotPlaybackJump() ));
+ connect(this, SIGNAL( fastForwardPlayback() ),
+ this, SLOT( slotPlaybackJump() ));
+ connect(this, SIGNAL( rewindPlaybackToBeginning() ),
+ this, SLOT( slotPlaybackJump() ));
+ connect(this, SIGNAL( fastForwardPlaybackToEnd() ),
+ this, SLOT( slotPlaybackJump() ));
+
+}
+
+
+/*************************************
+ CONTROL REALTIME PROCESSING
+ *************************************/
+
+void
+PitchTrackerView::slotPlaybackJump()
+{
+ m_transport_posn_change = true;
+ std::cout << "PitchTrackerView: User changed playback posn\n";
+}
+
+void
+PitchTrackerView::slotStartTracker()
+{
+ // The play transport button (which is what gets us here)
+ // is really a play/pause button, so we need to toggle
+ // the widget's behaviour rather than just starting it.
+ if (m_running) {
+ slotStopTracker();
+ } else {
+ m_history.clear();
+ m_jackCaptureClient->startProcessing();
+ m_running = true;
+
+ NotationStaff *currentStaff =
+ m_notationWidget->getScene()->getCurrentStaff();
+ if (!currentStaff) return;
+
+ ViewSegment *vs = dynamic_cast<ViewSegment*>(currentStaff);
+ m_notes = vs->getViewElementList();
+ //m_notes_itr = m_notes->begin();
+ // The tracker doesn't necessarily start at the beginning of the piece
+ m_transport_posn_change = true;
+ }
+}
+
+void
+PitchTrackerView::slotStopTracker()
+{
+ m_running = false;
+ m_jackCaptureClient->stopProcessing();
+}
+
+
+
+/*************************************
+ REALTIME PROCESSING
+ *************************************/
+
+// Pitch history maintenance functions
+
+void
+PitchTrackerView::addNoteBoundary(double freq, RealTime time)
+{
+ m_history.m_targetFreqs.append(freq);
+ m_history.m_targetChangeTimes.append(time);
+ m_pitchGraphWidget->update();
+}
+
+// Eventually, we'd like to be able to export a PML representation
+// of the captured performance, so we maintain a QVector of score time
+// (time) as well as the real performance time (realTime). Only the
+// latter is used in the genertion of the pitch error graph, but the
+// former will be useful in associating the performance part of the
+// PML with the score part.
+void
+PitchTrackerView::addPitchTime(double freq, timeT time, RealTime realTime)
+{
+ m_history.m_detectFreqs.append(freq);
+ m_history.m_detectTimes.append(time);
+ m_history.m_detectRealTimes.append(realTime);
+
+ if (freq == PitchDetector::NONE || freq == PitchDetector::NOSIGNAL) {
+ m_history.m_detectErrorsCents.append(freq);
+ m_history.m_detectErrorsValid.append(false);
+ } else {
+ double targetFreq = 0;
+ if (m_history.m_targetFreqs.size() > 0) {
+ targetFreq = m_history.m_targetFreqs.last();
+ }
+ // TODO: isn't log(2) a constant defined in some header?
+ // maybe move this out of this file?
+ double cents = 1200.0 * log( freq / targetFreq ) / log (2);
+
+ m_history.m_detectErrorsCents.append(cents);
+ m_history.m_detectErrorsValid.append(true);
+ }
+#if DEBUG_PRINT_ALL
+ for (int i = 0; i < m_history.m_detectErrorsCents.size(); i++) {
+ std::cout << m_history.m_detectErrorsCents[i]<<" ";
+ }
+ std::cout << std::endl;
+#endif
+ m_pitchGraphWidget->update();
+}
+
+//!!! deliberate design choice: this only gets the last few samples;
+// any samples between the last GUI event and the current GUI
+// event are lost. (reduces processing)
+void
+PitchTrackerView::slotUpdateValues(timeT time)
+{
+ // Pitch tracker not running? then don't bother with any processing
+ if (!m_running) return;
+
+ // Find the nearest preceding or simultaneous event
+ // in the our element list
+ const ViewElementList::iterator score_event_itr =
+ m_notes->findNearestTime(time);
+
+ // Gracefully handle repositioning of the play cursor by the user
+ if (m_transport_posn_change) {
+ std::cout << "User changed transport position\n";
+ m_transport_posn_change = false;
+ m_history.clear();
+ // Always record a note boundary if the transport posn's changed.
+ // We'll find the previous Note event (not rest) because in then
+ // case the new current event is a rest, we ignore pitch parameters
+ // anyway. We just need m_notes_itr to be "wrong" to force the
+ // boundary to be recorded.
+ m_notes_itr =
+ m_notes->findPrevious(Note::EventType, score_event_itr);
+ //m_notes_itr = m_notes->findNearestTime(time);
+ }
+
+ const Event * const e = (*score_event_itr)->event();
+
+ // Past the end of the last note? Nothing further to do.
+ if (m_notes->findNext(Note::EventType, score_event_itr) == m_notes->end()
+ && time > e->getAbsoluteTime()+e->getDuration()) {
+ return;
+ }
+
+ const RealTime rt = m_doc->getComposition().getElapsedRealTime(time);
+
+ // See whether the current note has changed since we last looked
+ if (score_event_itr != m_notes_itr) {
+ // if so, record the current note and issue record the note boundary
+ std::cout << "***** PitchTrackerView: New " << e->getType()
+ << " at " << time << std::endl;
+ m_notes_itr = score_event_itr;
+ // We can only register a note if this event isn't a rest
+ // and there's a valid tuning
+ if (e->isa(Note::EventType) && m_tuning) {
+ const double noteFreq =
+ m_tuning->getFrequency(Pitch(*e));
+ addNoteBoundary(noteFreq, rt);
+ }
+ }
+
+ // Record the actual pitch data. Easy case first
+ if (e->isa(Note::EventRestType)) {
+ addPitchTime(PitchDetector::NONE, time, rt);
+
+ } else if (e->isa(Note::EventType)) {
+ if (m_jackCaptureClient->getFrame(m_pitchDetector->getInBuffer(),
+ m_pitchDetector->getBufferSize())) {
+ const double freq = m_pitchDetector->getPitch();
+ addPitchTime(freq, time, rt);
+ }
+
+ } else {
+ std::cout << "PitchTrackerView: ummm, what's a \""
+ << e->getType() << "\"EventType?\n";
+ }
+}
+} // Rosegarden namespace
+#include "PitchTrackerView.moc"
+
Index: src/gui/editors/pitchtracker/PitchGraphWidget.h
===================================================================
--- src/gui/editors/pitchtracker/PitchGraphWidget.h (revision 0)
+++ src/gui/editors/pitchtracker/PitchGraphWidget.h (revision 0)
@@ -0,0 +1,71 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PITCH_GRAPH_WIDGET_H
+#define _RG_PITCH_GRAPH_WIDGET_H
+
+#include "PitchHistory.h"
+
+#include "base/Event.h"
+
+#include <QWidget>
+#include <QList>
+
+namespace Rosegarden {
+
+namespace Accidentals { class Tuning; }
+
+struct RealTime;
+
+/**
+ * \addtogroup Codicil
+ * \@{
+ * \brief Graphical display of pitch tracker results
+ *
+ * This is part of the Glasgow Center for Music Technology's
+ * "Rosegarden Codicil" project.
+ * http://www.n-ism.org/Projects/microtonalism.php
+ *
+ * \author Graham Percival
+ * \date 2009
+ */
+class PitchGraphWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ PitchGraphWidget(PitchHistory &history, QWidget *parent = 0);
+ ~PitchGraphWidget();
+
+ void setTuning(Accidentals::Tuning* tuning);
+
+protected:
+ void paintEvent(QPaintEvent *event);
+
+ unsigned int m_graphHeight; // Height of graph (in cents)
+ unsigned int m_graphWidth; // Width of graph (in milliseconds)
+
+ Accidentals::Tuning* m_tuning; // Tuning in use in this widget
+ PitchHistory& m_history; // structure of data to plot
+};
+
+} // End namespace Rosegarden
+
+/**\@}*/
+
+#endif
+
Index: src/gui/editors/pitchtracker/PitchTrackerView.h
===================================================================
--- src/gui/editors/pitchtracker/PitchTrackerView.h (revision 0)
+++ src/gui/editors/pitchtracker/PitchTrackerView.h (revision 0)
@@ -0,0 +1,119 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#ifndef _RG_PITCH_TRACKER_VIEW_H
+#define _RG_PITCH_TRACKER_VIEW_H
+
+#include "PitchHistory.h"
+
+#include "gui/editors/notation/NotationView.h"
+#include "base/ViewSegment.h"
+
+namespace Rosegarden {
+
+
+class JackCaptureClient;
+class PitchDetector;
+class PitchGraphWidget;
+class RosegardenDocument;
+
+namespace Accidentals { class Tuning; }
+
+
+/**
+ * \addtogroup Codicil
+ * \@{
+ * \brief Monophonic pitch tracker window (subclass of NotationView)
+ *
+ * This is part of the Glasgow Center for Music Technology's
+ * "Rosegarden Codicil" project.
+ * http://www.n-ism.org/Projects/microtonalism.php
+ *
+ * \author Graham Percival
+ * \date 2009
+ */
+class PitchTrackerView : public NotationView
+{
+ Q_OBJECT
+
+public:
+ // basic Rosegarden infrastructure
+ PitchTrackerView(RosegardenDocument *doc,
+ std::vector<Segment *> segments,
+ QWidget *parent = 0);
+ ~PitchTrackerView();
+
+ void setSegments(RosegardenDocument *document,
+ std::vector<Segment *> segments);
+
+ bool getJackConnected() {
+ return m_jackConnected;
+ }
+
+protected slots:
+ // updates from GUI moving the vertical position line
+ void slotUpdateValues(timeT time);
+ // connected to start/stop Transport actions
+ void slotStartTracker();
+ void slotStopTracker();
+ /** Ensure correct graphing state if user moves cursor during playback */
+ void slotPlaybackJump();
+
+protected:
+ /** Record new note (history maintenance utility) */
+ void addNoteBoundary(double freq, RealTime time);
+ /** Record new pitch data (history maintenance utility) */
+ void addPitchTime(double freq, timeT time, RealTime realTime);
+
+ // doc for real-time/score-time conversion
+ RosegardenDocument *m_doc;
+ // get audio
+ JackCaptureClient *m_jackCaptureClient;
+ bool m_jackConnected;
+ // get pitch from audio
+ PitchDetector *m_pitchDetector;
+ // display pitch errors
+ PitchGraphWidget *m_pitchGraphWidget;
+
+ bool m_running;
+
+ // Pitch tracker Parameters
+ int m_framesize;
+ int m_stepsize;
+
+ // notes in the Composition/Document -- what note should we be singing?
+ ViewElementList *m_notes;
+ ViewElementList::iterator m_notes_itr;
+
+ // Tuning standard in use by this View
+ Accidentals::Tuning *m_tuning;
+
+private:
+ // Used to resync note iterator after user ff/rwind
+ bool m_transport_posn_change;
+
+ // Lists of detected and target frequencies
+ // These lists are maintained here and used by the Widget
+ PitchHistory m_history;
+};
+
+}
+
+/**\@}*/
+
+#endif
+
Index: src/gui/editors/pitchtracker/PitchHistory.cpp
===================================================================
--- src/gui/editors/pitchtracker/PitchHistory.cpp (revision 0)
+++ src/gui/editors/pitchtracker/PitchHistory.cpp (revision 0)
@@ -0,0 +1,36 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PitchHistory.h"
+
+namespace Rosegarden {
+
+// Clear all member lists
+void
+PitchHistory::clear(void)
+{
+ m_detectTimes.clear();
+ m_detectRealTimes.clear();
+ m_detectFreqs.clear();
+ m_detectErrorsCents.clear();
+ m_detectErrorsValid.clear();
+ m_targetFreqs.clear();
+ m_targetChangeTimes.clear();
+}
+
+
+} // namespace Rosegarden
Index: src/gui/editors/pitchtracker/PitchGraphWidget.cpp
===================================================================
--- src/gui/editors/pitchtracker/PitchGraphWidget.cpp (revision 0)
+++ src/gui/editors/pitchtracker/PitchGraphWidget.cpp (revision 0)
@@ -0,0 +1,246 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+ Copyright 2000-2009 the Rosegarden development team.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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. See the file
+ COPYING included with this distribution for more information.
+*/
+
+#include "PitchGraphWidget.h"
+#include "misc/ConfigGroups.h"
+#include "gui/configuration/PitchTrackerConfigurationPage.h"
+#include "sound/PitchDetector.h"
+
+// Access to the currently used tuning class
+#include "sound/Tuning.h"
+
+#include <QVBoxLayout>
+#include <QPainter>
+#include <QLabel>
+#include <QSettings>
+#include <QPoint>
+
+// maybe move this out of this file?
+#include <math.h>
+
+#define DEBUG_PRINT_ALL 0
+
+//using namespace Rosegarden;
+namespace Rosegarden {
+
+PitchGraphWidget::PitchGraphWidget(PitchHistory &history, QWidget *parent) :
+ QWidget(parent),
+ m_tuning(NULL),
+ m_history(history)
+{
+ setMinimumHeight(100);
+ setMinimumWidth(100);
+
+ // Read this widget's current settings
+ QSettings settings;
+ settings.beginGroup(PitchTrackerConfigGroup);
+ m_graphHeight =
+ settings.value("graphheight",
+ PitchTrackerConfigurationPage::defaultGraphHeight).
+ toInt();
+ m_graphWidth =
+ settings.value("graphwidth",
+ PitchTrackerConfigurationPage::defaultGraphWidth).
+ toInt();
+
+ qDebug("******************** end pitchgraphwidget ctor");
+}
+
+PitchGraphWidget::~PitchGraphWidget()
+{
+}
+
+void
+PitchGraphWidget::setTuning(Accidentals::Tuning* tuning)
+{
+ m_tuning = tuning;
+ // If the tuning gets changed via a settings update,
+ // we'll need to redraw the graph too.
+}
+
+void
+PitchGraphWidget::paintEvent(QPaintEvent *event)
+{
+ const QColor defaultColor(Qt::white); // for axes, text etc.
+ const QColor noNoteColor(Qt::gray);
+ const QColor noInputColor(Qt::red);
+ const QColor normalPlotColor(Qt::green);
+ const QColor noteBoundaryColor(Qt::blue);
+
+ QPainter painter(this);
+ QString labelStg;
+ QColor labelColor;
+
+ double targetFreq = 0;
+ bool targetValid = false;
+ double freq_err_cents = 0;
+ double freq_actual = 0;
+ if (m_history.m_targetFreqs.size() > 0) {
+ targetFreq = m_history.m_targetFreqs.last();
+ }
+ if (!m_history.m_detectErrorsCents.isEmpty()) {
+ targetValid = m_history.m_detectErrorsValid.last();
+ freq_err_cents = m_history.m_detectErrorsCents.last();
+ freq_actual = m_history.m_detectFreqs.last();
+ }
+
+ // Draw the target frequency in green if it's defined,
+ // or grey if we're playing a rest right now.
+ if (freq_actual != PitchDetector::NONE) {
+ labelStg = QString::number(targetFreq, 'f', 3);
+ labelColor = normalPlotColor;
+ } else {
+ labelStg = tr("None (Rest)",
+ "No target frequency because no note is playing");
+ labelColor = noNoteColor;
+ }
+ painter.setPen(defaultColor);
+ painter.drawText(0, 20, "Target freq:");
+ painter.setPen(labelColor);
+ painter.drawText(100, 20, labelStg);
+
+ // Same for the actual frequency, but must take the "error" conditions
+ // into account. If actual freq is -ve, could be one of NOSIGNAL
+ // or NONE (playing a rest) as defined in PitchDetector.
+ if (targetValid) {
+ labelStg = QString::number(freq_actual, 'f', 3);
+ labelColor = normalPlotColor;
+ } else {
+ labelStg = tr("Undefined");
+ if (freq_actual == PitchDetector::NONE) {
+ labelColor = noNoteColor;
+ }
+ else if (freq_actual == PitchDetector::NOSIGNAL) {
+ labelColor = noInputColor;
+ }
+ }
+ painter.setPen(defaultColor);
+ painter.drawText(0, 40, tr("Tuning System:"));
+ painter.drawText(0, 70, tr("Actual freq:"));
+ painter.drawText(0, 90, tr("Error (cents):"));
+ painter.setPen(labelColor);
+ // If a valid tuning's defined, use its name. Otherwise behave well.
+ QString tuningName = m_tuning ? QString(m_tuning->getName().c_str()) :
+ tr("None available (check preferences)");
+ painter.drawText(100, 40, tuningName);
+ painter.drawText(100, 70, labelStg);
+ if (targetValid) labelStg = QString::number(freq_err_cents, 'f', 3);
+ painter.drawText(100, 90, labelStg);
+ painter.setPen(defaultColor);
+
+ RealTime nextBoundary = RealTime::zeroTime;
+ QList<RealTime> note_stack = m_history.m_targetChangeTimes;
+ if (!note_stack.isEmpty()) {
+ // We're going to draw backwards from now, so the first
+ // boundary we're going to consider is the most recent one.
+ nextBoundary = note_stack.takeLast();
+ }
+
+ const int midY = height() / 2;
+ // central axis
+ painter.drawLine(0, midY, width(), midY);
+
+ const RealTime lastPointTime = m_history.m_detectRealTimes.isEmpty() ?
+ RealTime::zeroTime :
+ m_history.m_detectRealTimes.last();
+ // Record the state for drawing. If the ErrorsValid QVector is empty,
+ // or the first pitch error is invalid (e.g. there was no signal),
+ // start off in the first_err state. Otherwise begin in the first state.
+ enum {first, subsequent, first_err, subseq_err} drawState =
+ (m_history.m_detectErrorsValid.isEmpty() ||
+ !m_history.m_detectErrorsValid[0]) ?
+ first_err : first;
+
+ QPoint firstPoint;
+ // Start plotting at the most recent point and work backwards
+ bool lastPoint = false;
+ for (int i=m_history.m_detectErrorsCents.size() - 1;
+ i >= 0 && !lastPoint;
+ --i) {
+ const RealTime pointTime = m_history.m_detectRealTimes[i];
+ // Work out how far (in milliseconds) to the right of the graph
+ const double timeToLastPoint_msec =
+ 1000.0 * ((lastPointTime-pointTime)/RealTime(1, 0));
+ const int x = width() * (1.0 - timeToLastPoint_msec/m_graphWidth);
+ // if this is past the left end of the graph, it's the last
+ // line segment we'll bother with
+ lastPoint = (timeToLastPoint_msec > m_graphWidth);
+ // draw note boundaries
+ painter.setPen(noteBoundaryColor);
+ // nextBoundary < zeroTime implies all boundarys have been delt with
+ if (nextBoundary >= RealTime::zeroTime && pointTime <= nextBoundary) {
+ painter.drawLine(x, 0, x, height());
+ if (!note_stack.isEmpty()) {
+ nextBoundary = note_stack.takeLast();
+ } else {
+ nextBoundary = RealTime(-1, 0);
+ }
+ }
+
+
+ // computer graphics: lower numbers -> higher on screen
+ // Trace should move in range +/- graphHeight
+ const int y = -height() *
+ (0.5*m_history.m_detectErrorsCents[i]/m_graphHeight);
+ const bool valid = m_history.m_detectErrorsValid[i];
+ QPoint here;
+ // draw pitches
+ switch (drawState) {
+ // start of a valid line
+ case first:
+ firstPoint = QPoint(x, midY+y);
+ drawState = valid ? subsequent : first_err;
+ break;
+ // continuation of a valid line
+ case subsequent:
+ here = QPoint(x, midY+y);
+ if (valid) {
+ painter.setPen(normalPlotColor);
+ painter.drawLine(firstPoint, here);
+ } else {
+ drawState = first_err;
+ }
+ firstPoint = here;
+ break;
+ // start of an invalid region
+ case first_err:
+ firstPoint = QPoint(x, midY);
+ drawState = valid ? first : subseq_err;
+ break;
+ // continuation of an invalid region
+ case subseq_err:
+ here = QPoint(x, midY);
+ if (!valid) {
+ if (m_history.m_detectErrorsCents[i]
+ == PitchDetector::NOSIGNAL) {
+ painter.setPen(noInputColor);
+ } else {
+ painter.setPen(noNoteColor);
+ }
+ painter.drawLine(firstPoint, here);
+ } else {
+ drawState = first;
+ }
+ firstPoint = here;
+ break;
+ }
+ }
+}
+
+}
+#include "PitchGraphWidget.moc"
+
------------------------------------------------------------------------------
Xperia(TM) PLAY
It's a major breakthrough. An authentic gaming
smartphone on the nation's most reliable network.
And it wants your games.
http://p.sf.net/sfu/verizon-sfdev
_______________________________________________
Rosegarden-devel mailing list
Rosegarden-devel@lists.sourceforge.net - use the link below to unsubscribe
https://lists.sourceforge.net/lists/listinfo/rosegarden-devel