Chris@58: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@0: Chris@0: /* Chris@59: Sonic Visualiser Chris@59: An audio file viewer and annotation editor. Chris@59: Centre for Digital Music, Queen Mary, University of London. Chris@59: This file copyright 2006 Chris Cannam. Chris@0: Chris@59: This program is free software; you can redistribute it and/or Chris@59: modify it under the terms of the GNU General Public License as Chris@59: published by the Free Software Foundation; either version 2 of the Chris@59: License, or (at your option) any later version. See the file Chris@59: COPYING included with this distribution for more information. Chris@0: */ Chris@0: Chris@0: #include "LayerFactory.h" Chris@0: Chris@0: #include "WaveformLayer.h" Chris@0: #include "SpectrogramLayer.h" Chris@0: #include "TimeRulerLayer.h" Chris@0: #include "TimeInstantLayer.h" Chris@0: #include "TimeValueLayer.h" Chris@30: #include "NoteLayer.h" matthiasm@621: #include "FlexiNoteLayer.h" Chris@411: #include "RegionLayer.h" Chris@1518: #include "BoxLayer.h" Chris@35: #include "TextLayer.h" Chris@303: #include "ImageLayer.h" Chris@0: #include "Colour3DPlotLayer.h" Chris@133: #include "SpectrumLayer.h" Chris@193: #include "SliceLayer.h" Chris@193: #include "SliceableLayer.h" Chris@0: Chris@360: #include "base/Clipboard.h" Chris@360: Chris@128: #include "data/model/RangeSummarisableTimeValueModel.h" Chris@128: #include "data/model/DenseTimeValueModel.h" Chris@128: #include "data/model/SparseOneDimensionalModel.h" Chris@128: #include "data/model/SparseTimeValueModel.h" Chris@128: #include "data/model/NoteModel.h" Chris@411: #include "data/model/RegionModel.h" Chris@1518: #include "data/model/BoxModel.h" Chris@128: #include "data/model/TextModel.h" Chris@303: #include "data/model/ImageModel.h" Chris@128: #include "data/model/DenseThreeDimensionalModel.h" Chris@156: #include "data/model/WaveFileModel.h" Chris@156: #include "data/model/WritableWaveFileModel.h" Chris@0: Chris@326: #include Chris@326: #include Chris@326: #include Chris@326: #include Chris@326: Chris@326: #include Chris@326: Chris@0: LayerFactory * Chris@0: LayerFactory::m_instance = new LayerFactory; Chris@0: Chris@0: LayerFactory * Chris@125: LayerFactory::getInstance() Chris@0: { Chris@0: return m_instance; Chris@0: } Chris@0: Chris@0: LayerFactory::~LayerFactory() Chris@0: { Chris@0: } Chris@0: Chris@0: QString Chris@0: LayerFactory::getLayerPresentationName(LayerType type) Chris@0: { Chris@0: switch (type) { Chris@0: case Waveform: return Layer::tr("Waveform"); Chris@0: case Spectrogram: return Layer::tr("Spectrogram"); Chris@0: case TimeRuler: return Layer::tr("Ruler"); Chris@0: case TimeInstants: return Layer::tr("Time Instants"); Chris@0: case TimeValues: return Layer::tr("Time Values"); Chris@30: case Notes: return Layer::tr("Notes"); gyorgyf@625: case FlexiNotes: return Layer::tr("Flexible Notes"); Chris@411: case Regions: return Layer::tr("Regions"); Chris@1518: case Boxes: return Layer::tr("Boxes"); Chris@35: case Text: return Layer::tr("Text"); Chris@303: case Image: return Layer::tr("Images"); Chris@0: case Colour3DPlot: return Layer::tr("Colour 3D Plot"); Chris@133: case Spectrum: return Layer::tr("Spectrum"); Chris@193: case Slice: return Layer::tr("Time Slice"); Chris@0: Chris@0: case MelodicRangeSpectrogram: Chris@1266: // The user can change all the parameters of this after the Chris@1266: // fact -- there's nothing permanently melodic-range about it Chris@1266: // that should be encoded in its name Chris@1266: return Layer::tr("Spectrogram"); Chris@11: Chris@37: case PeakFrequencySpectrogram: Chris@1266: // likewise Chris@1266: return Layer::tr("Spectrogram"); Chris@37: Chris@805: case UnknownLayer: Chris@805: default: Chris@805: cerr << "WARNING: LayerFactory::getLayerPresentationName passed unknown layer" << endl; Chris@805: return Layer::tr("Unknown Layer"); Chris@0: } Chris@0: } Chris@0: Chris@193: bool Chris@193: LayerFactory::isLayerSliceable(const Layer *layer) Chris@193: { Chris@193: if (dynamic_cast(layer)) { Chris@193: if (dynamic_cast(layer)) { Chris@193: Chris@193: //!!! We can create slices of spectrograms, but there's a Chris@193: // problem managing the models. The source model for the Chris@193: // slice layer has to be one of the spectrogram's FFT Chris@193: // models -- that's fine, except that we can't store & Chris@193: // recall the slice layer with a reference to that model Chris@193: // because the model is internal to the spectrogram layer Chris@193: // and the document has no record of it. We would need Chris@193: // some other way of managing models that are used in this Chris@193: // way. For the moment we just don't allow slices of Chris@193: // spectrograms -- and provide a spectrum layer for this Chris@193: // instead. Chris@193: // Chris@193: // This business needs a bit more thought -- either come Chris@193: // up with a sensible way to deal with that stuff, or Chris@193: // simplify the existing slice layer logic so that it Chris@193: // doesn't have to deal with models disappearing on it at Chris@193: // all (and use the normal Document setModel mechanism to Chris@193: // set its sliceable model instead of the fancy pants Chris@193: // nonsense it's doing at the moment). Chris@193: Chris@193: return false; Chris@193: } Chris@193: return true; Chris@193: } Chris@193: return false; Chris@193: } Chris@193: Chris@0: LayerFactory::LayerTypeSet Chris@1471: LayerFactory::getValidLayerTypes(ModelId modelId) Chris@0: { Chris@0: LayerTypeSet types; Chris@0: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Colour3DPlot); Chris@193: types.insert(Slice); Chris@193: } Chris@193: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Waveform); Chris@0: } Chris@0: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Spectrogram); Chris@1266: types.insert(MelodicRangeSpectrogram); Chris@1266: types.insert(PeakFrequencySpectrogram); Chris@0: } Chris@0: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(TimeInstants); Chris@0: } Chris@0: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(TimeValues); Chris@411: } Chris@411: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1471: auto nm = ModelById::getAs(modelId); Chris@1471: if (nm && nm->getSubtype() == NoteModel::FLEXI_NOTE) { Chris@1426: types.insert(FlexiNotes); Chris@1426: } else { Chris@1426: types.insert(Notes); Chris@1426: } matthiasm@621: } matthiasm@621: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Regions); Chris@411: } Chris@411: Chris@1518: if (ModelById::getAs(modelId)) { Chris@1518: types.insert(Boxes); Chris@1512: } Chris@1512: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Text); Chris@30: } Chris@30: Chris@1471: if (ModelById::getAs(modelId)) { Chris@1266: types.insert(Image); Chris@303: } Chris@303: Chris@1471: if (ModelById::getAs(modelId)) { Chris@133: types.insert(Spectrum); Chris@133: } Chris@133: Chris@0: // We don't count TimeRuler here as it doesn't actually display Chris@0: // the data, although it can be backed by any model Chris@0: Chris@0: return types; Chris@0: } Chris@0: Chris@17: LayerFactory::LayerTypeSet Chris@17: LayerFactory::getValidEmptyLayerTypes() Chris@17: { Chris@17: LayerTypeSet types; Chris@17: types.insert(TimeInstants); Chris@17: types.insert(TimeValues); Chris@962: // Because this is strictly a UI function -- list the layer types Chris@962: // to show in a menu -- it should not contain FlexiNotes; the Chris@962: // layer isn't meaningfully editable in SV Chris@962: // types.insert(FlexiNotes); Chris@30: types.insert(Notes); Chris@411: types.insert(Regions); Chris@1518: types.insert(Boxes); Chris@35: types.insert(Text); Chris@303: types.insert(Image); Chris@17: //!!! and in principle Colour3DPlot -- now that's a challenge Chris@17: return types; Chris@17: } Chris@17: Chris@0: LayerFactory::LayerType Chris@6: LayerFactory::getLayerType(const Layer *layer) Chris@0: { Chris@6: if (dynamic_cast(layer)) return Waveform; Chris@6: if (dynamic_cast(layer)) return Spectrogram; Chris@6: if (dynamic_cast(layer)) return TimeRuler; Chris@6: if (dynamic_cast(layer)) return TimeInstants; Chris@6: if (dynamic_cast(layer)) return TimeValues; Chris@782: if (dynamic_cast(layer)) return FlexiNotes; Chris@30: if (dynamic_cast(layer)) return Notes; Chris@411: if (dynamic_cast(layer)) return Regions; Chris@1518: if (dynamic_cast(layer)) return Boxes; Chris@35: if (dynamic_cast(layer)) return Text; Chris@303: if (dynamic_cast(layer)) return Image; Chris@6: if (dynamic_cast(layer)) return Colour3DPlot; Chris@133: if (dynamic_cast(layer)) return Spectrum; Chris@193: if (dynamic_cast(layer)) return Slice; Chris@6: return UnknownLayer; Chris@6: } Chris@6: Chris@6: QString Chris@17: LayerFactory::getLayerIconName(LayerType type) Chris@17: { Chris@17: switch (type) { Chris@17: case Waveform: return "waveform"; Chris@17: case Spectrogram: return "spectrogram"; Chris@17: case TimeRuler: return "timeruler"; Chris@17: case TimeInstants: return "instants"; Chris@17: case TimeValues: return "values"; Chris@30: case Notes: return "notes"; Chris@782: case FlexiNotes: return "flexinotes"; Chris@411: case Regions: return "regions"; Chris@1518: case Boxes: return "boxes"; Chris@35: case Text: return "text"; Chris@303: case Image: return "image"; Chris@17: case Colour3DPlot: return "colour3d"; Chris@133: case Spectrum: return "spectrum"; Chris@193: case Slice: return "spectrum"; Chris@326: case MelodicRangeSpectrogram: return "spectrogram"; Chris@326: case PeakFrequencySpectrogram: return "spectrogram"; Chris@805: case UnknownLayer: Chris@805: default: Chris@805: cerr << "WARNING: LayerFactory::getLayerIconName passed unknown layer" << endl; Chris@805: return "unknown"; Chris@17: } Chris@17: } Chris@17: Chris@17: QString Chris@6: LayerFactory::getLayerTypeName(LayerType type) Chris@6: { Chris@6: switch (type) { Chris@6: case Waveform: return "waveform"; Chris@6: case Spectrogram: return "spectrogram"; Chris@6: case TimeRuler: return "timeruler"; Chris@6: case TimeInstants: return "timeinstants"; Chris@6: case TimeValues: return "timevalues"; Chris@30: case Notes: return "notes"; matthiasm@623: case FlexiNotes: return "flexinotes"; Chris@411: case Regions: return "regions"; Chris@1518: case Boxes: return "boxes"; Chris@35: case Text: return "text"; Chris@303: case Image: return "image"; Chris@6: case Colour3DPlot: return "colour3dplot"; Chris@133: case Spectrum: return "spectrum"; Chris@193: case Slice: return "slice"; Chris@326: case MelodicRangeSpectrogram: return "melodicrange"; Chris@326: case PeakFrequencySpectrogram: return "peakfrequency"; Chris@805: case UnknownLayer: Chris@805: default: Chris@805: cerr << "WARNING: LayerFactory::getLayerTypeName passed unknown layer" << endl; Chris@805: return "unknown"; Chris@6: } Chris@6: } Chris@6: Chris@6: LayerFactory::LayerType Chris@6: LayerFactory::getLayerTypeForName(QString name) Chris@6: { Chris@6: if (name == "waveform") return Waveform; Chris@6: if (name == "spectrogram") return Spectrogram; Chris@6: if (name == "timeruler") return TimeRuler; Chris@6: if (name == "timeinstants") return TimeInstants; Chris@6: if (name == "timevalues") return TimeValues; Chris@1005: if (name == "notes") return Notes; matthiasm@623: if (name == "flexinotes") return FlexiNotes; Chris@411: if (name == "regions") return Regions; Chris@1518: if (name == "boxes" || name == "timefrequencybox") return Boxes; Chris@35: if (name == "text") return Text; Chris@303: if (name == "image") return Image; Chris@6: if (name == "colour3dplot") return Colour3DPlot; Chris@133: if (name == "spectrum") return Spectrum; Chris@193: if (name == "slice") return Slice; Chris@1600: if (name == "melodicrange") return MelodicRangeSpectrogram; Chris@1600: if (name == "peakfrequency") return PeakFrequencySpectrogram; Chris@0: return UnknownLayer; Chris@0: } Chris@0: Chris@0: void Chris@1471: LayerFactory::setModel(Layer *layer, ModelId model) Chris@0: { Chris@156: if (trySetModel(layer, model)) Chris@1266: return; Chris@156: Chris@156: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: Chris@0: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: Chris@0: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: Chris@0: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: Chris@0: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: gyorgyf@626: if (trySetModel(layer, model)) Chris@1266: return; Chris@30: Chris@1426: if (trySetModel(layer, model)) Chris@1266: return; Chris@1266: Chris@411: if (trySetModel(layer, model)) Chris@1266: return; Chris@411: Chris@1518: if (trySetModel(layer, model)) Chris@1512: return; Chris@1512: Chris@35: if (trySetModel(layer, model)) Chris@1266: return; Chris@35: Chris@303: if (trySetModel(layer, model)) Chris@1266: return; Chris@303: Chris@0: if (trySetModel(layer, model)) Chris@1266: return; Chris@0: Chris@133: if (trySetModel(layer, model)) Chris@133: return; Chris@0: } Chris@0: Chris@1471: std::shared_ptr Chris@1471: LayerFactory::createEmptyModel(LayerType layerType, ModelId baseModelId) Chris@17: { Chris@1471: auto baseModel = ModelById::get(baseModelId); Chris@1471: if (!baseModel) return {}; Chris@1471: Chris@1471: sv_samplerate_t rate = baseModel->getSampleRate(); Chris@1471: Chris@17: if (layerType == TimeInstants) { Chris@1471: return std::make_shared(rate, 1); Chris@17: } else if (layerType == TimeValues) { Chris@1471: return std::make_shared(rate, 1, true); Chris@782: } else if (layerType == FlexiNotes) { Chris@1471: return std::make_shared(rate, 1, true); Chris@30: } else if (layerType == Notes) { Chris@1471: return std::make_shared(rate, 1, true); Chris@411: } else if (layerType == Regions) { Chris@1471: return std::make_shared(rate, 1, true); Chris@1518: } else if (layerType == Boxes) { Chris@1518: return std::make_shared(rate, 1, true); Chris@35: } else if (layerType == Text) { Chris@1471: return std::make_shared(rate, 1, true); Chris@303: } else if (layerType == Image) { Chris@1471: return std::make_shared(rate, 1, true); Chris@17: } else { Chris@1471: return {}; Chris@17: } Chris@17: } Chris@17: Chris@53: int Chris@53: LayerFactory::getChannel(Layer *layer) Chris@53: { Chris@53: if (dynamic_cast(layer)) { Chris@1266: return dynamic_cast(layer)->getChannel(); Chris@53: } Chris@53: if (dynamic_cast(layer)) { Chris@1266: return dynamic_cast(layer)->getChannel(); Chris@53: } Chris@53: return -1; Chris@53: } Chris@53: Chris@53: void Chris@53: LayerFactory::setChannel(Layer *layer, int channel) Chris@53: { Chris@53: if (dynamic_cast(layer)) { Chris@1266: dynamic_cast(layer)->setChannel(channel); Chris@1266: return; Chris@53: } Chris@53: if (dynamic_cast(layer)) { Chris@1266: dynamic_cast(layer)->setChannel(channel); Chris@1266: return; Chris@53: } Chris@349: if (dynamic_cast(layer)) { Chris@1266: dynamic_cast(layer)->setChannel(channel); Chris@1266: return; Chris@349: } Chris@53: } Chris@53: Chris@0: Layer * Chris@53: LayerFactory::createLayer(LayerType type) Chris@0: { Chris@1408: Layer *layer = nullptr; Chris@0: Chris@0: switch (type) { Chris@0: Chris@0: case Waveform: Chris@1266: layer = new WaveformLayer; Chris@1266: break; Chris@0: Chris@0: case Spectrogram: Chris@1266: layer = new SpectrogramLayer; Chris@1266: break; Chris@0: Chris@0: case TimeRuler: Chris@1266: layer = new TimeRulerLayer; Chris@1266: break; Chris@0: Chris@0: case TimeInstants: Chris@1266: layer = new TimeInstantLayer; Chris@1266: break; Chris@0: Chris@0: case TimeValues: Chris@1266: layer = new TimeValueLayer; Chris@1266: break; Chris@0: matthiasm@623: case FlexiNotes: Chris@1266: layer = new FlexiNoteLayer; Chris@1266: break; matthiasm@623: Chris@30: case Notes: Chris@1266: layer = new NoteLayer; Chris@1266: break; Chris@30: Chris@411: case Regions: Chris@1266: layer = new RegionLayer; Chris@1266: break; Chris@411: Chris@1518: case Boxes: Chris@1518: layer = new BoxLayer; Chris@1512: break; Chris@1512: Chris@35: case Text: Chris@1266: layer = new TextLayer; Chris@1266: break; Chris@35: Chris@303: case Image: Chris@1266: layer = new ImageLayer; Chris@1266: break; Chris@303: Chris@0: case Colour3DPlot: Chris@1266: layer = new Colour3DPlotLayer; Chris@1266: break; Chris@0: Chris@133: case Spectrum: Chris@133: layer = new SpectrumLayer; Chris@133: break; Chris@133: Chris@193: case Slice: Chris@193: layer = new SliceLayer; Chris@193: break; Chris@193: Chris@0: case MelodicRangeSpectrogram: Chris@1266: layer = new SpectrogramLayer(SpectrogramLayer::MelodicRange); Chris@1266: break; Chris@11: Chris@37: case PeakFrequencySpectrogram: Chris@1266: layer = new SpectrogramLayer(SpectrogramLayer::MelodicPeaks); Chris@1266: break; Chris@37: Chris@805: case UnknownLayer: Chris@805: default: Chris@805: cerr << "WARNING: LayerFactory::createLayer passed unknown layer" << endl; Chris@805: break; Chris@0: } Chris@0: Chris@0: if (!layer) { Chris@1266: cerr << "LayerFactory::createLayer: Unknown layer type " Chris@1266: << type << endl; Chris@0: } else { Chris@1266: // SVDEBUG << "LayerFactory::createLayer: Setting object name " Chris@1266: // << getLayerPresentationName(type) << " on " << layer << endl; Chris@1266: layer->setObjectName(getLayerPresentationName(type)); Chris@326: setLayerDefaultProperties(type, layer); Chris@0: } Chris@0: Chris@0: return layer; Chris@0: } Chris@0: Chris@326: void Chris@326: LayerFactory::setLayerDefaultProperties(LayerType type, Layer *layer) Chris@326: { Chris@587: // SVDEBUG << "LayerFactory::setLayerDefaultProperties: type " << type << " (name \"" << getLayerTypeName(type) << "\")" << endl; Chris@327: Chris@326: QSettings settings; Chris@326: settings.beginGroup("LayerDefaults"); Chris@326: QString defaults = settings.value(getLayerTypeName(type), "").toString(); Chris@326: if (defaults == "") return; Chris@1455: setLayerProperties(layer, defaults); Chris@1455: settings.endGroup(); Chris@1455: } Chris@326: Chris@1455: void Chris@1455: LayerFactory::setLayerProperties(Layer *layer, QString newXml) Chris@1455: { Chris@1455: QDomDocument docOld, docNew; Chris@1455: QString oldXml = layer->toXmlString(); Chris@327: Chris@1455: if (!docOld.setContent(oldXml, false)) { Chris@1455: SVCERR << "LayerFactory::setLayerProperties: Failed to parse XML for existing layer properties! XML string is: " << oldXml << endl; Chris@1455: return; Chris@1455: } Chris@1455: Chris@1455: if (!docNew.setContent(newXml, false)) { Chris@1455: SVCERR << "LayerFactory::setLayerProperties: Failed to parse XML: " << newXml << endl; Chris@1455: return; Chris@1455: } Chris@326: Chris@1455: QXmlAttributes attrs; Chris@326: Chris@1455: QDomElement layerElt = docNew.firstChildElement("layer"); Chris@1455: QDomNamedNodeMap attrNodes = layerElt.attributes(); Chris@326: Chris@1455: for (int i = 0; i < attrNodes.length(); ++i) { Chris@1455: QDomAttr attr = attrNodes.item(i).toAttr(); Chris@1455: if (attr.isNull()) continue; Chris@683: // cerr << "append \"" << attr.name() Chris@584: // << "\" -> \"" << attr.value() << "\"" Chris@682: // << endl; Chris@1455: attrs.append(attr.name(), "", "", attr.value()); Chris@1455: } Chris@1455: Chris@1455: layerElt = docOld.firstChildElement("layer"); Chris@1455: attrNodes = layerElt.attributes(); Chris@1455: for (int i = 0; i < attrNodes.length(); ++i) { Chris@1455: QDomAttr attr = attrNodes.item(i).toAttr(); Chris@1455: if (attr.isNull()) continue; Chris@1455: if (attrs.value(attr.name()) == "") { Chris@683: // cerr << "append \"" << attr.name() Chris@584: // << "\" -> \"" << attr.value() << "\"" Chris@682: // << endl; Chris@1455: attrs.append(attr.name(), "", "", attr.value()); Chris@326: } Chris@326: } Chris@1455: Chris@1455: layer->setProperties(attrs); Chris@326: } Chris@326: Chris@360: LayerFactory::LayerType Chris@360: LayerFactory::getLayerTypeForClipboardContents(const Clipboard &clip) Chris@360: { Chris@1423: const EventVector &contents = clip.getPoints(); Chris@360: Chris@360: bool haveValue = false; Chris@360: bool haveDuration = false; Chris@411: bool haveLevel = false; Chris@360: Chris@1423: for (EventVector::const_iterator i = contents.begin(); Chris@360: i != contents.end(); ++i) { Chris@1423: if (i->hasValue()) haveValue = true; Chris@1423: if (i->hasDuration()) haveDuration = true; Chris@1423: if (i->hasLevel()) haveLevel = true; Chris@360: } Chris@360: Chris@1423: if (haveValue && haveDuration && haveLevel) return Notes; Chris@1423: if (haveValue && haveDuration) return Regions; Chris@1423: if (haveValue) return TimeValues; Chris@360: return TimeInstants; Chris@360: } Chris@360: