Click here to Skip to main content

Leslie Sanford - Professional Profile

@Leslie-Sanford

Summary

63,820
Author
2,559
Authority
3,898
Debator
120
Enquirer
247
Organiser
2,172
Participant
0
Editor
Aside from dabbling in BASIC on his old Atari 1040ST years ago, Leslie's programming experience didn't really begin until he discovered the Internet in the late 90s. There he found a treasure trove of information about two of his favorite interests: MIDI and sound synthesis.
 
After spending a good deal of time calculating formulas he found on the Internet for creating new sounds by hand, he decided that an easier way would be to program the computer to do the work for him. This led him to learn C. He discovered that beyond using programming as a tool for synthesizing sound, he loved programming in and of itself.
 
Eventually he taught himself C++ and C#, and along the way he immersed himself in the ideas of object oriented programming. Like many of us, he gotten bitten by the design patterns bug and a copy of GOF is never far from his hands.
 
Now his primary interest is in creating a complete MIDI toolkit using the C# language. He hopes to create something that will become an indispensable tool for those wanting to write MIDI applications for the .NET framework.
 
Besides programming, his other interests are photography and playing his Les Paul guitars.
Member since Monday, August 5, 2002 (12 years, 2 months)

Contributions

Articles 18 (Legend)
Tech Blogs 0
Messages 1,538 (Master)
Q&A Questions 0
Q&A Answers 0
Tips/Tricks 0
Comments 0

Links

Reputation

For more information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege, and the given member types also gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilverAdmin
Store personal files in your account areaplatinumplatinumSitebuilder, Subeditor, Supporter, Editor, Staff
Have live hyperlinks in your biographybronzebronzebronzebronzebronzebronzesilverSubeditor, Protector, Editor, Staff, Admin
Edit a Question in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Edit an Answer in Q&AsilversilversilversilverYesSubeditor, Protector, Editor, Admin
Delete a Question in Q&AYesSubeditor, Protector, Editor, Admin
Delete an Answer in Q&AYesSubeditor, Protector, Editor, Admin
Report an ArticlesilversilversilversilverSubeditor, Mentor, Protector, Editor, Staff, Admin
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubeditor, Mentor, Protector, Editor, Staff, Admin
Edit other members' articlesSubeditor, Protector, Editor, Admin
Create an article without requiring moderationplatinumSubeditor, Mentor, Protector, Editor, Staff, Admin
Report a forum messagesilversilverbronzeProtector, Editor, Admin
Create a new tagsilversilversilversilverAdmin
Modify a tagsilversilversilversilverAdmin

Actions with a green tick can be performed by this member.


 
GeneralApplying Generic Programming to Runtime Polymorphism PinmemberLeslie Sanford22-Feb-10 10:28 
GeneralGeneric Programming [modified] PinmemberLeslie Sanford19-Jul-08 5:53 
GeneralOff-topic PinmvpRajesh R Subramanian19-Jun-09 12:48 
GeneralRe: Off-topic PinmemberLeslie Sanford19-Jun-09 19:06 
GeneralEnsuring Object State [modified] PinmemberLeslie Sanford27-Jun-07 8:00 
GeneralEvolution of Messaging PinmemberLeslie Sanford19-Oct-06 5:15 
GeneralEnumerators as Synthesizer Components PinmemberLeslie Sanford20-Sep-06 20:53 
GeneralAnonymous Methods as Glue PinmemberLeslie Sanford17-Sep-06 14:30 
GeneralRefining the approach PinmemberLeslie Sanford22-Jan-06 7:52 
GeneralFlowing down the Sink PinmemberLeslie Sanford21-Jan-06 16:45 
GeneralFlow-Based Programming in C# PinmemberLeslie Sanford29-Dec-05 13:07 
In writing my MIDI toolkit, one of the things I've had to deal with is handling the flow of MIDI messages throughout my system. MIDI messages arrive at an input device or are read from a MIDI file track and then flow through the system until reaching their final destination, an output device. How should I structure this? What approach will be the most flexible and extendable?
 
Researching this problem led me to J. Paul Morrison's[^] website on flow-based programming. He has written a book on the subject, and it is available on his website as well as on Amazon[^].
 
I've found his writings fascinating and just what I needed to approach the problem of (re)designing my MIDI toolkit.
 
I'm not going to say a lot about flow-based programming itself; for that, please refer to Mr. Morrison's book. I'll just say that it's simply about using components to handle the flow of information throughout a system. That's an oversimplification, but it's enough to get us started. I should also provide a disclaimer that what follows is my take on how flow-based programming works and can be implemented in C#. In other words, I'm mixing in my own terms and ideas, so don't judge the merits of flow-based programming just on this blog entry alone. Read the book. Smile | :)
 
Ok, how does one implement flow-based programming in C#?
 
