//See IDryMssEventInputPort
 public void ReceiveDryMssEvent(MssEvent mssEvent)
 {
     if (DryMssEventRecieved != null)
     {
         DryMssEventRecieved(mssEvent);
     }
 }
Beispiel #2
0
 //See IDryMssEventInputPort
 public void ReceiveDryMssEvent(MssEvent mssEvent)
 {
     if (DryMssEventRecieved != null)
     {
         DryMssEventRecieved(mssEvent);
     }
 }
        /// <summary>
        ///     Midi events are received from the host on this method and are either send to the DryMssEventRelay or
        ///     stored internally.
        /// </summary>
        /// <param name="events">A collection with midi events. Never null.</param>
        /// <remarks>
        ///     Note that some hosts will only receieve midi events during audio processing.
        ///     See also <see cref="IVstPluginAudioProcessor"/>.
        /// </remarks>
        public void Process(VstEventCollection vstEvents)
        {
            if (vstEvents == null || vstEvents.Count == 0 || this.vstHost == null)
            {
                return;
            }

            // NOTE: other types of events could be in the collection!
            foreach (VstEvent evnt in vstEvents)
            {
                if (evnt.EventType == VstEventTypes.MidiEvent)
                {
                    VstMidiEvent midiEvent = (VstMidiEvent)evnt;
                    MssEvent     mssEvent  = ConvertVstMidiEventToMssEvent(midiEvent,
                                                                           this.SampleTimeAtStartOfProcessingCycle,
                                                                           this.hostInfoOutputPort.SampleRate);

                    if (mssEvent == null)
                    {
                        outEvents.Add(evnt);
                    }
                    else
                    {
                        this.dryMssEventInputPort.ReceiveDryMssEvent(mssEvent);
                    }
                }
                else
                {
                    // non VstMidiEvent
                    outEvents.Add(evnt);
                }
            }
        }
        /// <summary>
        /// Listens for GeneratorToggle messages
        /// </summary>
        protected void WetMssEventOutputPort_WetMssEventReceived(MssEvent mssEvent)
        {
            if (mssEvent.mssMsg.Type == MssMsgType.GeneratorModify)
            {
                this.generatorMappingMgr.RunFuncOnMappingEntry(mssEvent.mssMsg.Data1AsInt, genEntry =>
                {
                    switch ((GenOperation)mssEvent.mssMsg.Data2)
                    {
                    case GenOperation.OnOff:
                        if (mssEvent.mssMsg.Data3 > 0)
                        {
                            genEntry.GenConfigInfo.Enabled = true;
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = double.NaN;
                            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate  = mssEvent.sampleTime - SAMPLES_PER_GENERATOR_UPDATE;
                            genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                            genEntry.GenHistoryInfo.Initialized   = true;
                        }
                        else
                        {
                            genEntry.GenConfigInfo.Enabled = false;
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = double.NaN;
                            genEntry.GenHistoryInfo.Initialized = false;
                        }
                        break;

                    case GenOperation.PlayPause:
                        if (mssEvent.mssMsg.Data3 > 0)
                        {
                            genEntry.GenConfigInfo.Enabled = true;
                            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate = mssEvent.sampleTime - SAMPLES_PER_GENERATOR_UPDATE;
                            genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                            genEntry.GenHistoryInfo.Initialized   = true;
                        }
                        else
                        {
                            genEntry.GenConfigInfo.Enabled      = false;
                            genEntry.GenHistoryInfo.Initialized = false;
                        }
                        break;

                    case GenOperation.SetPosition:
                        if (double.IsNaN(mssEvent.mssMsg.Data3) == false)
                        {
                            //TODO: The value that get's set here will not actually be used. Instead the next value will be used.
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = mssEvent.mssMsg.Data3;
                        }
                        break;

                    default:
                        Debug.Assert(false, "Unknown generator modify operation.");
                        break;
                    }
                });
            }
        }
        public void ConvertMssEventToVstMidiEvent_NoValidConversion_ReturnsNull()
        {
            MidiHandlerProtectedWrapper midiHandler = Factory_MidiHandler_Basic();

            long sampleTime = 123456;

            MssEvent internalEvent = new MssEvent();

            internalEvent.mssMsg     = new MssMsg(MssMsgType.Generator, 0, 0, 0);
            internalEvent.sampleTime = sampleTime;

            VstMidiEvent convertedEvent = midiHandler.ConvertMssEventToVstMidiEventWrapper(
                internalEvent, sampleTime, this.hostInfoOutputPort.SampleRate);

            Assert.IsNull(convertedEvent);
        }
        /// <summary>
        ///     Attempts to create an MssEvent representation of <paramref name="midiEvent"/>.
        /// </summary>
        /// <returns>The MssEvent representation of midiEvent or null of there is no valid conversion.</returns>
        protected static MssEvent ConvertVstMidiEventToMssEvent(VstMidiEvent midiEvent,
                                                                long sampleTimeAtStartOfProcessingCycle,
                                                                double sampleRate)
        {
            MssEvent mssEvent = new MssEvent();

            //Sets the sample time for mssEvent.
            mssEvent.sampleTime = sampleTimeAtStartOfProcessingCycle + midiEvent.DeltaFrames;

            MssMsgType msgType = MidiUtil.GetMssTypeFromMidiData(midiEvent.Data);

            mssEvent.mssMsg.Type = msgType;

            //If msgType is "Unsupported" then midiEvent cannot be represented as an MssEvent
            if (msgType == MssMsgType.Unsupported)
            {
                return(null);
            }

            //Sets mssEvent's Data1 (midi channel).
            //Adds one because channels in an MssMsg start at 1 but channels in a VstMidiEvent start at 0
            mssEvent.mssMsg.Data1 = (midiEvent.Data[0] & 0x0F) + 1;

            //Sets mssEvent's Data2 and Data3 (the midi message's data bytes).
            if (msgType == MssMsgType.PitchBend)
            {
                //The two data bytes for pitch bend are used to store one 14-bit number so this number is stored in
                //mssEvent.Data3 and mssEvent.Data2 is not used.
                mssEvent.mssMsg.Data2 = MssMsgUtil.UNUSED_MSS_MSG_DATA;

                //data1 contains the least significant 7 bits of the pitch bend value and data2 contains the most
                //significant 7 bits
                mssEvent.mssMsg.Data3 = (midiEvent.Data[2] << 7) + midiEvent.Data[1];
            }
            else if (msgType == MssMsgType.ChanAftertouch)
            {
                mssEvent.mssMsg.Data2 = MssMsgUtil.UNUSED_MSS_MSG_DATA;
                mssEvent.mssMsg.Data3 = midiEvent.Data[1];
            }
            else
            {
                mssEvent.mssMsg.Data2 = midiEvent.Data[1];
                mssEvent.mssMsg.Data3 = midiEvent.Data[2];
            }

            return(mssEvent);
        }
        /// <summary>
        ///     Attempts to create a VstMidiEvent representation of <paramref name="mssEvent"/>.
        /// </summary>
        /// <returns>The VstMidiEvent representation of mssEvent or null of there is no valid conversion.</returns>
        protected static VstMidiEvent ConvertMssEventToVstMidiEvent(MssEvent mssEvent,
                                                                    long sampleTimeAtStartOfProcessingCycle,
                                                                    double sampleRate)
        {
            Debug.Assert(mssEvent.sampleTime >= sampleTimeAtStartOfProcessingCycle);

            //Calculate delta frames
            int deltaFrames = (int)(mssEvent.sampleTime - sampleTimeAtStartOfProcessingCycle);

            byte[] midiData = new byte[3];

            //Get status half of first byte.
            byte statusByte;

            if (MidiUtil.GetStatusFromMssMsgType(mssEvent.mssMsg.Type, out statusByte) == false)
            {
                return(null);
            }

            //subtract 1 becasue channels are 0 based in midi but 1 based in mss
            byte channelByte = (byte)(mssEvent.mssMsg.Data1AsInt - 1);

            midiData[0] = (byte)(statusByte | channelByte);

            if (mssEvent.mssMsg.Type == MssMsgType.PitchBend)
            {
                //most significant bits
                int MsbVal = (mssEvent.mssMsg.Data3AsInt) >> 7;
                midiData[2] = (byte)MsbVal;
                midiData[1] = (byte)((mssEvent.mssMsg.Data3AsInt) - (MsbVal << 7));
            }
            else if (mssEvent.mssMsg.Type == MssMsgType.ChanAftertouch)
            {
                midiData[1] = (byte)mssEvent.mssMsg.Data3AsInt;
                midiData[2] = 0;
            }
            else
            {
                midiData[1] = (byte)mssEvent.mssMsg.Data2AsInt;
                midiData[2] = (byte)mssEvent.mssMsg.Data3AsInt;
            }
            VstMidiEvent midiEvent = new VstMidiEvent(deltaFrames, 0, 0, midiData, 0, 0);

            return(midiEvent);
        }
        public void ReceiveWetMssEvent(MssEvent mssEvent)
        {
            Debug.Assert(mssEvent != null);

            lock (this.relayLock)
            {
                this.mssEventBuffer.Add(mssEvent);
            }

            if (WetMssEventsReceived != null)
            {
                WetMssEventsReceived(mssEvent);
            }

            if (this.OnlySendOnProcessingCycleEnd == false && SendingWetMssEvents != null)
            {
                SendingWetMssEvents(transferMssEventBufferContentToNewList(), 0);
            }
        }
        public void ReceiveWetMssEvent(MssEvent mssEvent)
        {
            Debug.Assert(mssEvent != null);

            lock (this.relayLock)
            {
                this.mssEventBuffer.Add(mssEvent);
            }

            if (WetMssEventsReceived != null)
            {
                WetMssEventsReceived(mssEvent);
            }

            if (this.OnlySendOnProcessingCycleEnd == false && SendingWetMssEvents != null)
            {
                SendingWetMssEvents(transferMssEventBufferContentToNewList(), 0);
            }
        }
        //*******************************Helpers*******************************


        //if testMssToMidi is true then this method tests ConvertMssEventToVstMidiEvent. Otherwise this method tests ConvertVstMidiEventToMssEvent
        protected void Test_MsgConversion(MssMsgType msgType, int midiChannel, int midiParam1, int midiParam2, bool testMssToMidi)
        {
            long sampleTimeAtStartOfCycle = 12345;
            int  deltaFrames = 789;

            MidiHandlerProtectedWrapper midiHandler = Factory_MidiHandler_Basic();

            MssEvent mssEvent = new MssEvent();

            if (msgType == MssMsgType.PitchBend)
            {
                mssEvent.mssMsg = new MssMsg(msgType, midiChannel, MssMsgUtil.UNUSED_MSS_MSG_DATA, (midiParam2 << 7) + midiParam1);
            }
            else
            {
                mssEvent.mssMsg = new MssMsg(msgType, midiChannel, midiParam1, midiParam2);
            }
            mssEvent.sampleTime = sampleTimeAtStartOfCycle + deltaFrames;

            byte[]       midiData  = MidiUtil.CreateMidiData(msgType, midiChannel, (byte)midiParam1, (byte)midiParam2);
            VstMidiEvent midiEvent = new VstMidiEvent(deltaFrames, 0, 0, midiData, 0, 0x00);

            if (testMssToMidi == true)
            {
                MssEvent convertedMssEvent = midiHandler.ConvertVstMidiEventToMssEventWrapper(
                    midiEvent, sampleTimeAtStartOfCycle, this.hostInfoOutputPort.SampleRate);
                Assert.AreEqual(mssEvent, convertedMssEvent);
            }
            else
            {
                VstMidiEvent ConvertedMidiEvent = midiHandler.ConvertMssEventToVstMidiEventWrapper(
                    mssEvent, sampleTimeAtStartOfCycle, this.hostInfoOutputPort.SampleRate);
                Assert.AreEqual(midiEvent.Data, ConvertedMidiEvent.Data);
                Assert.AreEqual(midiEvent.DeltaFrames, ConvertedMidiEvent.DeltaFrames);
            }
        }
 public VstMidiEvent ConvertMssEventToVstMidiEventWrapper(MssEvent mssEvent,
                                                          long processingCycleStartTime,
                                                          double sampleRate)
 {
     return(ConvertMssEventToVstMidiEvent(mssEvent, processingCycleStartTime, sampleRate));
 }
        /// <summary>
        /// Each call to GenerateEvent() will generate the next event for genEntry. The
        /// next event's sample time will be set to SAMPLES_PER_GENERATOR_UPDATE more then the
        /// sample time for the event that was previously created for genEntry. Returns null if
        /// no event currently needs to be generated for this generator.
        /// </summary>
        /// <remarks>
        /// Preconditions: genEntry is initilaized and enabled.
        /// </remarks>
        protected MssEvent GenerateEvent(IGeneratorMappingEntry genEntry)
        {
            Debug.Assert(genEntry.GenConfigInfo.Enabled == true);
            Debug.Assert(genEntry.GenHistoryInfo.Initialized == true);

            //Stores the relative position through the period that the next event for genEntry
            //will occur.
            double relPosInPeriod;
            bool   reachedEndOfPeriod;

            if (genEntry.GenConfigInfo.PeriodType == GenPeriodType.BeatSynced)
            {
                //TODO: this code does not work. relPosInPeriod can be negative which screws everything up.
                //It also doesn't take looping into account
                relPosInPeriod = GetRelPosInBeatSyncedPeriod(genEntry.GenConfigInfo,
                                                             genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate +
                                                             SAMPLES_PER_GENERATOR_UPDATE);
                reachedEndOfPeriod = false;
            }
            else if (genEntry.GenConfigInfo.PeriodType == GenPeriodType.Time ||
                     genEntry.GenConfigInfo.PeriodType == GenPeriodType.Bars)
            {
                int    periodSizeInSamples     = GetPeriodSizeInSamples(genEntry.GenConfigInfo);
                double RelativeperiodIncrement =
                    ((double)SAMPLES_PER_GENERATOR_UPDATE) / ((double)periodSizeInSamples);

                //PercentThroughPeriodOnLastUpdate will be NaN the first time it's generator is updated.
                if (double.IsNaN(genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate) == false)
                {
                    relPosInPeriod = genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate +
                                     RelativeperiodIncrement;
                }
                else
                {
                    relPosInPeriod = 0;
                }
                reachedEndOfPeriod = (relPosInPeriod >= 1);
                //Remove the interger component of relPosInPeriod so that it is between 0 and 1.
                relPosInPeriod = relPosInPeriod % 1;
            }
            else
            {
                //Unexpected period type
                Debug.Assert(false);
                return(null);
            }

            Debug.Assert(relPosInPeriod >= 0);

            //If this generator is not set to loop and it has finished one full period then disable it
            //it and return null so that no more events are sent.
            if (genEntry.GenConfigInfo.Loop == false && reachedEndOfPeriod)
            {
                genEntry.GenConfigInfo.Enabled = false;
                genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = 0;
                genEntry.GenHistoryInfo.Initialized = false;
                return(null);
            }

            MssMsg relPosMsg = CreateInputMsgForGenMappingEntry(genEntry, relPosInPeriod);
            //Processing the relPosMsg should convert it into a Generator message and apply the
            //equation for this generator to it's data3.
            List <MssMsg> processedMsgList = this.mssMsgProcessor.ProcessMssMsg(relPosMsg).ToList();

            //Sample time for new event.
            long updatedSampleTime = genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate +
                                     SAMPLES_PER_GENERATOR_UPDATE;

            //Update the generator's history info.
            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate  = updatedSampleTime;
            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = relPosInPeriod;

            //Count could equal 0 if data 3 has been mapped above 1 or mapped to NaN.
            Debug.Assert(processedMsgList.Count <= 1);

            if (processedMsgList.Count == 0)
            {
                genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                return(null);
            }
            //Don't bother sending the event if it is the same as the last one sent.
            else if (processedMsgList[0].Data3 == genEntry.GenHistoryInfo.LastValueSent)
            {
                return(null);
            }
            else
            {
                //Initialize the fields in the new event and return it.
                MssEvent generatedEvent = new MssEvent();
                generatedEvent.mssMsg     = processedMsgList[0];
                generatedEvent.sampleTime = updatedSampleTime;

                genEntry.GenHistoryInfo.LastValueSent = generatedEvent.mssMsg.Data3;

                return(generatedEvent);
            }
        }
        /// <summary>
        /// Iterates through the entries in generatorMappingManager, creates all nessary Mss
        /// Events, and send them to dryMssEventInputPort. This function will be called before
        /// each audio processing cycle ends.
        /// </summary>
        /// <param name="sampleTimeAtEndOfCycle">
        /// The sample time for when the current processing cycle will end.
        /// </param>
        protected void HostInfoOutputPort_BeforeProcessingCycleEnd(long sampleTimeAtEndOfCycle)
        {
            List <int> genEntryIdList = this.generatorMappingMgr.GetEntryIdList();

            foreach (int genEntryId in genEntryIdList)
            {
                this.generatorMappingMgr.RunFuncOnMappingEntry(genEntryId, (genEntry) => {
                    //In order to generate events we need to some information about the
                    //host. If any of this information hasn't been initialized yet then just don't
                    //generate anything for this generator.
                    switch (genEntry.GenConfigInfo.PeriodType)
                    {
                    case GenPeriodType.BeatSynced:
                        if (this.hostInfoOutputPort.TempoIsInitialized == false ||
                            this.hostInfoOutputPort.TimeSignatureIsInitialized == false ||
                            this.hostInfoOutputPort.CalculatedBarZeroIsInitialized == false ||
                            this.hostInfoOutputPort.TransportPlayingIsInitialized == false)
                        {
                            return;
                        }

                        //If the host stopped playing then nothing will be generated for this generator
                        //and it will need to be reinitialized when the host starts playing again.
                        if (this.hostInfoOutputPort.TransportPlaying == false)
                        {
                            genEntry.GenHistoryInfo.Initialized = false;
                            return;
                        }
                        break;

                    case GenPeriodType.Time:

                        if (this.hostInfoOutputPort.TimeSignatureIsInitialized == false)
                        {
                            return;
                        }
                        break;

                    case GenPeriodType.Bars:
                        if (this.hostInfoOutputPort.TempoIsInitialized == false ||
                            this.hostInfoOutputPort.TimeSignatureIsInitialized == false)
                        {
                            return;
                        }
                        break;

                    default:
                        Debug.Assert(false);
                        break;
                    }

                    //Only enabled generators should generate anything
                    if (genEntry.GenConfigInfo.Enabled == true)
                    {
                        //Initializes the history info for this generator if it has not already been
                        //initialized.
                        if (genEntry.GenHistoryInfo.Initialized == false)
                        {
                            //The GenHistoryInfo will be initialized such that it appears to have been
                            //updated on the last processing cycle.
                            genEntry.GenHistoryInfo.InitAllMembers(
                                this.sampleTimeAtEndOfLastCycle,
                                genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate,
                                double.NaN);
                        }

                        //Generate events for this generator until the next event coming from this
                        //generator would fall into the next audio processing cycle. The enabled status
                        //of a generator can change in a call to GenerateEvent() so we need to ensure
                        //that this generator is still enabled.
                        while (genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate +
                               SAMPLES_PER_GENERATOR_UPDATE <= sampleTimeAtEndOfCycle &&
                               genEntry.GenConfigInfo.Enabled == true)
                        {
                            MssEvent generatedEvent = GenerateEvent(genEntry);
                            if (generatedEvent != null)
                            {
                                //This can be true in the following scenario.
                                //  1. At the end of a cycle a generator generates a genmodify message
                                //  2. The gen modify event gets caught by this class when all the wet events are sent out
                                //     which enables a generator and sets it's SampleTimeAtLastGeneratorUpdate to one cycle
                                //     before the time of the gen modify event.
                                //  3. The newly enabled generator doesn't get generated this cycle because HostInfoOutputPort_BeforeProcessingCycleEnd
                                //     has already finished.
                                //  4. On the next cycle the newly enabled generator starts getting generated but it has
                                //     events that should have gone out last cycle.
                                //
                                // Just ignoreing the events is not a great solution as they really should have been sent
                                // out last cycle. But this does give us an easy way to prevent infinate feedback loops.
                                if (this.sampleTimeAtEndOfLastCycle >= generatedEvent.sampleTime)
                                {
                                    continue;
                                }

                                this.dryMssEventInputPort.ReceiveDryMssEvent(generatedEvent);
                            }
                        }
                    }
                    else
                    {
                        genEntry.GenHistoryInfo.Initialized = false;
                    }
                });
            }
        }
        /// <summary>
        ///     Event handler for MssEvents coming
        /// </summary>
        /// <param name="dryMssEvent"></param>
        protected void dryMssEventOutputPort_DryMssEventRecieved(MssEvent dryMssEvent)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action<MssEvent>(dryMssEventOutputPort_DryMssEventRecieved), dryMssEvent);
                return;
            }

            MssMsg curMsg = dryMssEvent.mssMsg;

            if (this.learnMode != LearnMode.Off &&
                curMsg.Type != MssMsgType.NoteOff &&
                curMsg.Type != MssMsgType.Generator)
            {
                if (curMsg.Type == MssMsgType.NoteOn)
                {
                    curMsg.Type = MssMsgType.Note;
                }

                ComboBox curTypeCombo = null;

                if (this.learnMode == LearnMode.In)
                {
                    curTypeCombo = this.inTypeCombo;
                }
                else if (this.learnMode == LearnMode.Out)
                {
                    curTypeCombo = this.outTypeCombo;
                }
                else
                {
                    //Unknown learn mode
                    Debug.Assert(false);
                    return;
                }

                string newTypeName = MssMsg.MssMsgTypeNames[(int)curMsg.Type];
                //Ensure that the type of the message recieved can be assigned to the current type combo box.
                if (curTypeCombo.FindStringExact(newTypeName) != -1)
                {
                    //This will trigger the corresponding MsgMetadata to be regenerated.
                    curTypeCombo.Text = newTypeName;
                }
                else
                {
                    return;
                }

                MssMsgRangeEntryMetadata curEntryMetadata;
                if(this.learnMode == LearnMode.In)
                {
                    curEntryMetadata = this.inMsgMetadata;
                }
                else //(this.learnMode == LearnMode.Out)
                {
                    curEntryMetadata = this.outMsgMetadata;
                }

                IMssMsgRange learnedRange = new MssMsgRange();
                learnedRange.MsgType = curMsg.Type;

                //If the type is the same as the last message and either data1 or data2 has changed
                //then treat this as a range of notes.
                if (this.lastMsgReceived != null &&
                    this.lastMsgReceived.Type == curMsg.Type &&
                    (this.lastMsgReceived.Data1 != curMsg.Data1 ||
                    this.lastMsgReceived.Data2 != curMsg.Data2))
                {
                    learnedRange.Data1RangeBottom = Math.Min(this.lastMsgReceived.Data1, curMsg.Data1);
                    learnedRange.Data1RangeTop = Math.Max(this.lastMsgReceived.Data1, curMsg.Data1);
                    learnedRange.Data2RangeBottom = Math.Min(this.lastMsgReceived.Data2, curMsg.Data2);
                    learnedRange.Data2RangeTop = Math.Max(this.lastMsgReceived.Data2, curMsg.Data2);
                }
                else
                {
                    learnedRange.Data1RangeBottom = curMsg.Data1;
                    learnedRange.Data1RangeTop = curMsg.Data1;
                    learnedRange.Data2RangeBottom = curMsg.Data2;
                    learnedRange.Data2RangeTop = curMsg.Data2;
                }

                curEntryMetadata.UseExistingMsgRange(learnedRange);

                if (this.learnMode == LearnMode.In)
                {
                    inMsgMetadata.ValidateEntryField1();
                    inMsgMetadata.ValidateEntryField2();
                }
                else //(this.learnMode == LearnMode.Out)
                {
                    outMsgMetadata.ValidateEntryField1();
                    outMsgMetadata.ValidateEntryField2();
                }

                this.lastMsgReceived = curMsg;
            }
        }
 public void dryMssEventInputPort_DryMssEventRecieved(MssEvent dryMssEvent)
 {
     numDryEventsReceivedByRelay++;
 }
        /// <summary>
        /// Each call to GenerateEvent() will generate the next event for genEntry. The 
        /// next event's sample time will be set to SAMPLES_PER_GENERATOR_UPDATE more then the 
        /// sample time for the event that was previously created for genEntry. Returns null if 
        /// no event currently needs to be generated for this generator.
        /// </summary>
        /// <remarks>
        /// Preconditions: genEntry is initilaized and enabled.
        /// </remarks>
        protected MssEvent GenerateEvent(IGeneratorMappingEntry genEntry)
        {
            Debug.Assert(genEntry.GenConfigInfo.Enabled == true);
            Debug.Assert(genEntry.GenHistoryInfo.Initialized == true);

            //Stores the relative position through the period that the next event for genEntry
            //will occur.
            double relPosInPeriod;
            bool reachedEndOfPeriod;
            if (genEntry.GenConfigInfo.PeriodType == GenPeriodType.BeatSynced)
            {
                //TODO: this code does not work. relPosInPeriod can be negative which screws everything up.
                //It also doesn't take looping into account
                relPosInPeriod = GetRelPosInBeatSyncedPeriod(genEntry.GenConfigInfo,
                        genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate +
                        SAMPLES_PER_GENERATOR_UPDATE);
                reachedEndOfPeriod = false;
            }
            else if (genEntry.GenConfigInfo.PeriodType == GenPeriodType.Time ||
                     genEntry.GenConfigInfo.PeriodType == GenPeriodType.Bars)
            {
                int periodSizeInSamples = GetPeriodSizeInSamples(genEntry.GenConfigInfo);
                double RelativeperiodIncrement =
                        ((double)SAMPLES_PER_GENERATOR_UPDATE) / ((double)periodSizeInSamples);

                //PercentThroughPeriodOnLastUpdate will be NaN the first time it's generator is updated.
                if (double.IsNaN(genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate) == false)
                {
                    relPosInPeriod = genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate +
                                            RelativeperiodIncrement;
                }
                else {
                    relPosInPeriod = 0;
                }
                reachedEndOfPeriod = (relPosInPeriod >= 1);
                //Remove the interger component of relPosInPeriod so that it is between 0 and 1.
                relPosInPeriod = relPosInPeriod % 1;
            }
            else
            {
                //Unexpected period type
                Debug.Assert(false);
                return null;
            }

            Debug.Assert(relPosInPeriod >= 0);

            //If this generator is not set to loop and it has finished one full period then disable it
            //it and return null so that no more events are sent.
            if (genEntry.GenConfigInfo.Loop == false && reachedEndOfPeriod)
            {
                genEntry.GenConfigInfo.Enabled = false;
                genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = 0;
                genEntry.GenHistoryInfo.Initialized = false;
                return null;
            }

            MssMsg relPosMsg = CreateInputMsgForGenMappingEntry(genEntry, relPosInPeriod);
            //Processing the relPosMsg should convert it into a Generator message and apply the
            //equation for this generator to it's data3.
            List<MssMsg> processedMsgList = this.mssMsgProcessor.ProcessMssMsg(relPosMsg).ToList();

            //Sample time for new event.
            long updatedSampleTime = genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate +
                                      SAMPLES_PER_GENERATOR_UPDATE;

            //Update the generator's history info.
            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate = updatedSampleTime;
            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = relPosInPeriod;

            //Count could equal 0 if data 3 has been mapped above 1 or mapped to NaN.
            Debug.Assert(processedMsgList.Count <= 1);

            if (processedMsgList.Count == 0) {
                genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                return null;
            }
            //Don't bother sending the event if it is the same as the last one sent.
            else if (processedMsgList[0].Data3 == genEntry.GenHistoryInfo.LastValueSent)
            {
                return null;
            }
            else
            {
                //Initialize the fields in the new event and return it.
                MssEvent generatedEvent = new MssEvent();
                generatedEvent.mssMsg = processedMsgList[0];
                generatedEvent.sampleTime = updatedSampleTime;

                genEntry.GenHistoryInfo.LastValueSent = generatedEvent.mssMsg.Data3;

                return generatedEvent;
            }
        }
        /// <summary>
        /// Listens for GeneratorToggle messages
        /// </summary>
        protected void WetMssEventOutputPort_WetMssEventReceived(MssEvent mssEvent)
        {
            if (mssEvent.mssMsg.Type == MssMsgType.GeneratorModify)
            {
                this.generatorMappingMgr.RunFuncOnMappingEntry(mssEvent.mssMsg.Data1AsInt, genEntry =>
                {
                    switch ((GenOperation)mssEvent.mssMsg.Data2)
                    {
                    case GenOperation.OnOff:
                        if (mssEvent.mssMsg.Data3 > 0)
                        {
                            genEntry.GenConfigInfo.Enabled = true;
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = double.NaN;
                            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate = mssEvent.sampleTime - SAMPLES_PER_GENERATOR_UPDATE;
                            genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                            genEntry.GenHistoryInfo.Initialized = true;
                        }
                        else
                        {
                            genEntry.GenConfigInfo.Enabled = false;
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = double.NaN;
                            genEntry.GenHistoryInfo.Initialized = false;
                        }
                        break;

                    case GenOperation.PlayPause:
                        if (mssEvent.mssMsg.Data3 > 0)
                        {
                            genEntry.GenConfigInfo.Enabled = true;
                            genEntry.GenHistoryInfo.SampleTimeAtLastGeneratorUpdate = mssEvent.sampleTime - SAMPLES_PER_GENERATOR_UPDATE;
                            genEntry.GenHistoryInfo.LastValueSent = double.NaN;
                            genEntry.GenHistoryInfo.Initialized = true;
                        }
                        else
                        {
                            genEntry.GenConfigInfo.Enabled = false;
                            genEntry.GenHistoryInfo.Initialized = false;
                        }
                        break;

                    case GenOperation.SetPosition:
                        if (double.IsNaN(mssEvent.mssMsg.Data3) == false)
                        {
                            //TODO: The value that get's set here will not actually be used. Instead the next value will be used.
                            genEntry.GenHistoryInfo.PercentThroughPeriodOnLastUpdate = mssEvent.mssMsg.Data3;
                        }
                        break;

                    default:
                        Debug.Assert(false,"Unknown generator modify operation.");
                        break;
                    }
                });
            }
        }
        /// <summary>
        ///     Event handler for MssEvents coming
        /// </summary>
        /// <param name="dryMssEvent"></param>
        protected void dryMssEventOutputPort_DryMssEventRecieved(MssEvent dryMssEvent)
        {
            if (this.InvokeRequired)
            {
                this.BeginInvoke(new Action <MssEvent>(dryMssEventOutputPort_DryMssEventRecieved), dryMssEvent);
                return;
            }

            MssMsg curMsg = dryMssEvent.mssMsg;

            if (this.learnMode != LearnMode.Off &&
                curMsg.Type != MssMsgType.NoteOff &&
                curMsg.Type != MssMsgType.Generator)
            {
                if (curMsg.Type == MssMsgType.NoteOn)
                {
                    curMsg.Type = MssMsgType.Note;
                }

                ComboBox curTypeCombo = null;

                if (this.learnMode == LearnMode.In)
                {
                    curTypeCombo = this.inTypeCombo;
                }
                else if (this.learnMode == LearnMode.Out)
                {
                    curTypeCombo = this.outTypeCombo;
                }
                else
                {
                    //Unknown learn mode
                    Debug.Assert(false);
                    return;
                }

                string newTypeName = MssMsg.MssMsgTypeNames[(int)curMsg.Type];
                //Ensure that the type of the message recieved can be assigned to the current type combo box.
                if (curTypeCombo.FindStringExact(newTypeName) != -1)
                {
                    //This will trigger the corresponding MsgMetadata to be regenerated.
                    curTypeCombo.Text = newTypeName;
                }
                else
                {
                    return;
                }


                MssMsgRangeEntryMetadata curEntryMetadata;
                if (this.learnMode == LearnMode.In)
                {
                    curEntryMetadata = this.inMsgMetadata;
                }
                else //(this.learnMode == LearnMode.Out)
                {
                    curEntryMetadata = this.outMsgMetadata;
                }

                IMssMsgRange learnedRange = new MssMsgRange();
                learnedRange.MsgType = curMsg.Type;

                //If the type is the same as the last message and either data1 or data2 has changed
                //then treat this as a range of notes.
                if (this.lastMsgReceived != null &&
                    this.lastMsgReceived.Type == curMsg.Type &&
                    (this.lastMsgReceived.Data1 != curMsg.Data1 ||
                     this.lastMsgReceived.Data2 != curMsg.Data2))
                {
                    learnedRange.Data1RangeBottom = Math.Min(this.lastMsgReceived.Data1, curMsg.Data1);
                    learnedRange.Data1RangeTop    = Math.Max(this.lastMsgReceived.Data1, curMsg.Data1);
                    learnedRange.Data2RangeBottom = Math.Min(this.lastMsgReceived.Data2, curMsg.Data2);
                    learnedRange.Data2RangeTop    = Math.Max(this.lastMsgReceived.Data2, curMsg.Data2);
                }
                else
                {
                    learnedRange.Data1RangeBottom = curMsg.Data1;
                    learnedRange.Data1RangeTop    = curMsg.Data1;
                    learnedRange.Data2RangeBottom = curMsg.Data2;
                    learnedRange.Data2RangeTop    = curMsg.Data2;
                }

                curEntryMetadata.UseExistingMsgRange(learnedRange);

                if (this.learnMode == LearnMode.In)
                {
                    inMsgMetadata.ValidateEntryField1();
                    inMsgMetadata.ValidateEntryField2();
                }
                else //(this.learnMode == LearnMode.Out)
                {
                    outMsgMetadata.ValidateEntryField1();
                    outMsgMetadata.ValidateEntryField2();
                }

                this.lastMsgReceived = curMsg;
            }
        }