# HG changeset patch # User Chris Cannam # Date 1386004280 0 # Node ID d6bd5751b8f657fdca70735bea76f2760fa162e8 # Parent dba8a02b0413335ee34cf302aef7754daebd0b8d Add NoteExportable base class, use it in MIDI export (and also elsewhere in playback) diff -r dba8a02b0413 -r d6bd5751b8f6 data/fileio/MIDIFileWriter.cpp --- a/data/fileio/MIDIFileWriter.cpp Mon Dec 02 12:29:09 2013 +0000 +++ b/data/fileio/MIDIFileWriter.cpp Mon Dec 02 17:11:20 2013 +0000 @@ -23,8 +23,7 @@ #include "MIDIFileWriter.h" #include "data/midi/MIDIEvent.h" - -#include "model/NoteModel.h" +#include "model/NoteData.h" #include "base/Pitch.h" @@ -37,14 +36,13 @@ using namespace MIDIConstants; -MIDIFileWriter::MIDIFileWriter(QString path, NoteModel *model, float tempo) : +MIDIFileWriter::MIDIFileWriter(QString path, const NoteExportable *exportable, + int sampleRate, float tempo) : m_path(path), - m_model(model), - m_modelUsesHz(false), + m_exportable(exportable), + m_sampleRate(sampleRate), m_tempo(tempo) { - if (model->getScaleUnits().toLower() == "hz") m_modelUsesHz = true; - if (!convert()) { m_error = "Conversion from model to internal MIDI format failed"; } @@ -342,42 +340,28 @@ // Omit time signature - const NoteModel::PointList ¬es = - static_cast *>(m_model)->getPoints(); + NoteList notes = m_exportable->getNotes(); - for (NoteModel::PointList::const_iterator i = notes.begin(); - i != notes.end(); ++i) { + for (NoteList::const_iterator i = notes.begin(); i != notes.end(); ++i) { - long frame = i->frame; - float value = i->value; + size_t frame = i->start; size_t duration = i->duration; - - int pitch; - - if (m_modelUsesHz) { - pitch = Pitch::getPitchForFrequency(value); - } else { - pitch = lrintf(value); - } + int pitch = i->midiPitch; + int velocity = i->velocity; if (pitch < 0) pitch = 0; if (pitch > 127) pitch = 127; // Convert frame to MIDI time - double seconds = double(frame) / double(m_model->getSampleRate()); + double seconds = double(frame) / double(m_sampleRate); double quarters = (seconds * m_tempo) / 60.0; - unsigned long midiTime = lrint(quarters * m_timingDivision); - - int velocity = 100; - if (i->level > 0.f && i->level <= 1.f) { - velocity = lrintf(i->level * 127.f); - } + unsigned long midiTime = int(quarters * m_timingDivision + 0.5); // Get the sounding time for the matching NOTE_OFF - seconds = double(frame + duration) / double(m_model->getSampleRate()); + seconds = double(frame + duration) / double(m_sampleRate); quarters = (seconds * m_tempo) / 60.0; - unsigned long endTime = lrint(quarters * m_timingDivision); + unsigned long endTime = int(quarters * m_timingDivision + 0.5); // At this point all the notes we insert have absolute times // in the delta time fields. We resolve these into delta diff -r dba8a02b0413 -r d6bd5751b8f6 data/fileio/MIDIFileWriter.h --- a/data/fileio/MIDIFileWriter.h Mon Dec 02 12:29:09 2013 +0000 +++ b/data/fileio/MIDIFileWriter.h Mon Dec 02 17:11:20 2013 +0000 @@ -32,7 +32,7 @@ #include class MIDIEvent; -class NoteModel; +class NoteExportable; /** * Write a MIDI file. This includes file write code for generic @@ -43,7 +43,10 @@ class MIDIFileWriter { public: - MIDIFileWriter(QString path, NoteModel *model, float tempo = 120.f); + MIDIFileWriter(QString path, + const NoteExportable *exportable, + int sampleRate, // used to convert exportable sample timings + float tempo = 120.f); virtual ~MIDIFileWriter(); virtual bool isOK() const; @@ -74,18 +77,18 @@ bool convert(); - QString m_path; - NoteModel *m_model; - bool m_modelUsesHz; - float m_tempo; - int m_timingDivision; // pulses per quarter note - MIDIFileFormatType m_format; - unsigned int m_numberOfTracks; + QString m_path; + const NoteExportable *m_exportable; + int m_sampleRate; + float m_tempo; + int m_timingDivision; // pulses per quarter note + MIDIFileFormatType m_format; + unsigned int m_numberOfTracks; - MIDIComposition m_midiComposition; + MIDIComposition m_midiComposition; - std::ofstream *m_midiFile; - QString m_error; + std::ofstream *m_midiFile; + QString m_error; }; #endif diff -r dba8a02b0413 -r d6bd5751b8f6 data/model/FlexiNoteModel.h --- a/data/model/FlexiNoteModel.h Mon Dec 02 12:29:09 2013 +0000 +++ b/data/model/FlexiNoteModel.h Mon Dec 02 17:11:20 2013 +0000 @@ -16,10 +16,10 @@ #ifndef _FLEXINOTE_MODEL_H_ #define _FLEXINOTE_MODEL_H_ -// #include "NotelikeModel.h" // GF: reomved as this is an uncommitted experiment for now - #include "IntervalModel.h" +#include "NoteData.h" #include "base/RealTime.h" +#include "base/Pitch.h" #include "base/PlayParameterRepository.h" /** @@ -97,7 +97,7 @@ }; -class FlexiNoteModel : public IntervalModel +class FlexiNoteModel : public IntervalModel, public NoteExportable { Q_OBJECT @@ -227,6 +227,48 @@ return SortNumeric; } + /** + * NoteExportable methods. + */ + + NoteList getNotes() const { + return getNotes(getStartFrame(), getEndFrame()); + } + + NoteList getNotes(size_t startFrame, size_t endFrame) const { + + PointList points = getPoints(startFrame, endFrame); + NoteList notes; + + for (PointList::iterator pli = + points.begin(); pli != points.end(); ++pli) { + + size_t duration = pli->duration; + if (duration == 0 || duration == 1) { + duration = getSampleRate() / 20; + } + + int pitch = lrintf(pli->value); + + int velocity = 100; + if (pli->level > 0.f && pli->level <= 1.f) { + velocity = lrintf(pli->level * 127); + } + + NoteData note(pli->frame, duration, pitch, velocity); + + if (getScaleUnits() == "Hz") { + note.frequency = pli->value; + note.midiPitch = Pitch::getPitchForFrequency(note.frequency); + note.isMidiPitchQuantized = false; + } + + notes.push_back(note); + } + + return notes; + } + protected: float m_valueQuantization; }; diff -r dba8a02b0413 -r d6bd5751b8f6 data/model/NoteData.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/model/NoteData.h Mon Dec 02 17:11:20 2013 +0000 @@ -0,0 +1,43 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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 NOTE_DATA_H +#define NOTE_DATA_H + +#include + +struct NoteData +{ + NoteData(size_t _start, size_t _dur, int _mp, int _vel) : + start(_start), duration(_dur), midiPitch(_mp), frequency(0), + isMidiPitchQuantized(true), velocity(_vel) { }; + + size_t start; // audio sample frame + size_t duration; // in audio sample frames + int midiPitch; // 0-127 + int frequency; // Hz, to be used if isMidiPitchQuantized false + bool isMidiPitchQuantized; + int velocity; // MIDI-style 0-127 +}; + +typedef std::vector NoteList; + +class NoteExportable +{ +public: + virtual NoteList getNotes() const = 0; + virtual NoteList getNotes(size_t startFrame, size_t endFrame) const = 0; +}; + +#endif diff -r dba8a02b0413 -r d6bd5751b8f6 data/model/NoteModel.h --- a/data/model/NoteModel.h Mon Dec 02 12:29:09 2013 +0000 +++ b/data/model/NoteModel.h Mon Dec 02 17:11:20 2013 +0000 @@ -17,8 +17,10 @@ #define _NOTE_MODEL_H_ #include "IntervalModel.h" +#include "NoteData.h" #include "base/RealTime.h" #include "base/PlayParameterRepository.h" +#include "base/Pitch.h" /** * NoteModel -- a concrete IntervalModel for notes. @@ -91,7 +93,7 @@ }; -class NoteModel : public IntervalModel +class NoteModel : public IntervalModel, public NoteExportable { Q_OBJECT @@ -219,6 +221,48 @@ return SortNumeric; } + /** + * NoteExportable methods. + */ + + NoteList getNotes() const { + return getNotes(getStartFrame(), getEndFrame()); + } + + NoteList getNotes(size_t startFrame, size_t endFrame) const { + + PointList points = getPoints(startFrame, endFrame); + NoteList notes; + + for (PointList::iterator pli = + points.begin(); pli != points.end(); ++pli) { + + size_t duration = pli->duration; + if (duration == 0 || duration == 1) { + duration = getSampleRate() / 20; + } + + int pitch = lrintf(pli->value); + + int velocity = 100; + if (pli->level > 0.f && pli->level <= 1.f) { + velocity = lrintf(pli->level * 127); + } + + NoteData note(pli->frame, duration, pitch, velocity); + + if (getScaleUnits() == "Hz") { + note.frequency = pli->value; + note.midiPitch = Pitch::getPitchForFrequency(note.frequency); + note.isMidiPitchQuantized = false; + } + + notes.push_back(note); + } + + return notes; + } + protected: float m_valueQuantization; }; diff -r dba8a02b0413 -r d6bd5751b8f6 data/model/SparseOneDimensionalModel.h --- a/data/model/SparseOneDimensionalModel.h Mon Dec 02 12:29:09 2013 +0000 +++ b/data/model/SparseOneDimensionalModel.h Mon Dec 02 17:11:20 2013 +0000 @@ -17,6 +17,7 @@ #define _SPARSE_ONE_DIMENSIONAL_MODEL_H_ #include "SparseModel.h" +#include "NoteData.h" #include "base/PlayParameterRepository.h" #include "base/RealTime.h" @@ -69,7 +70,8 @@ }; -class SparseOneDimensionalModel : public SparseModel +class SparseOneDimensionalModel : public SparseModel, + public NoteExportable { Q_OBJECT @@ -181,6 +183,32 @@ if (column == 2) return SortAlphabetical; return SortNumeric; } + + /** + * NoteExportable methods. + */ + + NoteList getNotes() const { + return getNotes(getStartFrame(), getEndFrame()); + } + + NoteList getNotes(size_t startFrame, size_t endFrame) const { + + PointList points = getPoints(startFrame, endFrame); + NoteList notes; + + for (PointList::iterator pli = + points.begin(); pli != points.end(); ++pli) { + + notes.push_back + (NoteData(pli->frame, + getSampleRate() / 6, // arbitrary short duration + 64, // default pitch + 100)); // default velocity + } + + return notes; + } }; #endif