There are several ways, and the first thing we should talk about are sources and sinks. A source is a component that is a source of a message, data packet, whatever (I'll refer to the objects that flow through a system from here on out as messages). A sink is a component capable of receiving a message. A component can be a both a source and a sink.
 
Let's create interfaces for sinks and source for a mythical "channel message":
 
public interface IChannelSource
{
    event EventHandler<ChannelEventArgs> ChannelMessageOccurred;
}
 
This interface defines functionality for a source of channel messages. The channel message data is encapsulated in a ChannelEventArgs class. When a class that implements this interface receives, reads, generates, etc. a channel message, it will raise the ChannelMessageOccurred event.
 
Next, let's create a sink for channel messages:
 
public interface IChannelSink
{
    void Connect(IChannelSource source);
    void Disconnect(IChannelSource source);
}
 
This interface defines functionality for connecting to and disconnecting from an IChannelSource. When an IChannelSink is connected to an IChannelSource it receives channel message events from the IChannelSource.
 
We can use generics to make our interfaces more reusable:
 
public interface ISource<T> where T : EventArgs
{
    event EventHandler<T> MessageOccurred;
}
public interface ISink<T> where T : EventArgs
{
    void Connect(ISource<T> source);
    void Disconnect(ISource<T> source);
}
 
A component can be the source of several kinds of messages, and a sink can be capable of receiving several kinds of messages as well. This means that if you have a component that is the source of several kinds of messages, it will need to implement the ISource interface more than once, which means that each implementation after the first one will need to be explicit. This may obfuscate your code more than you'd like, and you may want to bypass using generics in this way and stick to having a seperate interface for each message type.
 
A class implementing the IChannelSink interface would implement the Connect and Disconnect methods as follows:
 
public void Connect(IChannelSource source)
{
    source.ChannelMessageOccurred += new EventHandler<ChannelEventArgs>(HandleChannelMessage);
}
 
public void Disconnect(IChannelSource source)
{
    source.ChannelMessageOccurred -= new EventHandler<ChannelEventArgs>(HandleChannelMessage);
}
 
If our IChannelSink class also implements IDisposable, we may want to keep track of the IChannelSources connected to it so that the class can disconnect from the sources when it is disposed:
 
public void Connect(IChannelSource source)
{
    if(sources.Contains(source))
    {
        return;
    }
 
    source.ChannelMessageOccurred += new EventHandler<ChannelEventArgs>(HandleChannelMessage);
 
    sources.Add(source);
}
 
public void Disconnect(IChannelSource source)
{
    source.ChannelMessageOccurred -= new EventHandler<ChannelEventArgs>(HandleChannelMessage);
 
    sources.Remove(source);
}
 
public void Dispose()
{
    foreach(IChannelSource source in sources)
    {
        source.ChannelMessageOccurred -= new EventHandler<ChannelEventArgs>(HandleChannelMessage);
    }
}
 
This extra infrastructure automates disconnecting from sources when a sink is being disposed. Sources do not have to be explicitely disconnected by a third party.
 
Earlier, we created source and sink interfaces for channel messages. Assume that we also have source and sink interfaces for several other kinds of messages, e.g. Meta, SysEx, SysRealtime (those of you familiar with MIDI will recoginize these message names. If you aren't familiar with MIDI, don't worry about it. The important point is that there are several kinds of messages).
 
What we're doing with the above interfaces is using events to fascilitate flow-based programming. The beauty of this approach is that several sinks can be connected to the same source. And each sink can do something different with the message it receives.
 
For example, imagine a MIDI application in which notes are received by an input device. This input device is a source of note messages. A component capable of transposing the notes it receives up or down in pitch is connected to the input device.
 
After transposing the note messages, it passes the altered message along to the next sink. This sink could be an output device or yet another component capable of transforming note messages in some other way. At the same time, our output device could be connected to the input device so that it also receives the note message as well. Thus the original note as well as the transposed note are mixed together at the output device.
 
It's important to note (no pun intended) that messages should be immutable. You don't want one component altering the original message and that alteration affecting unrelated components that receive the same message object. Each time a component alters a message, it's not changing the original message but creating a new message that represents the altered message.
 

A Simpler Way

 
Well, there is a more straightforward way to achieve flow-based programming using delegates and events. You simply make a classes event handlers public. Say you have one class that has an event:
 
public class SomeClass
{
    public event EventHandler SomethingOccurred;
}
 
And another class capable of handling the event:
 
public class AnotherClass
{
    public void Send(object sender, EventArgs e)
    {
        // Do stuff...
    }
}
 
And you can connect the two like this:
 
SomeClass sc = new SomeClass();
AnotherClass ac = new AnotherClass();
 
sc.SomethingOccurred += as.Send;
 
The advantage to this approach is that there is no need for source or sink interfaces. There is less coupling with this approach. As long as the method matches the required delegate type for the event, they can be connected.
 
There are two disadvantages:
 
One, you have the event handler public. This may look strange to clients. An event handler doesn't look like a normal method. So there should be some understanding of the purpose behind making the event handler public.
 
As an aside, this is one of those instances in which I really dislike the .NET Framework event convention. I'd rather bypass the convention and have the methods look normal without the "object sender, EventArgs e" noise. You're mileage may vary.
 
Two, if the class receiving the event implements IDisposable, extra care should be taken to disconnect the class from the event before it is disposed so that when the event is raised, it's event handler is not called. Basically, the class responsible for connecting the two classes should be responsible for disposing of the classes and disconnecting them.
 
This simpler approach is the one I'm now leaning towards in implementing flow-based programming
 
-- modified at 2:05 Sunday 15th January, 2006
GeneralCombining Visitor, Observer, and Iterator PinmemberLeslie Sanford25-Dec-05 21:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.


Advertise | Privacy | Mobile
Web04 | 2.8.141022.2 | Last Updated 24 Oct 2014
Copyright © CodeProject, 1999-2014
All Rights Reserved. Terms of Service
Layout: fixed | fluid