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"
Chris@411: #include "RegionLayer.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@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 <QDomDocument>
Chris@326: #include <QDomElement>
Chris@326: #include <QDomNamedNodeMap>
Chris@326: #include <QDomAttr>
Chris@326: 
Chris@326: #include <QSettings>
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");
Chris@411:     case Regions:      return Layer::tr("Regions");
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@0: 	// The user can change all the parameters of this after the
Chris@0: 	// fact -- there's nothing permanently melodic-range about it
Chris@0: 	// that should be encoded in its name
Chris@0: 	return Layer::tr("Spectrogram");
Chris@11: 
Chris@37:     case PeakFrequencySpectrogram:
Chris@37: 	// likewise
Chris@37: 	return Layer::tr("Spectrogram");
Chris@37: 
Chris@11:     default: break;
Chris@0:     }
Chris@0: 
Chris@0:     return Layer::tr("Layer");
Chris@0: }
Chris@0: 
Chris@193: bool
Chris@193: LayerFactory::isLayerSliceable(const Layer *layer)
Chris@193: {
Chris@193:     if (dynamic_cast<const SliceableLayer *>(layer)) {
Chris@193:         if (dynamic_cast<const SpectrogramLayer *>(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@0: LayerFactory::getValidLayerTypes(Model *model)
Chris@0: {
Chris@0:     LayerTypeSet types;
Chris@0: 
Chris@0:     if (dynamic_cast<DenseThreeDimensionalModel *>(model)) {
Chris@0: 	types.insert(Colour3DPlot);
Chris@193:         types.insert(Slice);
Chris@193:     }
Chris@193: 
Chris@193:     if (dynamic_cast<RangeSummarisableTimeValueModel *>(model)) {
Chris@193: 	types.insert(Waveform);
Chris@0:     }
Chris@0: 
Chris@0:     if (dynamic_cast<DenseTimeValueModel *>(model)) {
Chris@0: 	types.insert(Spectrogram);
Chris@0: 	types.insert(MelodicRangeSpectrogram);
Chris@37: 	types.insert(PeakFrequencySpectrogram);
Chris@0:     }
Chris@0: 
Chris@0:     if (dynamic_cast<SparseOneDimensionalModel *>(model)) {
Chris@0: 	types.insert(TimeInstants);
Chris@0:     }
Chris@0: 
Chris@0:     if (dynamic_cast<SparseTimeValueModel *>(model)) {
Chris@0: 	types.insert(TimeValues);
Chris@411:     }
Chris@411: 
Chris@35:     if (dynamic_cast<NoteModel *>(model)) {
Chris@35: 	types.insert(Notes);
Chris@0:     }
Chris@0: 
Chris@411:     if (dynamic_cast<RegionModel *>(model)) {
Chris@411: 	types.insert(Regions);
Chris@411:     }
Chris@411: 
Chris@35:     if (dynamic_cast<TextModel *>(model)) {
Chris@35: 	types.insert(Text);
Chris@30:     }
Chris@30: 
Chris@303:     if (dynamic_cast<ImageModel *>(model)) {
Chris@303: 	types.insert(Image);
Chris@303:     }
Chris@303: 
Chris@133:     if (dynamic_cast<DenseTimeValueModel *>(model)) {
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@30:     types.insert(Notes);
Chris@411:     types.insert(Regions);
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<const WaveformLayer *>(layer)) return Waveform;
Chris@6:     if (dynamic_cast<const SpectrogramLayer *>(layer)) return Spectrogram;
Chris@6:     if (dynamic_cast<const TimeRulerLayer *>(layer)) return TimeRuler;
Chris@6:     if (dynamic_cast<const TimeInstantLayer *>(layer)) return TimeInstants;
Chris@6:     if (dynamic_cast<const TimeValueLayer *>(layer)) return TimeValues;
Chris@30:     if (dynamic_cast<const NoteLayer *>(layer)) return Notes;
Chris@411:     if (dynamic_cast<const RegionLayer *>(layer)) return Regions;
Chris@35:     if (dynamic_cast<const TextLayer *>(layer)) return Text;
Chris@303:     if (dynamic_cast<const ImageLayer *>(layer)) return Image;
Chris@6:     if (dynamic_cast<const Colour3DPlotLayer *>(layer)) return Colour3DPlot;
Chris@133:     if (dynamic_cast<const SpectrumLayer *>(layer)) return Spectrum;
Chris@193:     if (dynamic_cast<const SliceLayer *>(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@411:     case Regions: return "regions";
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@17:     default: 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";
Chris@411:     case Regions: return "regions";
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@6:     default: 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@30:     if (name == "notes") return Notes;
Chris@411:     if (name == "regions") return Regions;
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@0:     return UnknownLayer;
Chris@0: }
Chris@0: 
Chris@0: void
Chris@0: LayerFactory::setModel(Layer *layer, Model *model)
Chris@0: {
Chris@156: //    if (trySetModel<WaveformLayer, RangeSummarisableTimeValueModel>(layer, model))
Chris@156: //	return;
Chris@156: 
Chris@156:     if (trySetModel<WaveformLayer, WaveFileModel>(layer, model))
Chris@156: 	return;
Chris@156: 
Chris@156:     if (trySetModel<WaveformLayer, WritableWaveFileModel>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@0:     if (trySetModel<SpectrogramLayer, DenseTimeValueModel>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@0:     if (trySetModel<TimeRulerLayer, Model>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@0:     if (trySetModel<TimeInstantLayer, SparseOneDimensionalModel>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@0:     if (trySetModel<TimeValueLayer, SparseTimeValueModel>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@30:     if (trySetModel<NoteLayer, NoteModel>(layer, model))
Chris@30: 	return;
Chris@30: 
Chris@411:     if (trySetModel<RegionLayer, RegionModel>(layer, model))
Chris@411: 	return;
Chris@411: 
Chris@35:     if (trySetModel<TextLayer, TextModel>(layer, model))
Chris@35: 	return;
Chris@35: 
Chris@303:     if (trySetModel<ImageLayer, ImageModel>(layer, model))
Chris@303: 	return;
Chris@303: 
Chris@0:     if (trySetModel<Colour3DPlotLayer, DenseThreeDimensionalModel>(layer, model))
Chris@0: 	return;
Chris@0: 
Chris@0:     if (trySetModel<SpectrogramLayer, DenseTimeValueModel>(layer, model))
Chris@0: 	return;
Chris@133: 
Chris@133:     if (trySetModel<SpectrumLayer, DenseTimeValueModel>(layer, model)) 
Chris@133:         return;
Chris@193: 
Chris@193: //    if (trySetModel<SliceLayer, DenseThreeDimensionalModel>(layer, model)) 
Chris@193: //        return;
Chris@0: }
Chris@0: 
Chris@17: Model *
Chris@17: LayerFactory::createEmptyModel(LayerType layerType, Model *baseModel)
Chris@17: {
Chris@17:     if (layerType == TimeInstants) {
Chris@17: 	return new SparseOneDimensionalModel(baseModel->getSampleRate(), 1);
Chris@17:     } else if (layerType == TimeValues) {
Chris@245: 	return new SparseTimeValueModel(baseModel->getSampleRate(), 1, true);
Chris@30:     } else if (layerType == Notes) {
Chris@245: 	return new NoteModel(baseModel->getSampleRate(), 1, true);
Chris@411:     } else if (layerType == Regions) {
Chris@411: 	return new RegionModel(baseModel->getSampleRate(), 1, true);
Chris@35:     } else if (layerType == Text) {
Chris@35: 	return new TextModel(baseModel->getSampleRate(), 1, true);
Chris@303:     } else if (layerType == Image) {
Chris@303: 	return new ImageModel(baseModel->getSampleRate(), 1, true);
Chris@17:     } else {
Chris@17: 	return 0;
Chris@17:     }
Chris@17: }
Chris@17: 
Chris@53: int
Chris@53: LayerFactory::getChannel(Layer *layer)
Chris@53: {
Chris@53:     if (dynamic_cast<WaveformLayer *>(layer)) {
Chris@53: 	return dynamic_cast<WaveformLayer *>(layer)->getChannel();
Chris@53:     } 
Chris@53:     if (dynamic_cast<SpectrogramLayer *>(layer)) {
Chris@53: 	return dynamic_cast<SpectrogramLayer *>(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<WaveformLayer *>(layer)) {
Chris@53: 	dynamic_cast<WaveformLayer *>(layer)->setChannel(channel);
Chris@53: 	return;
Chris@53:     } 
Chris@53:     if (dynamic_cast<SpectrogramLayer *>(layer)) {
Chris@53: 	dynamic_cast<SpectrogramLayer *>(layer)->setChannel(channel);
Chris@53: 	return;
Chris@53:     }
Chris@349:     if (dynamic_cast<SpectrumLayer *>(layer)) {
Chris@349: 	dynamic_cast<SpectrumLayer *>(layer)->setChannel(channel);
Chris@349: 	return;
Chris@349:     }
Chris@53: }
Chris@53: 
Chris@0: Layer *
Chris@53: LayerFactory::createLayer(LayerType type)
Chris@0: {
Chris@0:     Layer *layer = 0;
Chris@0: 
Chris@0:     switch (type) {
Chris@0: 
Chris@0:     case Waveform:
Chris@44: 	layer = new WaveformLayer;
Chris@0: 	break;
Chris@0: 
Chris@0:     case Spectrogram:
Chris@44: 	layer = new SpectrogramLayer;
Chris@0: 	break;
Chris@0: 
Chris@0:     case TimeRuler:
Chris@44: 	layer = new TimeRulerLayer;
Chris@0: 	break;
Chris@0: 
Chris@0:     case TimeInstants:
Chris@44: 	layer = new TimeInstantLayer;
Chris@0: 	break;
Chris@0: 
Chris@0:     case TimeValues:
Chris@44: 	layer = new TimeValueLayer;
Chris@0: 	break;
Chris@0: 
Chris@30:     case Notes:
Chris@44: 	layer = new NoteLayer;
Chris@30: 	break;
Chris@30: 
Chris@411:     case Regions:
Chris@411: 	layer = new RegionLayer;
Chris@411: 	break;
Chris@411: 
Chris@35:     case Text:
Chris@44: 	layer = new TextLayer;
Chris@35: 	break;
Chris@35: 
Chris@303:     case Image:
Chris@303: 	layer = new ImageLayer;
Chris@303: 	break;
Chris@303: 
Chris@0:     case Colour3DPlot:
Chris@44: 	layer = new Colour3DPlotLayer;
Chris@0: 	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@44: 	layer = new SpectrogramLayer(SpectrogramLayer::MelodicRange);
Chris@0: 	break;
Chris@11: 
Chris@37:     case PeakFrequencySpectrogram: 
Chris@44: 	layer = new SpectrogramLayer(SpectrogramLayer::MelodicPeaks);
Chris@37: 	break;
Chris@37: 
Chris@11:     default: break;
Chris@0:     }
Chris@0: 
Chris@0:     if (!layer) {
Chris@0: 	std::cerr << "LayerFactory::createLayer: Unknown layer type " 
Chris@0: 		  << type << std::endl;
Chris@0:     } else {
Chris@336: //	std::cerr << "LayerFactory::createLayer: Setting object name "
Chris@336: //		  << getLayerPresentationName(type).toStdString() << " on " << layer << std::endl;
Chris@0: 	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@336: //    std::cerr << "LayerFactory::setLayerDefaultProperties: type " << type << " (name \"" << getLayerTypeName(type).toStdString() << "\")" << std::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@326: 
Chris@336: //    std::cerr << "defaults=\"" << defaults.toStdString() << "\"" << std::endl;
Chris@327: 
Chris@326:     QString xml = layer->toXmlString();
Chris@326:     QDomDocument docOld, docNew;
Chris@326:     
Chris@326:     if (docOld.setContent(xml, false) &&
Chris@326:         docNew.setContent(defaults, false)) {
Chris@326:         
Chris@326:         QXmlAttributes attrs;
Chris@326:         
Chris@326:         QDomElement layerElt = docNew.firstChildElement("layer");
Chris@326:         QDomNamedNodeMap attrNodes = layerElt.attributes();
Chris@326:         
Chris@326:         for (unsigned int i = 0; i < attrNodes.length(); ++i) {
Chris@326:             QDomAttr attr = attrNodes.item(i).toAttr();
Chris@326:             if (attr.isNull()) continue;
Chris@336: //            std::cerr << "append \"" << attr.name().toStdString()
Chris@336: //                      << "\" -> \"" << attr.value().toStdString() << "\""
Chris@336: //                      << std::endl;
Chris@326:             attrs.append(attr.name(), "", "", attr.value());
Chris@326:         }
Chris@326:         
Chris@326:         layerElt = docOld.firstChildElement("layer");
Chris@326:         attrNodes = layerElt.attributes();
Chris@326:         for (unsigned int i = 0; i < attrNodes.length(); ++i) {
Chris@326:             QDomAttr attr = attrNodes.item(i).toAttr();
Chris@326:             if (attr.isNull()) continue;
Chris@326:             if (attrs.value(attr.name()) == "") {
Chris@336: //                std::cerr << "append \"" << attr.name().toStdString()
Chris@336: //                          << "\" -> \"" << attr.value().toStdString() << "\""
Chris@336: //                          << std::endl;
Chris@326:                 attrs.append(attr.name(), "", "", attr.value());
Chris@326:             }
Chris@326:         }
Chris@326:         
Chris@326:         layer->setProperties(attrs);
Chris@326:     }
Chris@326: 
Chris@326:     settings.endGroup();
Chris@326: }
Chris@326: 
Chris@360: LayerFactory::LayerType
Chris@360: LayerFactory::getLayerTypeForClipboardContents(const Clipboard &clip)
Chris@360: {
Chris@360:     const Clipboard::PointList &contents = clip.getPoints();
Chris@360: 
Chris@360:     bool haveFrame = false;
Chris@360:     bool haveValue = false;
Chris@360:     bool haveDuration = false;
Chris@411:     bool haveLevel = false;
Chris@360: 
Chris@360:     for (Clipboard::PointList::const_iterator i = contents.begin();
Chris@360:          i != contents.end(); ++i) {
Chris@360:         if (i->haveFrame()) haveFrame = true;
Chris@360:         if (i->haveValue()) haveValue = true;
Chris@360:         if (i->haveDuration()) haveDuration = true;
Chris@411:         if (i->haveLevel()) haveLevel = true;
Chris@360:     }
Chris@360: 
Chris@411:     if (haveFrame && haveValue && haveDuration && haveLevel) return Notes;
Chris@411:     if (haveFrame && haveValue && haveDuration) return Regions;
Chris@360:     if (haveFrame && haveValue) return TimeValues;
Chris@360:     return TimeInstants;
Chris@360: }
Chris@360: