diff --git a/Source/CoreServices.cpp b/Source/CoreServices.cpp index 69f3cd426a6da4b56874b7cb575094b5f06775c8..46b68398ec1c4b74bf70d4008d9f045d211991b3 100644 --- a/Source/CoreServices.cpp +++ b/Source/CoreServices.cpp @@ -80,12 +80,17 @@ void highlightEditor(GenericEditor* ed) int64 getGlobalTimestamp() { - return getMessageCenter()->getTimestamp(); + return static_cast<MessageCenter*>(&(getMessageCenter()->processor))->getGlobalTimestamp(); } int64 getSoftwareTimestamp() { - return getMessageCenter()->getTimestamp(true); + return static_cast<MessageCenter*>(&(getMessageCenter()->processor))->getGlobalTimestamp(true); +} + +float getGlobalSampleRate() +{ + return static_cast<MessageCenter*>(&(getMessageCenter()->processor))->getGlobalSampleRate(); } void setRecordingDirectory(String dir) diff --git a/Source/CoreServices.h b/Source/CoreServices.h index 56dbf6887ff10a4b02c93789fdf87f736dc267bb..e4f6ec2c42ac55a09c091712b56589c28f3a8aac 100644 --- a/Source/CoreServices.h +++ b/Source/CoreServices.h @@ -63,6 +63,11 @@ Defaults to the first hardware timestamp source or the software one if no hardware timestamping is present*/ PLUGIN_API int64 getGlobalTimestamp(); +/** Gets the sample rate selected on the MessageCenter interface +Defaults to the dample rate of the first hardware source or +the software high resolution timer if no hardware source is present*/ +PLUGIN_API float getGlobalSampleRate(); + /** Gets the software timestamp based on a high resolution timer aligned to the start of each processing block */ PLUGIN_API int64 getSoftwareTimestamp(); diff --git a/Source/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp b/Source/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp index 15d2d6bddc890ed0602dd02346fb3df7464a28e0..1908013f59fe3ca472723f92cc8c014ae7f7f533 100644 --- a/Source/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp +++ b/Source/Plugins/BasicSpikeDisplay/SpikeDetector/SpikeDetector.cpp @@ -94,10 +94,6 @@ void SpikeDetector::createSpikeChannels() } SpikeChannel* spk = new SpikeChannel(SpikeChannel::typeFromNumChannels(nChans), this, chans); spk->setNumSamples(elec->prePeakSamples, elec->postPeakSamples); - //We currently have no means to prevent creating spikes from channels from different sources - //We assume that all come from the same source, thus sharing sample rate ans bitvolts. By default, set it to the - //first channel. - spk->setSampleRate(chans[0]->getSampleRate()); spikeChannelArray.add(spk); } } diff --git a/Source/Plugins/LfpDisplayNode/LfpDisplayNode.cpp b/Source/Plugins/LfpDisplayNode/LfpDisplayNode.cpp index 4fdb6eb06b0223421904f41b97d381510c77aeb2..dcf2d80a8186a062266a6b56a6f096996faf282b 100644 --- a/Source/Plugins/LfpDisplayNode/LfpDisplayNode.cpp +++ b/Source/Plugins/LfpDisplayNode/LfpDisplayNode.cpp @@ -163,8 +163,9 @@ void LfpDisplayNode::handleEvent(const EventChannel* eventInfo, const MidiMessag const int eventChannel = ttl->getChannel(); const int eventTime = samplePosition; const uint32 eventSourceNodeId = getProcessorFullId(ttl->getSourceID(), ttl->getSubProcessorIdx()); - const int nSamples = numSamples.at (eventSourceNodeId); - const int samplesToFill = nSamples - eventTime; + const int nSamples = getNumSourceSamples (eventSourceNodeId); + int samplesToFill = nSamples - eventTime; + if (samplesToFill < 0) samplesToFill = 0; // std::cout << "Received event from " << eventSourceNode << ", channel " // << eventChannel << ", with ID " << eventId << ", copying to " @@ -230,7 +231,7 @@ void LfpDisplayNode::initializeEventChannels() const int chan = channelForEventSource[eventSourceNodes[i]]; const int index = displayBufferIndex[chan]; const int samplesLeft = displayBuffer->getNumSamples() - index; - const int nSamples = numSamples.at(eventSourceNodes[i]); + const int nSamples = getNumSourceSamples(eventSourceNodes[i]); if (nSamples < samplesLeft) { diff --git a/Source/Plugins/PhaseDetector/PhaseDetector.cpp b/Source/Plugins/PhaseDetector/PhaseDetector.cpp index d952139487482954f4303351e6d5b90fbc358a9c..3447a1272ab24ec34c68c3ecc3ea656bdd935854 100644 --- a/Source/Plugins/PhaseDetector/PhaseDetector.cpp +++ b/Source/Plugins/PhaseDetector/PhaseDetector.cpp @@ -51,13 +51,40 @@ AudioProcessorEditor* PhaseDetector::createEditor() return editor; } -void PhaseDetector::getDefaultEventInfo(Array<DefaultEventInfo>& events, int subProcessorIdx) const +void PhaseDetector::createEventChannels() { - if (subProcessorIdx != 0) return; - events.add(DefaultEventInfo(EventChannel::TTL, 8, 1)); + moduleEventChannels.clear(); + for (int i = 0; i < modules.size(); i++) + { + const DataChannel* in = getDataChannel(modules[i].inputChan); + EventChannel* ev = new EventChannel(EventChannel::TTL, 8, 1, in->getSampleRate(), this); + ev->setName("Phase detector output " + String(i+1)); + ev->setDescription("Triggers when the input signal mets a given phase condition"); + String typeDesc; + switch (modules[i].type) + { + case PEAK: typeDesc = "Positive peak"; break; + case FALLING_ZERO: typeDesc = "Zero crossing with negative slope"; break; + case TROUGH: typeDesc = "Negative peak"; break; + case RISING_ZERO: typeDesc = "Zero crossing with positive slope"; break; + default: typeDesc = "No phase selected"; break; + } + MetaDataDescriptor md(MetaDataDescriptor::CHAR, 34, "Phase Type", "Description of the phase condition", "string.extraInfo"); + MetaDataValue mv(md); + mv.setValue(typeDesc); + ev->addMetaData(md, mv); + md = MetaDataDescriptor(MetaDataDescriptor::UINT16, 3, "Source Channel", + "Index at its source, Source processor ID and Sub Processor index of the channel that triggers this event", "source.channel.identifier.full"); + mv = MetaDataValue(md); + uint16 sourceInfo[3]; + sourceInfo[0] = in->getSourceIndex(); + sourceInfo[1] = in->getSourceNodeID(); + sourceInfo[3] = in->getSubProcessorIdx(); + mv.setValue(static_cast<const uint16*>(sourceInfo)); + ev->addMetaData(md, mv); + } } - void PhaseDetector::addModule() { DetectorModule m = DetectorModule(); @@ -145,8 +172,6 @@ void PhaseDetector::updateSettings() bool PhaseDetector::enable() { - //This is to avoid having to look for the channel every time - outputEventChannel = getEventChannel(getEventChannelIndex(0, getNodeId())); return true; } @@ -206,11 +231,9 @@ void PhaseDetector::process (AudioSampleBuffer& buffer) { if (module.type == PEAK) { - //A "whole ttl word" does not have any meaning on this processor, so we just create a dummy one - //Another option, to take into account for the future, is to create an entire TTL event object for each module uint8 ttlData = 1 << module.outputChan; - TTLEventPtr event = TTLEvent::createTTLEvent(outputEventChannel, getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); - addEvent(outputEventChannel, event, i); + TTLEventPtr event = TTLEvent::createTTLEvent(moduleEventChannels[i], getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); + addEvent(moduleEventChannels[i], event, i); module.samplesSinceTrigger = 0; module.wasTriggered = true; } @@ -224,8 +247,8 @@ void PhaseDetector::process (AudioSampleBuffer& buffer) if (module.type == FALLING_ZERO) { uint8 ttlData = 1 << module.outputChan; - TTLEventPtr event = TTLEvent::createTTLEvent(outputEventChannel, getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); - addEvent(outputEventChannel, event, i); + TTLEventPtr event = TTLEvent::createTTLEvent(moduleEventChannels[i], getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); + addEvent(moduleEventChannels[i], event, i); module.samplesSinceTrigger = 0; module.wasTriggered = true; } @@ -237,8 +260,8 @@ void PhaseDetector::process (AudioSampleBuffer& buffer) if (module.type == TROUGH) { uint8 ttlData = 1 << module.outputChan; - TTLEventPtr event = TTLEvent::createTTLEvent(outputEventChannel, getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); - addEvent(outputEventChannel, event, i); + TTLEventPtr event = TTLEvent::createTTLEvent(moduleEventChannels[i], getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); + addEvent(moduleEventChannels[i], event, i); module.samplesSinceTrigger = 0; module.wasTriggered = true; } @@ -252,8 +275,8 @@ void PhaseDetector::process (AudioSampleBuffer& buffer) if (module.type == RISING_ZERO) { uint8 ttlData = 1 << module.outputChan; - TTLEventPtr event = TTLEvent::createTTLEvent(outputEventChannel, getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); - addEvent(outputEventChannel, event, i); + TTLEventPtr event = TTLEvent::createTTLEvent(moduleEventChannels[i], getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); + addEvent(moduleEventChannels[i], event, i); module.samplesSinceTrigger = 0; module.wasTriggered = true; } @@ -268,8 +291,8 @@ void PhaseDetector::process (AudioSampleBuffer& buffer) if (module.samplesSinceTrigger > 1000) { uint8 ttlData = 0; - TTLEventPtr event = TTLEvent::createTTLEvent(outputEventChannel, getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); - addEvent(outputEventChannel, event, i); + TTLEventPtr event = TTLEvent::createTTLEvent(moduleEventChannels[i], getTimestamp(module.inputChan) + i, &ttlData, sizeof(uint8), module.outputChan); + addEvent(moduleEventChannels[i], event, i); module.wasTriggered = false; } else diff --git a/Source/Plugins/PhaseDetector/PhaseDetector.h b/Source/Plugins/PhaseDetector/PhaseDetector.h index 010978fa4a4ead0aae3a3eb6b4f7df31bca88f37..bd5b449540f0fb124e77a2a1056157934b29b509 100644 --- a/Source/Plugins/PhaseDetector/PhaseDetector.h +++ b/Source/Plugins/PhaseDetector/PhaseDetector.h @@ -52,7 +52,7 @@ public: bool enable() override; void updateSettings() override; - void getDefaultEventInfo(Array<DefaultEventInfo>& events, int subProcessorIdx = 0) const override; + void createEventChannels() override; void addModule(); void setActiveModule (int); @@ -98,7 +98,7 @@ private: bool fallingPos; bool fallingNeg; - const EventChannel* outputEventChannel; + Array<const EventChannel*> moduleEventChannels; JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (PhaseDetector); }; diff --git a/Source/Plugins/SerialInput/SerialInput.cpp b/Source/Plugins/SerialInput/SerialInput.cpp index 5c90d831a9c86c3981fd04571b96d594722db65b..24690561cf040ec7af792a7efd180310c21f43c3 100644 --- a/Source/Plugins/SerialInput/SerialInput.cpp +++ b/Source/Plugins/SerialInput/SerialInput.cpp @@ -63,8 +63,11 @@ SerialInput::~SerialInput() void SerialInput::createEventChannels() { //It's going to be raw binary data, so let's make it uint8 - EventChannel* chan = new EventChannel(EventChannel::UINT8_ARRAY, 1, MAX_MSG_SIZE, this); - chan->addEventMetaData(new MetaDataDescriptor(MetaDataDescriptor::UINT64, 1, "Read Bytes", "Number of actual read bytes in the buffer", "bufferLength")); + EventChannel* chan = new EventChannel(EventChannel::UINT8_ARRAY, 1, MAX_MSG_SIZE, CoreServices::getGlobalSampleRate(), this); + chan->setName("Serial message"); + chan->setDescription("Data received via serial port"); + chan->setDescriptor("buffer.rawData"); + chan->addEventMetaData(new MetaDataDescriptor(MetaDataDescriptor::UINT64, 1, "Read Bytes", "Number of actual read bytes in the buffer", "buffer.size")); eventChannelArray.add(chan); } diff --git a/Source/Processors/AudioNode/AudioNode.cpp b/Source/Processors/AudioNode/AudioNode.cpp index f5066997256d47f987a66ec52ad7f7fb992aaf94..29ff3ddf1ed349bac061aa0e801092cf3d93a2f4 100755 --- a/Source/Processors/AudioNode/AudioNode.cpp +++ b/Source/Processors/AudioNode/AudioNode.cpp @@ -319,7 +319,7 @@ void AudioNode::process(AudioSampleBuffer& buffer) int remainingSamples = numSamplesExpected[i] - samplesToCopyFromOverflowBuffer; - int samplesAvailable = numSamples.at(getProcessorFullId(dataChannelArray[i]->getSourceNodeID(), dataChannelArray[i]->getSubProcessorIdx())); + int samplesAvailable = getNumSourceSamples(dataChannelArray[i]->getSourceNodeID(), dataChannelArray[i]->getSubProcessorIdx()); int samplesToCopyFromIncomingBuffer = ((remainingSamples <= samplesAvailable) ? remainingSamples : diff --git a/Source/Processors/Channel/InfoObjects.cpp b/Source/Processors/Channel/InfoObjects.cpp index 96eabd3203551c1364eb10e7694dcaa374ffe43a..7bf9dfc3c73fdd90c1a89210a068e283db51e8f1 100644 --- a/Source/Processors/Channel/InfoObjects.cpp +++ b/Source/Processors/Channel/InfoObjects.cpp @@ -114,19 +114,15 @@ String NamedInfoObject::getDescription() const } //InfoObjectCommon -InfoObjectCommon::InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc) +InfoObjectCommon::InfoObjectCommon(uint16 idx, uint16 typeidx, float sampleRate, const GenericProcessor* source, uint16 subproc) : NodeInfoBase(source->getNodeId()), SourceProcessorInfo(source, subproc), m_sourceIndex(idx), - m_sourceTypeIndex(typeidx) + m_sourceTypeIndex(typeidx), + m_sampleRate(sampleRate) { } -void InfoObjectCommon::setSampleRate(float sampleRate) -{ - m_sampleRate = sampleRate; -} - float InfoObjectCommon::getSampleRate() const { return m_sampleRate; @@ -145,8 +141,8 @@ uint16 InfoObjectCommon::getSourceTypeIndex() const //DataChannel -DataChannel::DataChannel(DataChannelTypes type, GenericProcessor* source, uint16 subproc) : - InfoObjectCommon(source->dataChannelCount++, source->dataChannelTypeCount[type]++, source, subproc), +DataChannel::DataChannel(DataChannelTypes type, float sampleRate, GenericProcessor* source, uint16 subproc) : + InfoObjectCommon(source->dataChannelCount++, source->dataChannelTypeCount[type]++, sampleRate, source, subproc), m_type(type) { setName(getDefaultName()); @@ -224,7 +220,6 @@ void DataChannel::reset() m_isEnabled = true; m_isMonitored = false; m_isRecording = false; - setSampleRate(44100); } String DataChannel::getDefaultName() const @@ -245,8 +240,8 @@ String DataChannel::getDefaultName() const } //EventChannel -EventChannel::EventChannel(EventChannelTypes type, unsigned int nChannels, unsigned int dataLength, GenericProcessor* source, uint16 subproc) - : InfoObjectCommon(source->eventChannelCount++, source->eventChannelTypeCount[type]++, source, subproc), +EventChannel::EventChannel(EventChannelTypes type, unsigned int nChannels, unsigned int dataLength, float sampleRate, GenericProcessor* source, uint16 subproc) + : InfoObjectCommon(source->eventChannelCount++, source->eventChannelTypeCount[type]++, sampleRate, source, subproc), m_type(type) { m_numChannels = nChannels; @@ -347,8 +342,8 @@ String EventChannel::getDefaultName() const //SpikeChannel SpikeChannel::SpikeChannel(ElectrodeTypes type, GenericProcessor* source, const Array<const DataChannel*>& sourceChannels, uint16 subproc) - : InfoObjectCommon(source->spikeChannelCount++, source->spikeChannelTypeCount[type]++, source, subproc), - m_type(type) + : InfoObjectCommon(source->spikeChannelCount++, source->spikeChannelTypeCount[type]++, sourceChannels[0]->getSampleRate(), source, subproc), + m_type(type) //We define the sample rate of the whole spike to be equal to that of the first channel. A spike composed from channels from different sample rates has no sense { int n = sourceChannels.size(); jassert(n == getNumChannels(type)); diff --git a/Source/Processors/Channel/InfoObjects.h b/Source/Processors/Channel/InfoObjects.h index 3a652b6014579b22cb8c6171a98873c978352e82..70f1f255cf1656615bad10cbd2330ad87c428430 100644 --- a/Source/Processors/Channel/InfoObjects.h +++ b/Source/Processors/Channel/InfoObjects.h @@ -132,7 +132,7 @@ class PLUGIN_API InfoObjectCommon : public NodeInfoBase, public SourceProcessorInfo, public NamedInfoObject { protected: - InfoObjectCommon(uint16 idx, uint16 typeidx, const GenericProcessor* source, uint16 subproc = 0); + InfoObjectCommon(uint16 idx, uint16 typeidx, float sampleRate, const GenericProcessor* source, uint16 subproc = 0); public: enum InfoObjectType @@ -143,9 +143,6 @@ public: INVALID = 100 }; - /** Sets the sample rate value for this channel. */ - void setSampleRate(float sampleRate); - /** Returns the sample rate value for this channel. */ float getSampleRate() const; @@ -164,7 +161,7 @@ private: const uint16 m_sourceIndex; /** Index of this particular subtype in the source processor */ const uint16 m_sourceTypeIndex; - float m_sampleRate{ 44100.0f }; + const float m_sampleRate; }; // ------- Main objects -------// @@ -185,12 +182,11 @@ public: /** Default constructor for creating Channels from scratch. @param type The type of data this channel represents (HEADSTAGE, ADC, AUX) - @param idx The index of this data channel in the source processor - @param typeidx The index of this particular type of data channel in the source processor + @param sampleRate the sample rate this channel is acquiring data @param source A pointer to the source processor @param subproc Optional. The source subprocessor index. */ - DataChannel(DataChannelTypes type, GenericProcessor* source, uint16 subproc = 0); + DataChannel(DataChannelTypes type, float sampleRate, GenericProcessor* source, uint16 subproc = 0); /** Copy constructor. */ DataChannel(const DataChannel& ch); @@ -274,8 +270,7 @@ public: @param type The type of event this channel represents (TTL, TEXT, BYINARY_MSG) @param numChannels The number of virtual channels @param dataLength The length of the event payload - @param idx The index of this event channel in the source processor - @param typeidx The index of this particular type of event channel in the source processor + @param sampleRate the sample rate this channel timestamps are referred to @param source A pointer to the source processor @param subproc Optional. The source subprocessor index. @@ -289,7 +284,7 @@ public: -For typed array events, the number of elements */ - EventChannel(EventChannelTypes type, unsigned int numChannels, unsigned int dataLength, GenericProcessor* source, uint16 subproc = 0); + EventChannel(EventChannelTypes type, unsigned int numChannels, unsigned int dataLength, float sampleRate, GenericProcessor* source, uint16 subproc = 0); ~EventChannel(); @@ -350,8 +345,6 @@ public: /** Default constructor @param type The type of electrode this channel represents (SINGLE, STEREOTRODE, TETRODE) - @param idx The index of this spike channel in the source processor - @param typeidx The index of this particular type of spike channel in the source processor @param source A pointer to the source processor @param souceChannels An array containing const pointers to the channels that originate the data for this spike electrode @param subproc Optional. The source subprocessor index. diff --git a/Source/Processors/Channel/MetaData.cpp b/Source/Processors/Channel/MetaData.cpp index b4f7759985441784bc2eb866332655bd0b311709..6e73d3d0351ce6a2c1876b937ec514b47c8c5b81 100644 --- a/Source/Processors/Channel/MetaData.cpp +++ b/Source/Processors/Channel/MetaData.cpp @@ -46,10 +46,26 @@ bool checkMetaDataType(MetaDataDescriptor::MetaDataTypes baseType) MetaDataDescriptor::MetaDataDescriptor(MetaDataDescriptor::MetaDataTypes t, unsigned int length, String n, String d, String dm) : m_name(n), m_description(d), m_descriptor(dm), m_type(t), m_length(length) -{}; +{} MetaDataDescriptor::~MetaDataDescriptor() {}; +MetaDataDescriptor::MetaDataDescriptor(const MetaDataDescriptor& other) + :ReferenceCountedObject(), + m_name(other.m_name), m_descriptor(other.m_descriptor), m_description(other.m_description), + m_type(other.m_type), m_length(other.m_length) +{} + +MetaDataDescriptor& MetaDataDescriptor::operator=(const MetaDataDescriptor& other) +{ + m_name = other.m_name; + m_descriptor = other.m_descriptor; + m_description = other.m_description; + m_type = other.m_type; + m_length = other.m_length; + return *this; +} + MetaDataDescriptor::MetaDataTypes MetaDataDescriptor::getType() const { return m_type; } unsigned int MetaDataDescriptor::getLength() const { return m_length; } size_t MetaDataDescriptor::getDataSize() const { return m_length*getTypeSize(m_type); } @@ -149,7 +165,8 @@ void MetaDataValue::allocSpace() } MetaDataValue::MetaDataValue(const MetaDataValue& v) - : m_type(v.m_type), m_length(v.m_length), m_size(v.m_size) + : ReferenceCountedObject(), + m_type(v.m_type), m_length(v.m_length), m_size(v.m_size) { allocSpace(); setValue(v.m_data.getData()); @@ -243,10 +260,31 @@ MetaDataInfoObject::MetaDataInfoObject() {} void MetaDataInfoObject::addMetaData(MetaDataDescriptor* desc, MetaDataValue* val) { + if (desc->getType() != val->getDataType() || desc->getLength() != val->getDataLength()) + { + jassertfalse; + //This will cause a segfault if the software tries to use the pointers after calling this method + //Since this method should NEVER be called with non-matching metadata description and value, it's + //better than just leave dangling pointers. + delete desc; + delete val; + return; + } m_metaDataDescriptorArray.add(desc); m_metaDataValueArray.add(val); } +void MetaDataInfoObject::addMetaData(const MetaDataDescriptor& desc, const MetaDataValue& val) +{ + if (desc.getType() != val.getDataType() || desc.getLength() != val.getDataLength()) + { + jassertfalse; + return; + } + m_metaDataDescriptorArray.add(new MetaDataDescriptor(desc)); + m_metaDataValueArray.add(new MetaDataValue(val)); +} + const MetaDataDescriptor* MetaDataInfoObject::getMetaDataDescriptor(int index) const { return m_metaDataDescriptorArray[index]; @@ -278,6 +316,18 @@ void MetaDataEventObject::addEventMetaData(MetaDataDescriptor* desc) m_totalSize += desc->getDataSize(); } +void MetaDataEventObject::addEventMetaData(const MetaDataDescriptor& desc) +{ + if (eventMetaDataLock) + { + //throw assertion when debugging + jassertfalse; + return; + } + m_eventMetaDataDescriptorArray.add(new MetaDataDescriptor(desc)); + m_totalSize += desc.getDataSize(); +} + size_t MetaDataEventObject::getTotalEventMetaDataSize() const { return m_totalSize; diff --git a/Source/Processors/Channel/MetaData.h b/Source/Processors/Channel/MetaData.h index db5b8481f3f83fa9a1f2a596604152f1c6f53cfa..60801fc3ccfb9fee28003c56fe1e1acf6376f11e 100644 --- a/Source/Processors/Channel/MetaData.h +++ b/Source/Processors/Channel/MetaData.h @@ -69,6 +69,9 @@ public: */ MetaDataDescriptor(MetaDataTypes type, unsigned int length, String name, String humanDescription, String machineDescriptor); ~MetaDataDescriptor(); + MetaDataDescriptor(const MetaDataDescriptor& other); + MetaDataDescriptor& operator=(const MetaDataDescriptor& other); + /** Gets the primitive type of this field */ MetaDataTypes getType() const; /** Gets the number of elements in this field */ @@ -88,11 +91,11 @@ public: static size_t getTypeSize(MetaDataTypes type); private: MetaDataDescriptor() = delete; - const String m_name; - const String m_descriptor; - const String m_description; - const MetaDataTypes m_type; - const unsigned int m_length; + String m_name; + String m_descriptor; + String m_description; + MetaDataTypes m_type; + unsigned int m_length; JUCE_LEAK_DETECTOR(MetaDataDescriptor); }; @@ -169,6 +172,7 @@ protected: MetaDataInfoObject(); public: void addMetaData(MetaDataDescriptor* desc, MetaDataValue* val); + void addMetaData(const MetaDataDescriptor& desc, const MetaDataValue& val); const MetaDataDescriptor* getMetaDataDescriptor(int index) const; const MetaDataValue* getMetaDataValue(int index) const; const int getMetaDataCount() const; @@ -195,6 +199,7 @@ class PLUGIN_API MetaDataEventObject : private MetaDataEventLock public: //This method will only work when creating the info object, but not for those copied down the chain void addEventMetaData(MetaDataDescriptor* desc); + void addEventMetaData(const MetaDataDescriptor& desc); const MetaDataDescriptor* getEventMetaDataDescriptor(int index) const; size_t getTotalEventMetaDataSize() const; const int getEventMetaDataCount() const; diff --git a/Source/Processors/GenericProcessor/GenericProcessor.cpp b/Source/Processors/GenericProcessor/GenericProcessor.cpp index 2dd813feda5e59186fd8d86c3629d6c8eadb2fce..79cbadf38bb8ed7344974e796b5db0081ed3e2ea 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.cpp +++ b/Source/Processors/GenericProcessor/GenericProcessor.cpp @@ -443,8 +443,7 @@ void GenericProcessor::createDataChannelsByType(DataChannel::DataChannelTypes ty int nChans = getDefaultNumDataOutputs(type, sub); for (int i = 0; i < nChans; i++) { - DataChannel* chan = new DataChannel(type, this, sub); - chan->setSampleRate(getSampleRate(sub)); + DataChannel* chan = new DataChannel(type, getSampleRate(sub), this, sub); chan->setBitVolts(getBitVolts(sub)); chan->addToHistoricString(getName()); chan->m_nodeID = nodeId; @@ -465,8 +464,7 @@ void GenericProcessor::createEventChannels() { if (events[i].type != EventChannel::INVALID && events[i].nChannels > 0 && events[i].length > 0) { - EventChannel* chan = new EventChannel(events[i].type, events[i].nChannels, events[i].length, this, sub); - chan->setSampleRate(getSampleRate(sub)); + EventChannel* chan = new EventChannel(events[i].type, events[i].nChannels, events[i].length, events[i].sampleRate, this, sub); chan->m_nodeID = nodeId; eventChannelArray.add(chan); } @@ -564,7 +562,7 @@ uint32 GenericProcessor::getNumSamples (int channelNum) const { nSamples = numSamples.at (sourceID); } - catch (std::exception& e) + catch (...) { return 0; } @@ -598,7 +596,7 @@ uint64 GenericProcessor::getTimestamp (int channelNum) const { ts = timestamps.at (sourceID); } - catch (std::exception& e) + catch (...) { return 0; } @@ -606,6 +604,44 @@ uint64 GenericProcessor::getTimestamp (int channelNum) const return ts; } +uint32 GenericProcessor::getNumSourceSamples(uint16 processorID, uint16 subProcessorIdx) const +{ + return getNumSourceSamples(getProcessorFullId(processorID, subProcessorIdx)); +} + +uint32 GenericProcessor::getNumSourceSamples(uint32 fullSourceID) const +{ + uint32 nSamples; + try + { + nSamples = numSamples.at(fullSourceID); + } + catch (...) + { + return 0; + } + return nSamples; +} + +uint64 GenericProcessor::getSourceTimestamp(uint16 processorID, uint16 subProcessorIdx) const +{ + return getSourceTimestamp(getProcessorFullId(processorID, subProcessorIdx)); +} + +uint64 GenericProcessor::getSourceTimestamp(uint32 fullSourceID) const +{ + uint64 ts; + try + { + ts = timestamps.at(fullSourceID); + } + catch (...) + { + return 0; + } + return ts; +} + /** Used to set the timestamp for a given buffer, for a given channel. */ void GenericProcessor::setTimestampAndSamples(uint64 timestamp, uint32 nSamples, int subProcessorIdx) @@ -1165,19 +1201,20 @@ bool GenericProcessor::disable() return true; } -GenericProcessor::DefaultEventInfo::DefaultEventInfo(EventChannel::EventChannelTypes t, unsigned int c, unsigned int l) +GenericProcessor::DefaultEventInfo::DefaultEventInfo(EventChannel::EventChannelTypes t, unsigned int c, unsigned int l, float s) :type(t), nChannels(c), - length(l) + length(l), + sampleRate(s) { } GenericProcessor::DefaultEventInfo::DefaultEventInfo() :type(EventChannel::INVALID), nChannels(0), - length(0) -{ -} + length(0), + sampleRate(44100) +{} uint32 GenericProcessor::getProcessorFullId(uint16 sid, uint16 subid) { diff --git a/Source/Processors/GenericProcessor/GenericProcessor.h b/Source/Processors/GenericProcessor/GenericProcessor.h index d7b1617268e207207bb96c07cb8c93b048d10562..1bd477930074e2dd5eb961205c98c0ffdda0aba3 100755 --- a/Source/Processors/GenericProcessor/GenericProcessor.h +++ b/Source/Processors/GenericProcessor/GenericProcessor.h @@ -466,6 +466,24 @@ public: /** Used to get the timestamp for a given buffer, for a given channel. */ uint64 getTimestamp (int channelNumber) const; + /** Used to get the number of samples a specific source generates. + Look by source ID and subprocessor index */ + uint32 getNumSourceSamples(uint16 processorID, uint16 subProcessorIdx) const; + + /** Used to get the number of samples a specific source generates. + Look by full source ID. + @see GenericProcessor::getProcessorFullId(uint16,uint16) */ + uint32 getNumSourceSamples(uint32 fullSourceID) const; + + /** Used to get the current timestamp of a specific source. + Look by source ID and subprocessor index */ + uint64 getSourceTimestamp(uint16 processorID, uint16 subProcessorIdx) const; + + /** Used to get the current timestamp of a specific source. + Look by full source ID. + @see GenericProcessor::getProcessorFullId(uint16,uint16) */ + uint64 getSourceTimestamp(uint32 fullSourceID) const; + /** Used to set the timestamp for a given buffer, for a given source node. */ void setTimestampAndSamples (uint64 timestamp, uint32 nSamples, int subProcessorIdx = 0); @@ -505,10 +523,11 @@ public: { public: DefaultEventInfo(); - DefaultEventInfo(EventChannel::EventChannelTypes type, unsigned int nChans, unsigned int length); + DefaultEventInfo(EventChannel::EventChannelTypes type, unsigned int nChans, unsigned int length, float SampleRate); EventChannel::EventChannelTypes type{ EventChannel::INVALID }; unsigned int nChannels{ 0 }; unsigned int length{ 0 }; + float sampleRate{ 44100 }; }; protected: @@ -538,9 +557,6 @@ protected: Called by createEventChannels(). It is not needed to implement if createEventChannels() is overriden */ virtual void getDefaultEventInfo(Array<DefaultEventInfo>& events, int subProcessorIdx = 0) const; - std::map<uint32, uint32> numSamples; - std::map<uint32, int64> timestamps; - /** Sets whether processor will have behaviour like Source, Sink, Splitter, Utility or Merge */ void setProcessorType (PluginProcessorType processorType); @@ -576,6 +592,9 @@ protected: void updateChannelIndexes(bool updateNodeID = true); private: + std::map<uint32, uint32> numSamples; + std::map<uint32, int64> timestamps; + void createDataChannelsByType(DataChannel::DataChannelTypes type); /** Each processor has a unique integer ID that can be used to identify it.*/ diff --git a/Source/Processors/MessageCenter/MessageCenter.cpp b/Source/Processors/MessageCenter/MessageCenter.cpp index 168aea03b5fdd7149bcac24c815a91b865698dc1..31267d3e11605b4567c1efad4b6cda2d0c770ac1 100644 --- a/Source/Processors/MessageCenter/MessageCenter.cpp +++ b/Source/Processors/MessageCenter/MessageCenter.cpp @@ -29,7 +29,7 @@ //--------------------------------------------------------------------- MessageCenter::MessageCenter() : - GenericProcessor("Message Center"), newEventAvailable(false), isRecording(false), sourceNodeId(0), +GenericProcessor("Message Center"), newEventAvailable(false), isRecording(false), sourceNodeId(0), sourceNodeSubIdx(0), timestampSource(nullptr), lastTime(0), softTimestamp(0) { @@ -46,11 +46,9 @@ MessageCenter::~MessageCenter() void MessageCenter::addSpecialProcessorChannels(Array<EventChannel*>& channels) { - EventChannel* chan = new EventChannel(EventChannel::TEXT, 1, MAX_MSG_LENGTH, this, 0); + EventChannel* chan = new EventChannel(EventChannel::TEXT, 1, MAX_MSG_LENGTH, getGlobalSampleRate(), this, 0); chan->setName("GUI Messages"); chan->setDescription("Messages from the GUI Message Center"); - if (sourceNodeId) - chan->setSampleRate(static_cast<GenericProcessor*>(AccessClass::getProcessorGraph()->getNodeForId(sourceNodeId)->getProcessor())->getSampleRate()); channels.add(chan); eventChannelArray.add(new EventChannel(*chan)); updateChannelIndexes(); @@ -110,9 +108,15 @@ bool MessageCenter::disable() return true; } -void MessageCenter::setSourceNodeId(int id) +void MessageCenter::setSourceNodeId(int id, int sub) { sourceNodeId = id; + sourceNodeSubIdx = sub; + AudioProcessorGraph::Node* node = AccessClass::getProcessorGraph()->getNodeForId(sourceNodeId); + if (node) + { + timestampSource = static_cast<GenericProcessor*>(node->getProcessor()); + } } int MessageCenter::getSourceNodeId() @@ -120,18 +124,31 @@ int MessageCenter::getSourceNodeId() return sourceNodeId; } -int64 MessageCenter::getTimestamp(bool softwareTime) +int MessageCenter::getSourceSubIdx() { - if (!softwareTime && sourceNodeId > 0) - return timestampSource->getTimestamp(0); + return sourceNodeSubIdx; +} + +int64 MessageCenter::getGlobalTimestamp(bool softwareTime) +{ + if (!softwareTime && sourceNodeId > 0) + return timestampSource->getSourceTimestamp(sourceNodeId, sourceNodeSubIdx); else return (softTimestamp); } +float MessageCenter::getGlobalSampleRate() +{ + if (sourceNodeId > 0) + return timestampSource->getSampleRate(sourceNodeSubIdx); + else + return Time::getHighResolutionTicksPerSecond(); +} + void MessageCenter::process(AudioSampleBuffer& buffer) { softTimestamp = Time::getHighResolutionTicks() - lastTime; - setTimestampAndSamples(getTimestamp(), 0); + setTimestampAndSamples(getGlobalTimestamp(), 0); if (needsToSendTimestampMessage) { MidiBuffer& eventBuffer = *AccessClass::ExternalProcessorAccessor::getMidiBuffer(this); @@ -151,7 +168,7 @@ void MessageCenter::process(AudioSampleBuffer& buffer) eventString = eventString.dropLastCharacters(eventString.length() - MAX_MSG_LENGTH); - TextEventPtr event = TextEvent::createTextEvent(getEventChannel(0), getTimestamp(), eventString); + TextEventPtr event = TextEvent::createTextEvent(getEventChannel(0), getGlobalTimestamp(), eventString); addEvent(getEventChannel(0), event, 0); newEventAvailable = false; diff --git a/Source/Processors/MessageCenter/MessageCenter.h b/Source/Processors/MessageCenter/MessageCenter.h index 0090719a35e24b455a99c09b2637ec82fea883d4..5413436ab1942475689d2580f6f83fe972e9a968 100644 --- a/Source/Processors/MessageCenter/MessageCenter.h +++ b/Source/Processors/MessageCenter/MessageCenter.h @@ -75,13 +75,15 @@ public: needsToSendTimestampMessage = false; } - void setSourceNodeId(int id); + void setSourceNodeId(int id, int sub); int getSourceNodeId(); + int getSourceSubIdx(); void addSourceProcessor(GenericProcessor* p); void removeSourceProcessor(GenericProcessor* p); - int64 getTimestamp(bool softwareTime = false); + int64 getGlobalTimestamp(bool softwareTime = false); + float getGlobalSampleRate(); void addSpecialProcessorChannels(Array<EventChannel*>& channel); private: @@ -89,7 +91,8 @@ private: bool newEventAvailable; bool isRecording; int sourceNodeId; - GenericProcessor* timestampSource; + int sourceNodeSubIdx; + const GenericProcessor* timestampSource; int64 lastTime, softTimestamp; bool needsToSendTimestampMessage; diff --git a/Source/Processors/MessageCenter/MessageCenterEditor.cpp b/Source/Processors/MessageCenter/MessageCenterEditor.cpp index b32f0d4e7cd45a9c0d4b882485bb67fd96b740bb..83da998a85761e48174fe0a4e7ac4eb26823e8ed 100644 --- a/Source/Processors/MessageCenter/MessageCenterEditor.cpp +++ b/Source/Processors/MessageCenter/MessageCenterEditor.cpp @@ -170,11 +170,6 @@ void MessageCenterEditor::resized() sendMessageButton->setBounds(getWidth()-50, 5, 45, getHeight()-10); } -int64 MessageCenterEditor::getTimestamp(bool softwareTimestamp) -{ - return messageCenter->getTimestamp(softwareTimestamp); -} - void MessageCenterEditor::actionListenerCallback(const String& message) { @@ -189,6 +184,7 @@ void MessageCenterEditor::saveStateToXml(XmlElement* xml) { XmlElement* messageEditorState = xml->createNewChildElement("MESSAGECENTER"); messageEditorState->setAttribute("sourceNodeId",messageCenter->getSourceNodeId()); + messageEditorState->setAttribute("sourceSubProcessorIdx", messageCenter->getSourceSubIdx()); } void MessageCenterEditor::loadStateFromXml(XmlElement* xml) @@ -197,7 +193,7 @@ void MessageCenterEditor::loadStateFromXml(XmlElement* xml) { if (xmlNode->hasTagName("MESSAGECENTER")) { - messageCenter->setSourceNodeId(xmlNode->getIntAttribute("sourceNodeId")); + messageCenter->setSourceNodeId(xmlNode->getIntAttribute("sourceNodeId"), xmlNode->getIntAttribute("sourceSubProcessorIdx")); } } } @@ -225,22 +221,31 @@ void MessageCenterEditor::mouseDown(const MouseEvent& event) PopupMenu::dismissAllActiveMenus(); sourceMenu->clear(); sourceMenu->addItem(1,"Software timer",true,messageCenter->getSourceNodeId() == 0); + Array<int> sourceIdx; + Array<int> subIdx; for (int i=0; i < sourcesList.size(); i++) { GenericProcessor* p = sourcesList[i]; - sourceMenu->addItem(i+2,p->getName(),true,(p->getNodeId() == messageCenter->getSourceNodeId())); + int numSub = p->getNumSubProcessors(); + for (int j = 0; j < numSub; j++) + { + String text = (numSub > 1) ? p->getName() + "(" + String(j + 1) + ")" : p->getName(); + sourceMenu->addItem(i + 2, text, true, (p->getNodeId() == messageCenter->getSourceNodeId() && j == messageCenter->getSourceSubIdx())); + sourceIdx.add(i); + subIdx.add(j); + } } res = sourceMenu->show(0,50,0,0); if (res > 1) { - GenericProcessor* p = sourcesList[res-2]; - std::cout << "Selecting " << p->getName() << " with id " << p->getNodeId() << " as message source" << std::endl; - messageCenter->setSourceNodeId(p->getNodeId()); + GenericProcessor* p = sourcesList[sourceIdx[res-2]]; + std::cout << "Selecting " << p->getName() << " with id " << p->getNodeId() << " subprocessor: " << subIdx[res-2] <<" as message source" << std::endl; + messageCenter->setSourceNodeId(p->getNodeId(), subIdx[res-2]); } else if (res == 1) { - messageCenter->setSourceNodeId(0); + messageCenter->setSourceNodeId(0,0); } } } diff --git a/Source/Processors/MessageCenter/MessageCenterEditor.h b/Source/Processors/MessageCenter/MessageCenterEditor.h index 126a931dd7fc4130cb84fb03423ff06171895c76..49f4fa68181eb19bd44903b5b8e86f380aca5b81 100644 --- a/Source/Processors/MessageCenter/MessageCenterEditor.h +++ b/Source/Processors/MessageCenter/MessageCenterEditor.h @@ -72,8 +72,6 @@ public: void mouseDown(const MouseEvent& event); - int64 getTimestamp(bool softwareTimestamp = false); - private: void buttonClicked(Button* button); diff --git a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp index 0ba2515f1e82157cae8f686e687afe3af2b04d37..d793803e88b39d614416625025e669f7ccfcce64 100644 --- a/Source/Processors/ProcessorGraph/ProcessorGraph.cpp +++ b/Source/Processors/ProcessorGraph/ProcessorGraph.cpp @@ -109,11 +109,11 @@ void* ProcessorGraph::createNewProcessor(Array<var>& description, int id)//, // by default, all source nodes record automatically processor->setAllChannelsToRecord(); if (processor->isGeneratesTimestamps()) - { + { //If there are no source processors and we add one, set it as default for global timestamps and samplerates getMessageCenter()->addSourceProcessor(processor); if (getMessageCenter()->getSourceNodeId() == 0) { - getMessageCenter()->setSourceNodeId(processor->getNodeId()); + getMessageCenter()->setSourceNodeId(processor->getNodeId(),0); } } } @@ -560,7 +560,7 @@ void ProcessorGraph::removeProcessor(GenericProcessor* processor) } } } - getMessageCenter()->setSourceNodeId(newId); + getMessageCenter()->setSourceNodeId(newId, 0); } } diff --git a/Source/Processors/SourceNode/SourceNode.cpp b/Source/Processors/SourceNode/SourceNode.cpp index 5c388814e7fe709927060419d9e5359542f6bbbf..fa5f74ca13798eab9aa8106566c95c95a1b4b88c 100755 --- a/Source/Processors/SourceNode/SourceNode.cpp +++ b/Source/Processors/SourceNode/SourceNode.cpp @@ -182,8 +182,7 @@ void SourceNode::createEventChannels() nChans = jmin(nChans, 64); //Just 64 TTL channels per source for now if (nChans > 0) { - EventChannel* chan = new EventChannel(EventChannel::TTL, nChans, 0, this, i); - chan->setSampleRate(dataThread->getSampleRate(i)); + EventChannel* chan = new EventChannel(EventChannel::TTL, nChans, 0, dataThread->getSampleRate(i), this, i); eventChannelArray.add(chan); ttlChannels.add(chan); }