diff --git a/docs/user/Doxyfile b/docs/user/Doxyfile index 7c55890d27..5bbe1bc535 100644 --- a/docs/user/Doxyfile +++ b/docs/user/Doxyfile @@ -325,8 +325,8 @@ WARN_LOGFILE = # with spaces. # General documentations and extern documentation files -INPUT = . support ppp -INPUT += ../../headers/os/support ../../headers/posix +INPUT = . midi midi2 support ppp +INPUT += ../../headers/os/midi2 ../../headers/os/support ../../headers/posix # ToDo: we should probably make it recursive once we have docs for everything... # PPP @@ -389,7 +389,7 @@ EXAMPLE_RECURSIVE = NO # directories that contain image that are included in the documentation (see # the \image command). -IMAGE_PATH = . +IMAGE_PATH = . midi2 # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program diff --git a/docs/user/midi/midi1intro.dox b/docs/user/midi/midi1intro.dox new file mode 100644 index 0000000000..1719629ffb --- /dev/null +++ b/docs/user/midi/midi1intro.dox @@ -0,0 +1,48 @@ +/*! +\page midi1 The old Midi Kit (libmidi.so) + +The old Midi Kit, or midi1 for short, goes all the way back to DR8, when the +BeOS only ran on BeBoxen. Fortunately for us, it is pretty well documented: + +- Midi +Kit chapter in the online Be Book +- Midi +Kit section of the old Be Developer Library +- Be Newsletter Volume 1, Issue 49 - Introduces the MIDI synth +- Be Newsletter Volume 1, Issue 52 - Follow-up on issue 49 +- Be Newsletter Volume 1, Issue 91 - How to use BSynth +- Be Newsletter Volume 1, Issue 102 - Axe sample code +- Be Newsletter Volume 1, Issue 104 - How to use BMidiPort +- Be Newsletter Volume 2, Issue 23 - EdMidi sample code +- Be Newsletter Volume 2, Issue 37 - How to use the MIDI synth +- Be Newsletter Volume 3, Issue 37 - Whistle sample code + +To summarize, there are four basic MIDI classes: + +- BMidi is the base class for most other classes from the Midi Kit +- BMidiPort can talk to a MIDI hardware port +- BMidiStore can read, write, and perform Standard MIDI files +- BMidiText is a debugging aid that dumps MIDI messages to stdout + +The following classes let you use the Midi Kit's General MIDI synthesizer: + +- BSynth controls the synthesizer +- BMidiSynth connects a BMidi object to the synth +- BMidiSynthFile connects a MIDI file to the synth +- BSamples lets you access the synth's sound data stream + +To make MIDI data stream through your application, you create a "network" of +BMidi-derived objects that send and receive MIDI messages. + +The old Midi Kit is slowly fading into obscurity. You may want to use the +\ref midi2 "new kit" instead. Also note that the OpenBeOS implementation +of the kit sometimes behaves differently than the one from BeOS R5 or what +the BeBook says, but usually for the better ;-) + +Especially the synth classes are not completely functional, but enough to +play back General MIDI tunes. They should be backwards compatible with the +majority of BeOS MIDI applications. Not all methods of BSynth, BMidiSynth, +and BMidiSynthFile are implemented because some of them are rather obscure. +BSamples is a complete no-op; in other words, with the OpenBeOS Midi Kit +you cannot push waveform data into the output stream of the softsynth. +*/ diff --git a/docs/user/midi2/midi2concepts.png b/docs/user/midi2/midi2concepts.png new file mode 100644 index 0000000000..2278a4d53b Binary files /dev/null and b/docs/user/midi2/midi2concepts.png differ diff --git a/docs/user/midi2/midi2intro.dox b/docs/user/midi2/midi2intro.dox new file mode 100644 index 0000000000..5ba88c7654 --- /dev/null +++ b/docs/user/midi2/midi2intro.dox @@ -0,0 +1,303 @@ +/*! +\page midi2 The new Midi Kit (libmidi2.so) + +The Midi Kit is the API that implements support for generating, processing, and +playing music in MIDI format. MIDI, which +stands for 'Musical Instrument Digital Interface', is a well-established +standard for representing and communicating musical data. + +\section midi2twokits The two kits + +The BeOS comes with two different, but compatible Midi Kits. This documentation +focuses on the "new" Midi Kit, or midi2 as we like to call it, that was +introduced with BeOS R5. The old kit, which we'll refer to as midi1, is more +complete than the new kit, but less powerful. + +Both kits let you create so-called MIDI endpoints, but the endpoints from midi1 +cannot be shared between different applications. The midi2 kit solves that +problem, but unlike midi1 it does not include a General MIDI softsynth, nor +does it have a facility for reading and playing Standard MIDI Files. Don't +worry: both kits are compatible and you can mix-and-match them in your +applications. + +The main differences between the two kits: + +- Instead of one BMidi object that both produces and consumes events, we have +BMidiProducer and BMidiConsumer. +- Applications are capable of sharing MIDI producers and consumers with other +applications via the centralized Midi Roster. +- Physical MIDI ports are now sharable without apps "stealing" events from each +other. +- Applications can now send/receive raw MIDI byte streams (useful if an +application has its own MIDI parser/engine). +- Channels are numbered 0..15, not 1..16 +- Timing is now specified in microseconds instead of milliseconds. + +\section midi2concepts Midi Kit concepts + +A brief overview of the elements that comprise the Midi Kit: + +- \b Endpoints. This is what the Midi Kit is all about: sending MIDI messages +between endpoints. An endpoint is like a MIDI In or MIDI Out socket on your +equipment; it either receives information or it sends information. Endpoints +that send MIDI events are called \b producers; the endpoints that receive those +events are called \b consumers. An endpoint that is created by your own +application is called \b local; endpoints from other applications are \b +remote. You can access remote endpoints using \b proxies. + +- \b Filters. A filter is an object that has a consumer and a producer +endpoint. It reads incoming events from its consumer, performs some operation, +and tells its producer to send out the results. In its current form, the Midi +Kit doesn't provide any special facilities for writing filters. + +- \b Midi \b Roster. The roster is the list of all published producers and +consumers. By publishing an endpoint, you allow other applications to talk to +it. You are not required to publish your endpoints, in which case only your own +application can use them. + +- \b Midi \b Server. The Midi Server does the behind-the-scenes work. It +manages the roster, it connects endpoints, it makes sure that endpoints can +communicate, and so on. The Midi Server is started automatically when BeOS +boots, and you never have to deal with it directly. Just remember that it runs +the show. + +- \b libmidi. The BMidi* classes live inside two shared libraries: libmidi.so +and libmidi2.so. If you write an application that uses old Midi Kit, you must +link it to libmidi.so. Applications that use the new Midi Kit must link to +libmidi2.so. If you want to mix-and-match both kits, you should also link to +both libraries. + +Here is a pretty picture: + +\image html midi2concepts.png + +\section midi2mediakit Midi Kit != Media Kit + +Be chose not to integrate the Midi Kit into the Media Kit as another media +type, mainly because MIDI doesn't require any of the format negotiation that +other media types need. Although the two kits look similar -- both have a +"roster" for finding or registering "consumers" and "producers" -- there are +some very important differences. + +The first and most important point to note is that BMidiConsumer and +BMidiProducer in the Midi Kit are NOT directly analogous to BBufferConsumer and +BBufferProducer in the Media Kit! In the Media Kit, consumers and producers are +the data consuming and producing properties of a media node. A filter in the +Media Kit, therefore, inherits from both BBufferConsumer and BBufferProducer, +and implements their virtual member functions to do its work. + +In the Midi Kit, consumers and producers act as endpoints of MIDI data +connections, much as media_source and media_destination do in the Media Kit. +Thus, a MIDI filter does not derive from BMidiConsumer and BMidiProducer; +instead, it contains BMidiConsumer and BMidiProducer objects for each of its +distinct endpoints that connect to other MIDI objects. The Midi Kit does not +allow the use of multiple virtual inheritance, so you can't create an object +that's both a BMidiConsumer and a BMidiProducer. + +This also contrasts with the old Midi Kit's conception of a BMidi object, which +stood for an object that both received and sent MIDI data. In the new Midi Kit, +the endpoints of MIDI connections are all that matters. What lies between the +endpoints, i.e., how a MIDI filter is actually structured, is entirely at your +discretion. + +Also, rather than use token structs like media_node to make connections via the +MediaRoster, the new kit makes the connections directly via the BMidiProducer +object. + +\section midi2remotelocal Remote and local objects + +The Midi Kit makes a distinction between remote and local MIDI objects. You can +only create local MIDI endpoints, which derive from either BMidiLocalConsumer +or BMidiLocalProducer. Remote endpoints are endpoints that live in other +applications, and you access them through BMidiRoster. + +BMidiRoster only gives you access to BMidiEndpoints, BMidiConsumers, and +BMidiProducers. When you want to talk to remote MIDI objects, you do so through +the proxy objects that BMidiRoster provides. Unlike BMidiLocalConsumer and +BMidiLocalProducer, these classes do not provide a lot of functions. That is +intentional. In order to hide the details of communication with MIDI endpoints +in other applications, the Midi Kit must hide the details of how a particular +endpoint is implemented. + +So, what can you do with remote objects? Only what BMidiConsumer, +BMidiProducer, and BMidiEndpoint will let you do. You can connect objects, get +the properties of these objects -- and that's about it. + +\section midi2lifespan Creating and destroying objects + +The constructors and destructors of most midi2 classes are private, which mean +you cannot directly create them using the C++ new operator, on the +stack, or as globals. Nor can you delete them. Instead, these +objects are obtained through BMidiRoster. The only two exceptions to this rule +are BMidiLocalConsumer and BMidiLocalProducer. These two objects may be +directly created and subclassed by developers. + +\section midi2refcount Reference counting + +Each MIDI endpoint has a reference count associated with it, so that the Midi +Roster can do proper bookkeeping. When you construct a BMidiLocalProducer or +BMidiLocalConsumer endpoint, it starts with a reference count of 1. In +addition, BMidiRoster increments the reference count of any object it hands to +you as a result of \link BMidiRoster::NextEndpoint() NextEndpoint() \endlink or +\link BMidiRoster::FindEndpoint() FindEndpoint() \endlink. Once the count hits +0, the endpoint will be deleted. + +This means that, to delete an endpoint, you don't call the delete +operator directly; instead, you call \link BMidiEndpoint::Release() Release() +\endlink. To balance this call, there's also an \link BMidiEndpoint::Acquire() +Acquire() \endlink, in case you have two disparate parts of your application +working with the endpoint, and you don't want to have to keep track of who +needs to Release() the endpoint. + +When you're done with any endpoint object, you must Release() it. This is true +for both local and remote objects. Repeat after me: Release() when you're done. + +\section midi2events MIDI events + +To make some actual music, you need to \link BMidiProducer::Connect() Connect() +\endlink your consumers to your producers. Then you tell the producer to +"spray" MIDI events to all the connected consumers. The consumers are notified +of these incoming events through a set of hook functions. + +The Midi Kit already provides a set of commonly used spray functions, such as +\link BMidiLocalProducer::SprayNoteOn() SprayNoteOn() \endlink, \link +BMidiLocalProducer::SprayControlChange() SprayControlChange() \endlink, and so +on. These correspond one-to-one with the message types from the MIDI spec. You +don't need to be a MIDI expert to use the kit, but of course some knowledge of +the protocol helps. If you are really hardcore, you can also use the \link +BMidiLocalProducer::SprayData() SprayData() \endlink to send raw MIDI events to +the consumers. + +At the consumer side, a dedicated thread invokes a hook function for every +incoming MIDI event. For every spray function, there is a corresponding hook +function, e.g. \link BMidiLocalConsumer::NoteOn() NoteOn() \endlink and \link +BMidiLocalConsumer::ControlChange() ControlChange() \endlink. The hardcore MIDI +fanatics among you will be pleased to know that you can also tap into the \link +BMidiLocalConsumer::Data() Data() \endlink hook and get your hands dirty with +the raw MIDI data. + +\section midi2time Time + +The spray and hook functions accept a bigtime_t parameter named "time". This +indicates when the MIDI event should be performed. The time is given in +microseconds since the computer booted. To get the current tick measurement, +you call the system_time() function from the Kernel Kit. + +If you override a hook function in one of your consumer objects, it should look +at the time argument, wait until the designated time, and then perform its +action. The preferred method is to use the Kernel Kit's +snooze_until() function, which sends the consumer thread to sleep +until the requested time has come. (Or, if the time has already passed, returns +immediately.) + +Like this: + +\code +void MyConsumer::NoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time) +{ + snooze_until(time, B_SYSTEM_TIMEBASE); + ...do your thing... +} +\endcode + +If you want your producers to run in real time, i.e. they produce MIDI data +that needs to be performed immediately, you should pass time 0 to the spray +functions (which also happens to be the default value). Since time 0 has +already passed, snooze_until() returns immediately, and the +consumer will process the events as soon as they are received. + +To schedule MIDI events for a performance time that lies somewhere in the +future, the producer must take into account the consumer's latency. Producers +should attempt to get notes to the consumer by or before +(scheduled_performance_time - latency). The time argument is still the +scheduled performance time, so if your consumer has latency, it should snooze +like this before it starts to perform the events: + +\code +snooze_until(time - Latency(), B_SYSTEM_TIMEBASE); +\endcode + +Note that a typical producer sends out its events as soon as it can; unlike a +consumer, it does not have to snooze. + +\section midi2ports Other timing issues + +Each consumer object uses a Kernel Kit port to receive MIDI events from +connected producers. The queue for this port is only 1 message deep. This means +that if the consumer thread is asleep in a snooze_until(), it will +not read its port. Consequently, any producer that tries to write a new event +to this port will block until the consumer thread is ready to receive a new +message. This is intentional, because it prevents producers from generating and +queueing up thousands of events. + +This mechanism, while simple, puts on the producer the responsibility for +sorting the events in time. Suppose your producer sends three Note On events, +the first on t + 0, the second on t + 4, and the third on t + 2. This last +event won't be received until after t + 4, so it will be two ticks too late. If +this sort of thing can happen with your producer, you should somehow sort the +events before you spray them. Of course, if you have two or more producers +connected to the same consumer, it is nearly impossible to sort this all out +(pardon the pun). So it is not wise to send the same kinds of events from more +than one producer to one consumer at the same time. + +The article Introduction to MIDI, Part 2 in OpenBeOS +Newsletter 36 describes this problem in more detail, and provides a +solution. Go read it now! + +\section midi2filters Writing a filter + +A typical filter contains a consumer and a producer endpoint. It receives +events from the consumer, processes them, and sends them out again using the +producer. The consumer endpoint is a subclass of BMidiLocalConsumer, whereas +the producer is simply a BMidiLocalProducer, not a subclass. This is a common +configuration, because consumers work by overriding the event hooks to do work +when MIDI data arrives. Producers work by sending an event when you call their +member functions. You should hardly ever need to derive from BMidiLocalProducer +(unless you need to know when the producer gets connected or disconnected, +perhaps), but you'll always have to override one or more of +BMidiLocalConsumer's member functions to do something useful with incoming +data. + +Filters should ignore the time argument from the spray and hook functions, and +simply pass it on unchanged. Objects that only filter data should process the +event as quickly as possible and be done with it. Do not +snooze_until() in the consumer endpoint of a filter! + +\section midi2apidiffs API differences + +As far as the end user is concerned, the OpenBeOS Midi Kit is mostly the same +as the BeOS R5 kits, although there are a few small differences in the API +(mostly bug fixes): + +- BMidiEndpoint::IsPersistent() always returns false. +- The B_MIDI_CHANGE_LATENCY notification is now properly sent. The Be kit + incorrectly set be:op to B_MIDI_CHANGED_NAME, even though the rest of the + message was properly structured. +- If creating a local endpoint fails, you can still Release() the object + without crashing into the debugger. + +\section midi2seealso See also + +More about the Midi Kit: + +- \ref midi2defs +- Be Newsletter Volume 3, Issue 47 - Motor Mix sample code +- Be Newsletter Volume 4, Issue 3 - Overview of the new kit +- OpenBeOS + Newsletter 33, Introduction to MIDI, Part 1 +- OpenBeOS + Newsletter 36, Introduction to MIDI, Part 2 +- Sample code and other goodies at the + OpenBeOS Midi Kit team page + +Information about MIDI in general: + +- MIDI Manufacturers Association +- MIDI Tutorials +- MIDI Specification +- Standard MIDI File Format +- Jim Menard's MIDI Reference + +*/ diff --git a/docs/user/midi2/midiconsumer.dox b/docs/user/midi2/midiconsumer.dox new file mode 100644 index 0000000000..5083f5d64e --- /dev/null +++ b/docs/user/midi2/midiconsumer.dox @@ -0,0 +1,54 @@ +/*! +\class BMidiConsumer MidiConsumer.h +\brief Receives MIDI events from a producer + +A consumer is an object that knows how to deal with incoming MIDI events. A +consumer can be connected to multiple producers at the same time. There is no +way to find out which producers are connected to this consumer just by looking +at the BMidiConsumer object; you will have to consult BMidiRoster for that. + +A BMidiConsumer either represents a local consumer, i.e. a class extending from +BMidiLocalConsumer, or is a proxy for a remote object published by another app. + +*/ + +/*! +\fn bigtime_t BMidiConsumer::Latency() const +\brief Returns the latency of this consumer + +The latency is measured in microseconds. Producers should attempt to get MIDI +events to this consumer by (when - latency). You do this by subtracting +the latency from the performance time when you spray the events (provided that +you spray these events ahead of time, of course). + +You cannot set the latency on a BMidiConsumer, only on a +BMidiLocalConsumer. + +The latency issue gets slightly more complicated when multiple endpoints are +chained together, as in the following picture: + +\verbatim ++-------+ +-------------+ +-------+ +| | | | | | +| prodA |---->| consB prodB |---->| consC | +| | | | | | ++-------+ +-------------+ +-------+ + appA appB (filter) appC +\endverbatim + +Suppose consC has 200ms latency, and consB has 100ms latency. If consB simply +reports 100ms, then prodA will schedule its events for (t - 100), which is +really 200ms too late. (Of course, producers send out their events as soon as +possible, so depending on the load of the system, everything may work out just +fine.) + +ConsB should report the latency of the consumer that is hooked up to its +output, consC, in addition to its own latency. In other words, the full +downstream latency. So, the reported latency in this case would be 300ms. This +also means that appB should change the latency of consB when prodB makes or +breaks a connection, and when consC reports a latency change. (If multiple +consumers are connected to prodB, you should take the slowest one.) +Unfortunately, the Midi Kit provides no easy mechanism for doing any of this, +so you are on your own here. + +*/ diff --git a/docs/user/midi2/mididefs.dox b/docs/user/midi2/mididefs.dox new file mode 100644 index 0000000000..0374ee59c6 --- /dev/null +++ b/docs/user/midi2/mididefs.dox @@ -0,0 +1,89 @@ +/*! +\page midi2defs Midi Kit definitions (Midi2Defs.h) + +\section channelmasks Channel message masks + + + + + + + + + +
B_NOTE_OFF0x80
B_NOTE_ON0x90
B_KEY_PRESSURE0xa0
B_CONTROL_CHANGE0xb0
B_PROGRAM_CHANGE0xc0
B_CHANNEL_PRESSURE0xd0
B_PITCH_BEND0xe0
+ +\section systemmessages System messages + + + + + + + + + + + + + + + +
B_SYS_EX_START0xf0
B_MIDI_TIME_CODE0xf1
B_SONG_POSITION0xf2
B_SONG_SELECT0xf3
B_CABLE_MESSAGE0xf5
B_TUNE_REQUEST0xf6
B_SYS_EX_END0xf7
B_TIMING_CLOCK0xf8
B_START0xfa
B_CONTINUE0xfb
B_STOP0xfc
B_ACTIVE_SENSING0xfe
B_SYSTEM_RESET0xff
+ +\section controllernumbers Controller numbers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
B_MODULATION0x01
B_BREATH_CONTROLLER0x02
B_FOOT_CONTROLLER0x04
B_PORTAMENTO_TIME0x05
B_DATA_ENTRY0x06
B_MAIN_VOLUME0x07
B_MIDI_BALANCE0x08
B_PAN0x0a
B_EXPRESSION_CTRL0x0b
B_GENERAL_CTRL_10x10
B_GENERAL_CTRL_20x11
B_GENERAL_CTRL_30x12
B_GENERAL_CTRL_40x13
B_SUSTAIN_PEDAL0x40
B_PORTAMENTO0x41
B_SOSTENUTO0x42
B_SOFT_PEDAL0x43
B_HOLD_20x45
B_GENERAL_CTRL_50x50
B_GENERAL_CTRL_60x51
B_TEMPO_CHANGE0x51
B_GENERAL_CTRL_70x52
B_GENERAL_CTRL_80x53
B_EFFECTS_DEPTH0x5b
B_TREMOLO_DEPTH0x5c
B_CHORUS_DEPTH0x5d
B_CELESTE_DEPTH0x5e
B_PHASER_DEPTH0x5f
B_DATA_INCREMENT0x60
B_DATA_DECREMENT0x61
B_RESET_ALL_CONTROLLERS0x79
B_LOCAL_CONTROL0x7a
B_ALL_NOTES_OFF0x7b
B_OMNI_MODE_OFF0x7c
B_OMNI_MODE_ON0x7d
B_MONO_MODE_ON0x7e
B_POLY_MODE_ON0x7f
+ +\section bmidiop BMidiOp + + + + + + + + + + +
B_MIDI_NO_OP
B_MIDI_REGISTERED
B_MIDI_UNREGISTERED
B_MIDI_CONNECTED
B_MIDI_DISCONNECTED
B_MIDI_CHANGED_NAME
B_MIDI_CHANGED_LATENCY
B_MIDI_CHANGED_PROPERTIES
+ +*/ diff --git a/docs/user/midi2/midiendpoint.dox b/docs/user/midi2/midiendpoint.dox new file mode 100644 index 0000000000..87bbb4e8de --- /dev/null +++ b/docs/user/midi2/midiendpoint.dox @@ -0,0 +1,284 @@ +/*! +\class BMidiEndpoint MidiEndpoint.h +\brief Base class for all MIDI endpoints + +BMidiEndpoint is the abstract base class that represents either a producer or +consumer endpoint. It may be used to obtain the state, name, properties, or +system-wide ID of the object. BMidiEndpoint also provides the ability to change +the name and properties of endpoints that were created locally. + +Remember, you cannot call the destructor of BMidiEndpoint and its subclasses +directly. Endpoint objects are destructed automatically when their reference +count drops to zero. If necessary, the destructor of a local endpoint first +breaks off any connections and Unregister()'s the endpoint before it is +deleted. However, for good style and bonus points you should really \link +BMidiProducer::Disconnect() Disconnect() \endlink and Unregister() the object +yourself and not rely on the destructor to do this. + +*/ + +/*! +\fn const char* BMidiEndpoint::Name() const +\brief Returns the name of the endpoint + +The function never returns NULL. If you created a local endpoint by passing a +NULL name into its constructor (or passing no name, which is the same thing), +then Name() will return an empty string, not NULL. + +\sa SetName() + +*/ + +/*! +\fn void BMidiEndpoint::SetName(const char* name) +\brief Changes the name of the endpoint. + +Names don't have to be unique, but it is recommended that you give any +endpoints you publish meaningful and unique names, so users can easily +recognize what each endpoint does. There is no limit to the size of endpoint +names. + +Even though you can call this function on both remote and local objects, you +are only allowed to change the names of local endpoints; SetName() calls on +remote endpoints are ignored. + +\param name The new name. If you pass NULL, the name simply won't be changed. + +\sa Name() + +*/ + +/*! +\fn int32 BMidiEndpoint::ID() const +\brief Returns the ID of the endpoint + +An ID uniquely identifies an endpoint in the system. The ID is a signed 32-bit +number that is assigned by the Midi Server when the endpoint is created. (So +even if a local endpoint is not published, it still has a unique ID.) Valid IDs +range from 1 to 0x7FFFFFFF, the largest value an int32 can have. 0 and negative +values are not valid IDs. + +*/ + +/*! +\fn bool BMidiEndpoint::IsProducer() const +\brief Determines whether this endpoint is a BMidiProducer + +If it is, you can use a dynamic_cast to convert this object into a producer: + +\code +if (endp->IsProducer()) +{ + BMidiProducer* prod = dynamic_cast(endp); + .... +} +\endcode +*/ + +/*! +\fn bool BMidiEndpoint::IsConsumer() const +\brief Determines whether this endpoint is a BMidiConsumer + +If it is, you can use a dynamic_cast to convert this object into a consumer: + +\code +if (endp->IsConsumer()) +{ + BMidiConsumer* cons = dynamic_cast(endp); + .... +} +\endcode +*/ + +/*! +\fn bool BMidiEndpoint::IsRemote() const +\brief Determines whether this endpoint is a proxy for a remote object + +An endpoint is "remote" when it is created by another application. Obviously, +the remote object is Register()'ed as well, otherwise you would not be able to +see it. + +*/ + +/*! +\fn bool BMidiEndpoint::IsLocal() const +\brief Determines whether this endpoint represents a local object + +An endpoint is "local" when it is created by this application; in other words, +a BMidiLocalConsumer or BMidiLocalProducer. + +*/ + +/*! +\fn bool BMidiEndpoint::IsPersistent() const +\brief Not used + +The purpose of this function is unclear, and as a result it doesn't do anything +in the OpenBeOS implementation of the Midi Kit. + +\return Always returns false. +*/ + +/*! +\fn bool BMidiEndpoint::IsValid() const +\brief Determines whether the endpoint still exists + +Suppose you obtained a proxy object for a remote endpoint by querying the +BMidiRoster. What if the application that published this endpoint quits, or +less drastically, Unregister()'s that endpoint? Even though you still have a +BMidiEndpoint proxy object, the real endpoint no longer exists. You can use +IsValid() to check for this. + +Don't worry, operations on invalid objects, such as GetProperties(), will +return an error code (typically B_ERROR), but not cause a crash. Local objects +are always are considered to be valid, even if you did not Register() them. +(The only time a local endpoint is not valid is when there was a problem +constructing it.) + +If the application that created the remote endpoint crashes, then there is no +guarantee that the Midi Server immediately recognizes this. In that case, +IsValid() may still return true. Eventually, the stale endpoint will be removed +from the roster, though. From then on, IsValid() correctly returns false. + +*/ + +/*! +\fn status_t BMidiEndpoint::Acquire() +\brief Increments the endpoint's reference count + +Each BMidiEndpoint has a reference count associated with it, so that +BMidiRoster can do proper bookkeeping. Acquire() increments this reference +count, and Release() decrements it. Once the count reaches zero, the endpoint +is deleted. + +When you are done with the endpoint, whether local or remote, you should +always Release() it! + +Upon construction, local endpoints start with a reference count of 1. Any +objects you obtain from BMidiRoster using the NextXXX() or FindXXX() functions +have their reference counts incremented in the process. If you forget to call +Release(), the objects won't be properly cleaned up and you'll make a fool out +of yourself. + +After you Release() an object, you are advised not to use it any further. If +you do, your app will probably crash. That also happens if you Release() an +object too many times. + +Typically, you don't need to call Acquire(), unless you have two disparate +parts of your application working with the same endpoint, and you don't want to +have to keep track of who needs to Release() the endpoint. Now you simply have +both of them release it. + +\return Always returns B_OK +\sa Release() +*/ + +/*! +\fn status_t BMidiEndpoint::Release() +\brief Decrements the endpoint's reference count +\return Always returns B_OK +\sa Acquire() +*/ + +/*! +\fn status_t BMidiEndpoint::Register() +\brief Publishes the endpoint on the roster + +MIDI objects created by an application are invisible to other applications +until they are published. To publish an object use the Register() method. The +corresponding Unregister() method will cause an object to once again become +invisible to remote applications. + +BMidiRoster also has Register() and Unregister() methods. You may also use +those methods to publish or hide your endpoints; both do the same thing. + +Although it is considered bad style, calling Register() on local endpoints that +are already registered won't mess things up. The Midi Server will simply ignore +your request. Likewise for Unregister()'ing more than once. Attempts to +Register() or Unregister() remote endpoints will fail, of course. + +If you are \link BMidiRoster::StartWatching() watching \endlink, you will +not receive notifications for any local endpoints you register or +unregister. Of course, other applications will be notified about your +endpoints. + +Existing connections will not be broken when an object is unregistered, but +future remote connections will be denied. When objects are destroyed, they +automatically become unregistered. + +\return B_OK on success, or a negative error code (typically B_ERROR) if +something went wrong. + +\sa Unregister() +*/ + +/*! +\fn status_t BMidiEndpoint::Unregister() +\brief Hides the endpoint from the roster +\sa Register() +*/ + +/*! +\fn status_t BMidiEndpoint::SetProperties(const BMessage* props) +\brief Changes the properties of the endpoint + +Endpoints can have properties, which is any kind of information that might be +useful to associate with a MIDI object. The properties are stored in a +BMessage. + +Usage example: + +\code +BMessage props; +if (endpoint->GetProperties(&props) == B_OK) +{ + ...add data to the message... + endpoint->SetProperties(&props); +} +\endcode + +You are only allowed to call SetProperties() on a local object. + +Properties should follow a protocol, so different applications will know how to +read each other's properties. The current protocol is very limited -- it only +allows you to associate icons with your endpoints. Be planned to publish a more +complete protocol that included additional information, such as vendor/model +names, copyright/version info, category, etc., but they never got around to it. + + + + + +
propertyLarge (32x32) icon
field name"be:large_icon"
field type'ICON'
+ + + + + +
propertySmall (16x16) icon
field name"be:mini_icon"
field type'MICN'
+ +The MidiUtil package (downloadable from the OpenBeOS website) contains a number +of convenient functions to associate icons with endpoints, so you don't have to +write that code all over again. + +\sa GetProperties() +*/ + +/*! +\fn status_t BMidiEndpoint::GetProperties(BMessage* props) const +\brief Reads the properties of the endpoint + +Usage example: + +\code +BMessage props; +if (endpoint->GetProperties(&props) == B_OK) +{ + ...examine the contents of the message... +} +\endcode + +Note that GetProperties() overwrites the contents of your BMessage. + +\sa SetProperties() +*/ diff --git a/docs/user/midi2/midilocalcons.dox b/docs/user/midi2/midilocalcons.dox new file mode 100644 index 0000000000..4a63a6b337 --- /dev/null +++ b/docs/user/midi2/midilocalcons.dox @@ -0,0 +1,230 @@ +/*! +\class BMidiLocalConsumer MidiConsumer.h +\brief A consumer endpoint that is created by your own application + +If you want to create a consumer that reacts to MIDI events, you should +subclass BMidiLocalConsumer. + +Each local consumer has its own thread that receives and dispatches the MIDI +events. Whenever MIDI data arrives, the Data() hook passes the MIDI event on to +a more specific hook function: NoteOn(), NoteOff(), SystemExclusive(), and so +on. Calls to these hook functions are serialized -- they will never have to be +re-entrant. They also should not be called from outside the thread that is +invoking them. + +Your subclass can override any of the MIDI event hooks. BMidiLocalConsumer +doesn't provide default implementations for them, so you don't have to call a +hook's default implementation if you override it. For complete control, you can +also override Data(). + +Most hook functions take a channel argument. Even though MIDI channels are +really numbered 1 through 16, the hook functions work with channels 0 through +15. The performance time for the event is specified in microseconds relative to +the system time base. A performance time that is 0 (or really any time in the +past) means "play as soon as possible". See the \ref midi2time "introduction" +for more information about timing and consumers. + +The thread driving the consumer's events is a very high priority real time +thread. Events should be handled as quickly as possible (not counting +snoozing). If non-time-critical computation is needed it may be wise to queue +events up for a lower priority thread to handle them external to the main event +thread. + +*/ + +/*! +\fn BMidiLocalConsumer::BMidiLocalConsumer(const char *name = NULL) +\brief Creates a new local consumer endpoint + +The new endpoint is not visible to other applications until you Register() it. + +You can tell the constructor what the name of the new consumer will be. If you +pass NULL (or use the default argument), then the consumer's name will be an +empty string. It won't be NULL, since endpoint names cannot be NULL. + +There is no guarantee that the endpoint will be successfully created. For +example, the Midi Server may not be running. Therefore, you should always call +IsValid() after creating a new endpoint to make sure that everything went okay. +If not, Release() the object to reclaim memory and abort gracefully. + +\code +MyConsumer* cons = new MyConsumer(...); +if (!cons->IsValid()) +{ + cons->Release(); + ...exit gracefully... +} +\endcode + +*/ + +/*! +\fn void BMidiLocalConsumer::SetLatency(bigtime_t latency) +\brief Changes the published latency of the consumer +\sa Latency() +*/ + +/*! +\fn int32 BMidiLocalConsumer::GetProducerID() +\brief Returns the ID of the producer that most recently sent a MIDI event to +this consumer + +You can call this from one of the hooks to determine which producer the event +came from. + +*/ + +/*! +\fn void BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data) +\brief Requests that the Timeout() hook will be called at some point + +This method asks the consumer thread to call the Timeout() hook as soon as +possible after the timeout expires. For every call to SetTimeout(), the +Timeout() hook is only called once. Note: the term "timeout" may be a little +misleading; the hook will always be called, even if events are received +in the mean time. Apparently, this facility is handy for dealing with early +events. + +Note that the event thread blocks on the consumer's port as long as no events +arrive. By default no timeout is set, and as a result the thread blocks +forever. Your call to SetTimeout() doesn't change this. The new timeout value +will go into effect the next time the thread tries to read from the port, i.e. +after the first event has been received. If no event ever comes in, the +Timeout() hook will never be called. This also means that you cannot cancel a +timeout once you have set it. To repeat, calling SetTimeout() only takes effect +after at least one new event has been received. + +\param when An absolute time that's measured against the system clock. + +\param data A pointer to a "cookie" that you can pass along to Timeout(). The +data is not copied, so you must ensure that the pointer remains valid until +Timeout() is called. You typically delete the data inside Timeout(). + +*/ + +/*! +\fn void BMidiLocalConsumer::Timeout(void* data) +\brief Hook function that is called per your own request +\sa SetTimeout() +*/ + +/*! +\fn void BMidiLocalConsumer::Data( + uchar* data, size_t length, bool atomic, bigtime_t time) +\brief Invoked when raw MIDI is received + +What the default implementation of Data() does depends on the value of atomic. +If atomic is true, the data received comprises a single MIDI event; i.e. one +status byte followed by the appropriate number of data bytes and nothing else. +In this case, Data() calls the event-specific hook function that corresponds to +that status byte. This optimization is used by the Midi Kit to allow faster +dispatch of events generated by the specific Spray functions from +BMidiLocalProducer. + +If atomic is false, Data() ignores the MIDI event. If you want a consumer to +handle non-atomic events, you have to override Data() and program this +yourself. In that case, you probably also want to call the default +implementation to handle the "normal" MIDI events. + +Data() is rarely overridden, but you can override it if you want to. If you do, +remember that the data buffer is owned by the Midi Kit. Do not attempt to +modify or free it, lest you wish to be laughed at by other developers. + +\param data the MIDI event data +\param length byte size of the data buffer +\param atomic whether the data buffer contains a single complete MIDI event +\param time the requested performance time of the event + +\sa BMidiLocalProducer::SprayData() +*/ + +/*! +\fn void BMidiLocalConsumer::NoteOff( + uchar channel, uchar note, uchar velocity, bigtime_t time) +\brief Invoked when a Note Off event is received +\sa BMidiLocalProducer::SprayNoteOff() +*/ + +/*! +\fn void BMidiLocalConsumer::NoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time) +\brief Invoked when a Note On event is received +\sa BMidiLocalProducer::SprayNoteOn() +*/ + +/*! +\fn void BMidiLocalConsumer::KeyPressure( + uchar channel, uchar note, uchar pressure, bigtime_t time) +\brief Invoked when a Polyphonic Pressure (Aftertouch) event is received +\sa BMidiLocalProducer::SprayKeyPressure() +*/ + +/*! +\fn void BMidiLocalConsumer::ControlChange( + uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time) +\brief Invoked when a Controller Change event is received +\sa BMidiLocalProducer::SprayControlChange() +*/ + +/*! +\fn void BMidiLocalConsumer::ProgramChange( + uchar channel, uchar programNumber, bigtime_t time) +\brief Invoked when a Program Change event is received +\sa BMidiLocalProducer::SprayProgramChange() +*/ + +/*! +\fn void BMidiLocalConsumer::ChannelPressure( + uchar channel, uchar pressure, bigtime_t time) +\brief Invoked when a Channel Pressure event is received +\sa BMidiLocalProducer::SprayChannelPressure() +*/ + +/*! +\fn void BMidiLocalConsumer::PitchBend( + uchar channel, uchar lsb, uchar msb, bigtime_t time) +\brief Invoked when a Pitch Bend event is received +\sa BMidiLocalProducer::SprayPitchBend() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemExclusive( + void* data, size_t length, bigtime_t time) +\brief Invoked when a System Exclusive event is received + +The data does not include the sysex start and end control bytes (0xF0 and 0xF7), +only the payload of the sysex message. + +The data belongs to the Midi Kit and is only valid for the duration of this +event. You may not modify or free it. + +\sa BMidiLocalProducer::SpraySystemExclusive() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemCommon( + uchar status, uchar data1, uchar data2, bigtime_t time) +\brief Invoked when a System Common event is received + +Not all data bytes are used for all common events. Unused bytes are set to 0. + +\sa BMidiLocalProducer::SpraySystemCommon() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemRealTime( + uchar status, bigtime_t time) +\brief Invoked when a Real Time event is received +\sa BMidiLocalProducer::SpraySystemRealTime() +*/ + +/*! +\fn void BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time) +\brief Invoked when a Tempo Change event is received +\sa BMidiLocalProducer::SprayTempoChange() +*/ + +/*! +\fn void BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time) +\brief Not used +*/ diff --git a/docs/user/midi2/midilocalprod.dox b/docs/user/midi2/midilocalprod.dox new file mode 100644 index 0000000000..a8063ae362 --- /dev/null +++ b/docs/user/midi2/midilocalprod.dox @@ -0,0 +1,226 @@ +/*! +\class BMidiLocalProducer MidiProducer.h +\brief A producer endpoint that is created by your own application + +You create a BMidiLocalProducer if you want your application to send MIDI +events. You use the various spray functions to send events to all connected +consumers. If no consumers are connected to the producer, any calls to the +spray functions are ignored. + +Most spray functions accept a channel argument. Even though MIDI channels are +really numbered 1 through 16, the spray functions work with channels 0 through +15. You can also specify the performance time for the event using the time +argument. Specify 0 (or any time in the past) to perform the event "now", i.e. +as soon as possible. You can also schedule events to be performed in the +future, by passing a time such as system_time() + 5000000, which means 5 +seconds from now. + +Unlike BMidiLocalConsumer, which should be subclassed almost always, you hardly +ever need to derive a class from BMidiLocalProducer. The only reason for +subclassing is when you need to know when the producer gets connected or +disconnected. + +Also unlike consumers, local producers have no thread of control directly +associated with them. If you want to send out the MIDI events from a different +thread, you will have to create one yourself. + +*/ + +/*! +\fn BMidiLocalProducer::BMidiLocalProducer(const char *name = NULL) +\brief Creates a new local producer endpoint + +The new endpoint is not visible to other applications until you Register() it. + +You can tell the constructor what the name of the new producer will be. If you +pass NULL (or use the default argument), then the producer's name will be an +empty string. It won't be NULL, since endpoint names cannot be NULL. + +There is no guarantee that the endpoint will be successfully created. For +example, the Midi Server may not be running. Therefore, you should always call +IsValid() after creating a new endpoint to make sure that everything went okay. +If not, Release() the object to reclaim memory and abort gracefully. + +\code +BMidiLocalProducer* prod = new BMidiLocalProducer(...); +if (!prod->IsValid()) +{ + prod->Release(); + ...exit gracefully... +} +\endcode + +*/ + +/*! +\fn void BMidiLocalProducer::Connected(BMidiConsumer* cons) +\brief Invoked when a new consumer is connected to this producer + +Although typical notifications (i.e. from BMidiRoster's "watching" facility) +are only sent if it is some other app that is performing the operation, +Connected() is also called if you are making the connection yourself. + +If you override this hook, you don't have to call the default implementation, +because that does nothing. + +\param cons The newly connected consumer. The reference count of the consumer +object is not increased, so you should not Release() it. However, if you want +to keep track of the consumer beyond this function, you should first Acquire() +it, and Release() it when you are done. + +\sa Disconnected() +*/ + +/*! +\fn void BMidiLocalProducer::Disconnected(BMidiConsumer* cons) +\brief Invoked when a consumer is disconnected from this producer +\sa Connected() +*/ + +/*! +\fn void BMidiLocalProducer::SprayData( + void* data, size_t length, bool atomic = false, bigtime_t time = 0) const +\brief Sends raw MIDI data downstream to all connected consumers + +Typically you won't have to call SprayData(); the other spray functions will do +just fine. If you do call it, remember that you retain ownership of the data +and that you are responsible for freeing it at some point. (Even though data is +not declared const, the function does not change it.) + +With atomic set to false, you can send a MIDI message in segments (perhaps for +a large sysex dump). However, when you do this, you are on your own. The Midi +Kit only tags the data as being non-atomic, but offers no additional support. +The default implementation of BMidiLocalConsumer completely ignores such +events. To handle non-atomic MIDI data, you should override the +BMidiLocalConsumer::Data() hook and process the MIDI event yourself. All of +BMidiLocalProducer's other spray functions always send atomic data. + +\param data the MIDI event data +\param length byte size of the data buffer +\param atomic whether the data buffer contains a single complete MIDI event +\param time the required performance time of the event + +\sa BMidiLocalConsumer::Data() +*/ + +/*! +\fn void BMidiLocalProducer::SprayNoteOff( + uchar channel, uchar note, uchar velocity, bigtime_t time = 0) const +\brief Sends a Note Off event to all connected consumers +\sa BMidiLocalConsumer::NoteOff() +*/ + +/*! +\fn void BMidiLocalProducer::SprayNoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time = 0) const +\brief Sends a Note On event to all connected consumers +\sa BMidiLocalConsumer::NoteOn() +*/ + +/*! +\fn void BMidiLocalProducer::SprayKeyPressure( + uchar channel, uchar note, uchar pressure, bigtime_t time = 0) const +\brief Sends a Polyphonic Pressure (Aftertouch) event to all connected +consumers +\sa BMidiLocalConsumer::KeyPressure() +*/ + +/*! +\fn void BMidiLocalProducer::SprayControlChange( + uchar channel, uchar controlNumber, uchar controlValue, + bigtime_t time = 0) const +\brief Sends a Controller Change event to all connected consumers + +\sa \ref controllernumbers +\sa BMidiLocalConsumer::ControlChange() +*/ + +/*! +\fn void BMidiLocalProducer::SprayProgramChange( + uchar channel, uchar programNumber, bigtime_t time = 0) const +\brief Sends a Program Change event to all connected consumers +\sa BMidiLocalConsumer::ProgramChange() +*/ + +/*! +\fn void BMidiLocalProducer::SprayChannelPressure( + uchar channel, uchar pressure, bigtime_t time = 0) const +\brief Sends a Channel Pressure event to all connected consumers +\sa BMidiLocalConsumer::ChannelPressure() +*/ + +/*! +\fn void BMidiLocalProducer::SprayPitchBend( + uchar channel, uchar lsb, uchar msb, bigtime_t time = 0) const +\brief Sends a Pitch Bend event to all connected consumers +\sa BMidiLocalConsumer::PitchBend() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemExclusive( + void* data, size_t length, bigtime_t time = 0) const +\brief Sends a System Exclusive event to all connected consumers + +You retain ownership of the data and are responsible for freeing it. Even +though data is not declared const, the function does not change it. Even though +the amount of data may be quite large, this function always sends sysex +messages as an atomic block of data. + +\sa BMidiLocalConsumer::SystemExclusive() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemCommon( + uchar status, uchar data1, uchar data2, bigtime_t time = 0) const +\brief Sends a System Common event to the connected consumers + +The status byte must be one of the following: + + + + + + + + +
0xF1B_MIDI_TIME_CODEdata1 only
0xF2B_SONG_POSITIONdata1 and data2
0xF3B_SONG_SELECTdata1 only
0xF5B_CABLE_MESSAGEdata1 only
0xF6B_TUNE_REQUESTno data
0xF7B_SYS_EX_ENDno data
+ +\sa BMidiLocalConsumer::SystemCommon() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemRealTime( + uchar status, bigtime_t time = 0) const +\brief Sends a Real Time event to the connected consumers + +The status byte must be one of the following: + + + + + + + + +
0xF8B_TIMING_CLOCK
0xFAB_START
0xFBB_CONTINUE
0xFCB_STOP
0xFEB_ACTIVE_SENSING
0xFFB_SYSTEM_RESET
+ +Because of their high priority, the MIDI specification allows real time +messages to "interleave" with other MIDI messages. A large sysex dump, for +example, may be interrupted by a real time event. The Midi Kit, however, +doesn't care. If you (or another producer) have just sent a big system +exclusive to a consumer, any following real time message will simply have to +wait until the consumer has dealt with the sysex. + +\sa BMidiLocalConsumer::SystemRealTime() +*/ + +/*! +\fn void BMidiLocalProducer::SprayTempoChange( + int32 bpm, bigtime_t time = 0) const +\brief Sends a Tempo Change event to the connected consumers. + +This kind of Tempo Change event is not really part of the MIDI spec, rather +it is an extension from the SMF (Standard MIDI File) format. + +\sa BMidiLocalConsumer::TempoChange() +*/ diff --git a/docs/user/midi2/midiproducer.dox b/docs/user/midi2/midiproducer.dox new file mode 100644 index 0000000000..9af0f395f0 --- /dev/null +++ b/docs/user/midi2/midiproducer.dox @@ -0,0 +1,75 @@ +/*! +\class BMidiProducer MidiProducer.h +\brief Streams MIDI events to connected consumers + +A producer is an object that generate a stream of MIDI events. Each producer +has a list of BMidiConsumer objects to which it is connected, and may be asked +to connect to or disconnect from a BMidiConsumer. A producer can spray its +events to multiple consumers at the same time. + +A BMidiProducer either represents a local producer, i.e. a class extending from +BMidiLocalProducer, or is a proxy for a remote object published by another app. + +*/ + +/*! +\fn status_t BMidiProducer::Connect(BMidiConsumer* cons) +\brief Connects a consumer to this producer + +Establishes a connection between this producer and the specified consumer +endpoint. From now on, any events that this producer sprays will be sent to +that consumer. You may connect multiple consumers to a producer. + +\return B_OK on success, or an error code when the connection could not be +established. If the consumer is a proxy for a remote object and that object no +longer exists, Connect() returns B_ERROR. It also returns B_ERROR if you try to +connect the same producer and consumer more than once. + +\sa Disconnect() +*/ + +/*! +\fn status_t BMidiProducer::Disconnect(BMidiConsumer* cons) +\brief Disconnects a consumer from this producer + +Terminates the connection between this producer and the specified consumer +endpoint. From now on, any events that this producer sprays no longer go to +that consumer. + +\return B_OK on success, or an error code if there was no connection to break + +\sa Connect() +*/ + +/*! +\fn bool BMidiProducer::IsConnected(BMidiConsumer* cons) const +\brief Determines whether a consumer is connected to this producer +\sa Connect() +\sa Disconnect() +*/ + +/*! +\fn BList* BMidiProducer::Connections() const +\brief Returns a list with all connected consumers + +Returns a BList with pointers to BMidiEndpoint objects for all consumers that +are connected to this producer. You can examine the contents of the list as +follows: + +\code +BList* list = prod->Connections(); +for (int32 t = 0; t < list->CountItems(); ++t) +{ + BMidiEndpoint* endp = (BMidiEndpoint*) list->ItemAt(t); + ...do stuff... + endp->Release(); // yes, here too! +} +delete list; +\endcode + +Every time you call this function, a new BList is allocated. The caller (that +is you) is responsible for freeing this list. The BMidiEndpoint objects in the +list have their reference counts bumped, so you need to Release() them before +you delete the list or they will go all leaky on you. + +*/ diff --git a/docs/user/midi2/midiroster.dox b/docs/user/midi2/midiroster.dox new file mode 100644 index 0000000000..5389882805 --- /dev/null +++ b/docs/user/midi2/midiroster.dox @@ -0,0 +1,292 @@ +/*! +\class BMidiRoster MidiRoster.h +\brief Interface to the system-wide Midi Roster + +BMidiRoster allows you to find available MIDI consumer and producer objects. +You can locate these objects using the iterative NextEndpoint(), +NextProducer(), and NextConsumer() methods or by requesting notification +messages to be sent with StartWatching(). Notification messages may contain +object IDs which can be resolved using the FindEndpoint(), FindProducer(), and +FindConsumer() methods. + +The constructor and destructor of BMidiRoster are private, which means that you +cannot create or delete your own BMidiRoster objects. Every application can +have only one instance of BMidiRoster, which is automatically created the very +first time you use a Midi Kit function. + +You can call BMidiRoster's functions like this: + +\code +producer = BMidiRoster::FindProducer(someID); +\endcode + +Or using the slightly more annoying: + +\code +BMidiRoster* roster = BMidiRoster::MidiRoster(); +if (roster != NULL) +{ + producer = roster->FindProducer(someID); +} +\endcode +*/ + +/*! +\fn BMidiEndpoint* BMidiRoster::NextEndpoint(int32* id) +\brief Returns the next endpoint from the roster + +The "next endpoint" means: the endpoint with the ID that follows id. So if you +set id to 3, the first possible endpoint it returns is endpoint 4. No endpoint +can have ID 0, so passing 0 gives you the first endpoint. If you pass NULL +instead of an ID, NextEndpoint() always returns NULL. When the function +returns, it sets id to the ID of the endpoint that was found. If no more +endpoints exist, NextEndpoint() returns NULL and id is not changed. +NextEndpoint() does not return locally created endpoints, even if they +are Register()'ed. + +Usage example: + +\code +int32 id = 0; +BMidiEndpoint* endp; +while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL) +{ + ...do something with endpoint ... + endp->Release(); // don't forget! +} +\endcode + +Remember that NextEndpoint() bumps the endpoint's reference count, so you +should always \link BMidiEndpoint::Release() Release() \endlink it when you are +done. + +*/ + +/*! +\fn BMidiProducer* BMidiRoster::NextProducer(int32* id) +\brief Returns the next producer from the roster + +Like NextEndpoint(), but only returns producer endpoints. + +\sa NextConsumer +\sa NextEndpoint +*/ + +/*! +\fn BMidiConsumer* BMidiRoster::NextConsumer(int32* id) +\brief Returns the next consumer from the roster + +Like NextEndpoint(), but only returns consumer endpoints. + +\sa NextProducer +\sa NextEndpoint +*/ + +/*! +\fn BMidiEndpoint* BMidiRoster::FindEndpoint( + int32 id, bool localOnly = false) +\brief Returns the endpoint with the specified ID + +FindEndpoint() will always find any local endpoints created by this +application; they do not have to be published with Register() first. If +localOnly is false, FindEndpoint() also looks at remote endpoints, otherwise +only local endpoints will be resolved. Returns NULL if no such endpoint could +be found. + +You should use a dynamic_cast to convert the BMidiEndpoint into a producer or +consumer: + +\code +BMidiEndpoint* endp = ...; +BMidiProducer* prod = NULL; +BMidiConsumer* cons = NULL; + +if (endp->IsProducer()) +{ + prod = dynamic_cast(endp); +} +else if (endp->IsConsumer()) +{ + cons = dynamic_cast(endp); +} +\endcode + +Remember that FindEndpoint() increments the endpoint's reference count, so you +should always \link BMidiEndpoint::Release() Release() \endlink an endpoint +when you are done with it: + +\code +BMidiEndpoint* endp = BMidiRoster::FindEndpoint(someID); +if (endp != NULL) +{ + ...do stuff with the endpoint... + endp->Release(); +} +\endcode + +*/ + +/*! +\fn BMidiProducer* BMidiRoster::FindProducer( + int32 id, bool localOnly = false) +\brief Finds the producer with the specified ID + +Like FindEndpoint(), but only looks for producer endpoints. Returns NULL if no +endpoint with that ID exists, or if that endpoint is not a producer. + +\sa FindConsumer +\sa FindEndpoint +*/ + +/*! +\fn BMidiConsumer* BMidiRoster::FindConsumer( + int32 id, bool localOnly = false) +\brief Finds the consumer with the specified ID + +Like FindEndpoint(), but only looks for consumer endpoints. Returns NULL if no +endpoint with that ID exists, or if that endpoint is not a consumer. + +\sa FindProducer +\sa FindEndpoint +*/ + +/*! +\fn void BMidiRoster::StartWatching(const BMessenger* msngr) +\brief Start receiving notifications from the Midi Roster + +When you start watching, BMidiRoster sends you notifications for all currently +published remote endpoints, and all the current connections +between them. (At this point, BMidiRoster does not let you know about +connections between unpublished endpoints, nor does it tell you anything about +your local endpoints, even though they may be published.) + +Thereafter, you'll receive notifications any time something important happens +to an object. The application that performs these operations is itself not +notified. The assumption here is that you already know about these changes, +because you are the one that is performing them. + +The notifications are BMessages with code B_MIDI_EVENT. You specify the +BMessenger that will be used to send these messages. Each message contains a +field called be:op that describes the type of notification. + +The "registered" and "unregistered" notifications are sent when a remote +endpoint Register()'s or Unregister()'s, respectively. You don't receive these +notifications when you register or unregister your local endpoints, but the +other apps will. + + + + + +
be:opint32B_MIDI_REGISTERED
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
+ + + + + +
be:opint32B_MIDI_UNREGISTERED
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
+ +The "connected" and "disconnected" notifications are sent when a consumer \link +BMidiProducer::Connect() Connect()\endlink's to a producer, or when they \link +BMidiProducer::Disconnect() Disconnect() \endlink. You will receive these +notifications when any two endpoints connect or disconnect, even if they +are not published. (The purpose of which is debatable.) You won't receive the +notifications if you are the one making the connection, even if both endpoints +are remote. You will be notified when another app connects one of your +published endpoints. + + + + + +
be:opint32B_MIDI_CONNECTED
be:producerint32ID of the connector
be:consumerint32ID of the connectee
+ + + + + +
be:opint32B_MIDI_DISCONNECTED
be:producerint32ID of the connector
be:consumerint32ID of the connectee
+ +The following notifications are sent when an endpoint's attributes are changed. +You receive these notifications only if another application is changing one of +its published endpoints. + + + + + + +
be:opint32B_MIDI_CHANGED_NAME
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:namestringthe endpoint's new name
+ + + + + + +
be:opint32B_MIDI_CHANGED_LATENCY
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:latencyint64the new latency (microseconds)
+ + + + + + +
be:opint32B_MIDI_CHANGED_PROPERTIES
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:propertiesBMessagethe new properties
+ +Typical usage example: + +\code +void MyView::AttachedToWindow() +{ + BMessenger msgr(this); + BMidiRoster::StartWatching(&msgr); +} + +void MyView::MessageReceived(BMessage* msg) +{ + switch (msg->what) + { + case B_MIDI_EVENT: + HandleMidiEvent(msg); + break; + default: + super::MessageReceived(msg); + break; + } +} +\endcode + +\sa \ref bmidiop "The possible be:op codes (BMidiOp)" +*/ + +/*! +\fn void BMidiRoster::StopWatching() +\brief Stop receiving notifications from the Midi Roster +\sa StartWatching() +*/ + +/*! +\fn status_t BMidiRoster::Register(BMidiEndpoint* object) +\brief Publishes an endpoint to other applications + +Calls BMidiEndpoint's \link BMidiEndpoint::Register() Register() \endlink +method to publish an endpoint, which makes it visible to other applications. + +*/ + +/*! +\fn status_t BMidiRoster::Unregister(BMidiEndpoint* object) +\brief Hides an endpoint from other applications + +Calls BMidiEndpoint's \link BMidiEndpoint::Unregister() Unregister() \endlink +method to hide a previously published endpoint from other applications. + +*/ + +/*! +\fn BMidiRoster* BMidiRoster::MidiRoster() +\brief Returns a pointer to the only instance of BMidiRoster + +There is no real reason use this function, since all BMidiRoster's public +function are static. + +*/