summaryrefslogtreecommitdiff
path: root/libs/midi++2
diff options
context:
space:
mode:
authorTaybin Rutkin <taybin@taybin.com>2005-05-13 20:47:18 +0000
committerTaybin Rutkin <taybin@taybin.com>2005-05-13 20:47:18 +0000
commitd09f6b3016bacbc2871a8946cbb24ad705076509 (patch)
treef27312839c2a772cb2ce068a4f28b2449ad869df /libs/midi++2
Initial revision
git-svn-id: svn://localhost/trunk/ardour2@4 d708f5d6-7413-0410-9779-e7cbd77b26cf
Diffstat (limited to 'libs/midi++2')
-rw-r--r--libs/midi++2/.cvsignore3
-rw-r--r--libs/midi++2/AUTHORS0
-rw-r--r--libs/midi++2/COPYING340
-rw-r--r--libs/midi++2/ChangeLog9
-rw-r--r--libs/midi++2/NEWS0
-rw-r--r--libs/midi++2/README0
-rw-r--r--libs/midi++2/SConscript48
-rw-r--r--libs/midi++2/alsa_sequencer_midiport.cc156
-rw-r--r--libs/midi++2/coremidi_midiport.cc144
-rw-r--r--libs/midi++2/fd_midiport.cc181
-rw-r--r--libs/midi++2/fifomidi.cc44
-rw-r--r--libs/midi++2/libmidi++.pc.in11
-rw-r--r--libs/midi++2/libmidi++.spec.in71
-rw-r--r--libs/midi++2/midi++/.DS_Storebin0 -> 6148 bytes
-rw-r--r--libs/midi++2/midi++/.cvsignore1
-rw-r--r--libs/midi++2/midi++/alsa_rawmidi.h43
-rw-r--r--libs/midi++2/midi++/alsa_sequencer.h63
-rw-r--r--libs/midi++2/midi++/channel.h161
-rw-r--r--libs/midi++2/midi++/controllable.h92
-rw-r--r--libs/midi++2/midi++/coremidi_midiport.h67
-rw-r--r--libs/midi++2/midi++/factory.h40
-rw-r--r--libs/midi++2/midi++/fd_midiport.h94
-rw-r--r--libs/midi++2/midi++/fifomidi.h47
-rw-r--r--libs/midi++2/midi++/manager.h88
-rw-r--r--libs/midi++2/midi++/mmc.h261
-rw-r--r--libs/midi++2/midi++/nullmidi.h62
-rw-r--r--libs/midi++2/midi++/parser.h189
-rw-r--r--libs/midi++2/midi++/port.h147
-rw-r--r--libs/midi++2/midi++/port_request.h60
-rw-r--r--libs/midi++2/midi++/types.h69
-rw-r--r--libs/midi++2/midi.cc170
-rw-r--r--libs/midi++2/midichannel.cc304
-rw-r--r--libs/midi++2/midicontrollable.cc325
-rw-r--r--libs/midi++2/midifactory.cc95
-rw-r--r--libs/midi++2/midimanager.cc374
-rw-r--r--libs/midi++2/midiparser.cc800
-rw-r--r--libs/midi++2/midiport.cc131
-rw-r--r--libs/midi++2/miditrace.cc64
-rw-r--r--libs/midi++2/mmc.cc577
-rw-r--r--libs/midi++2/mmctest.cc112
-rw-r--r--libs/midi++2/mtc.cc329
-rw-r--r--libs/midi++2/port_request.cc80
42 files changed, 5852 insertions, 0 deletions
diff --git a/libs/midi++2/.cvsignore b/libs/midi++2/.cvsignore
new file mode 100644
index 0000000000..b1322ae10d
--- /dev/null
+++ b/libs/midi++2/.cvsignore
@@ -0,0 +1,3 @@
+libmidi++.pc
+libmidi++.spec
+version.cc
diff --git a/libs/midi++2/AUTHORS b/libs/midi++2/AUTHORS
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/midi++2/AUTHORS
diff --git a/libs/midi++2/COPYING b/libs/midi++2/COPYING
new file mode 100644
index 0000000000..d60c31a97a
--- /dev/null
+++ b/libs/midi++2/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/libs/midi++2/ChangeLog b/libs/midi++2/ChangeLog
new file mode 100644
index 0000000000..c40b4b5e82
--- /dev/null
+++ b/libs/midi++2/ChangeLog
@@ -0,0 +1,9 @@
+2005-05-08 Taybin Rutkin <taybin@earthlink.net>
+ * Brought up Jesse's CoreMIDI threading fix.
+
+2005-04-14 Taybin Rutkin <taybin@earthlink.net>
+ * Brought up Paul's parser fix from midi++ 1.16.
+
+2005-04-01 Taybin Rutkin <taybin@earthlink.net>
+ * Updated for sigc++-2.0.
+ * Incremented version number to 2.0.0.
diff --git a/libs/midi++2/NEWS b/libs/midi++2/NEWS
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/midi++2/NEWS
diff --git a/libs/midi++2/README b/libs/midi++2/README
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/libs/midi++2/README
diff --git a/libs/midi++2/SConscript b/libs/midi++2/SConscript
new file mode 100644
index 0000000000..7c2d3964ce
--- /dev/null
+++ b/libs/midi++2/SConscript
@@ -0,0 +1,48 @@
+# -*- python -*-
+
+import glob
+
+Import('env libraries')
+
+midi2 = env.Copy()
+midi2.Merge([ libraries['sigc2'], libraries['xml'], libraries['pbd3'] ])
+
+domain = 'midipp'
+
+midi2.Append(DOMAIN=domain,MAJOR=2,MINOR=1,MICRO=1)
+
+sources = Split("""
+fd_midiport.cc
+fifomidi.cc
+midi.cc
+midichannel.cc
+midicontrollable.cc
+midifactory.cc
+midimanager.cc
+midiparser.cc
+midiport.cc
+mmc.cc
+mtc.cc
+port_request.cc
+version.cc
+""")
+
+sysdep_sources = Split ("""
+alsa_sequencer_midiport.cc
+coremidi_midiport.cc
+""")
+
+if env['SYSMIDI'] == 'CoreMIDI':
+ sysdep_src = [ 'coremidi_midiport.cc' ]
+ midi2.Append (CCFLAGS="-DWITH_COREMIDI")
+else:
+ sysdep_src = [ 'alsa_sequencer_midiport.cc' ]
+ midi2.Append (CCFLAGS="-DWITH_ALSA")
+
+midi2.Append(CCFLAGS="-D_REENTRANT -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE")
+midi2.Append(CCFLAGS="-DLIBSIGC_DISABLE_DEPRECATED")
+
+midi2.VersionBuild(['version.cc','midi++/version.h'], 'SConscript')
+
+libmidi2 = midi2.StaticLibrary('midi++', [ sources, sysdep_src ])
+Default(libmidi2)
diff --git a/libs/midi++2/alsa_sequencer_midiport.cc b/libs/midi++2/alsa_sequencer_midiport.cc
new file mode 100644
index 0000000000..e0f6ad33e9
--- /dev/null
+++ b/libs/midi++2/alsa_sequencer_midiport.cc
@@ -0,0 +1,156 @@
+/*
+ Copyright (C) 2004 Paul Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <fcntl.h>
+#include <cerrno>
+
+#include <midi++/types.h>
+#include <midi++/alsa_sequencer.h>
+#include <midi++/port_request.h>
+
+//#define DOTRACE 1
+
+#ifdef DOTRACE
+#define TR_FN() (cerr << __FUNCTION__ << endl)
+#define TR_VAL(v) (cerr << __FILE__ " " << __LINE__ << " " #v "=" << v << endl)
+#else
+#define TR_FN()
+#define TR_VAL(v)
+#endif
+
+
+
+
+using namespace std;
+using namespace MIDI;
+
+ALSA_SequencerMidiPort::ALSA_SequencerMidiPort (PortRequest &req)
+ : Port (req)
+ , seq (0)
+ , decoder (0)
+ , encoder (0)
+{
+ TR_FN();
+ int err;
+ if (0 <= (err = CreatePorts (req)) &&
+ 0 <= (err = snd_midi_event_new (1024, &decoder)) && // Length taken from ARDOUR::Session::midi_read ()
+ 0 <= (err = snd_midi_event_new (64, &encoder))) { // Length taken from ARDOUR::Session::mmc_buffer
+ snd_midi_event_init (decoder);
+ snd_midi_event_init (encoder);
+ _ok = true;
+ req.status = PortRequest::OK;
+ } else
+ req.status = PortRequest::Unknown;
+}
+
+ALSA_SequencerMidiPort::~ALSA_SequencerMidiPort ()
+{
+ if (decoder)
+ snd_midi_event_free (decoder);
+ if (encoder)
+ snd_midi_event_free (encoder);
+ if (seq)
+ snd_seq_close (seq);
+}
+
+int ALSA_SequencerMidiPort::selectable () const
+{
+ struct pollfd pfd[1];
+ if (0 <= snd_seq_poll_descriptors (seq, pfd, 1, POLLIN | POLLOUT)) {
+ return pfd[0].fd;
+ }
+ return -1;
+}
+
+int ALSA_SequencerMidiPort::write (byte *msg, size_t msglen)
+{
+ TR_FN ();
+ int R;
+ snd_midi_event_reset_encode (encoder);
+ int nwritten = snd_midi_event_encode (encoder, msg, msglen, &SEv);
+ TR_VAL (nwritten);
+ if (0 < nwritten) {
+ if (0 <= (R = snd_seq_event_output (seq, &SEv)) &&
+ 0 <= (R = snd_seq_drain_output (seq))) {
+ bytes_written += nwritten;
+ if (output_parser) {
+ output_parser->raw_preparse (*output_parser, msg, nwritten);
+ for (int i = 0; i < nwritten; i++) {
+ output_parser->scanner (msg[i]);
+ }
+ output_parser->raw_postparse (*output_parser, msg, nwritten);
+ }
+ return nwritten;
+ } else {
+ TR_VAL(R);
+ return R;
+ }
+ } else
+ return nwritten;
+}
+
+int ALSA_SequencerMidiPort::read (byte *buf, size_t max)
+{
+ TR_FN();
+ int err;
+ snd_seq_event_t *ev;
+ if (0 <= (err = snd_seq_event_input (seq, &ev))) {
+ TR_VAL(err);
+ err = snd_midi_event_decode (decoder, buf, max, ev);
+ }
+
+ if (err > 0) {
+ bytes_read += err;
+
+ if (input_parser) {
+ input_parser->raw_preparse (*input_parser, buf, err);
+ for (int i = 0; i < err; i++) {
+ input_parser->scanner (buf[i]);
+ }
+ input_parser->raw_postparse (*input_parser, buf, err);
+ }
+ }
+ return -ENOENT == err ? 0 : err;
+}
+
+int ALSA_SequencerMidiPort::CreatePorts (PortRequest &req)
+{
+ int err;
+ if (0 <= (err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX,
+ (req.mode & O_NONBLOCK) ? SND_SEQ_NONBLOCK : 0))) {
+ snd_seq_set_client_name (seq, req.devname);
+ unsigned int caps = 0;
+ if (req.mode == O_WRONLY || req.mode == O_RDWR)
+ caps |= SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE;
+ if (req.mode == O_RDONLY || req.mode == O_RDWR)
+ caps |= SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ;
+ err = snd_seq_create_simple_port (seq, req.tagname, caps, SND_SEQ_PORT_TYPE_MIDI_GENERIC);
+ if (err >= 0) {
+ port_id = err;
+ snd_seq_ev_clear (&SEv);
+ snd_seq_ev_set_source (&SEv, port_id);
+ snd_seq_ev_set_subs (&SEv);
+ snd_seq_ev_set_direct (&SEv);
+ } else
+ snd_seq_close (seq);
+ }
+ return err;
+}
+
diff --git a/libs/midi++2/coremidi_midiport.cc b/libs/midi++2/coremidi_midiport.cc
new file mode 100644
index 0000000000..8d1d927b7b
--- /dev/null
+++ b/libs/midi++2/coremidi_midiport.cc
@@ -0,0 +1,144 @@
+/*
+ Copyright (C) 2004 Paul Davis
+ Copyright (C) 2004 Grame
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <fcntl.h>
+#include <cerrno>
+
+#include <midi++/coremidi_midiport.h>
+#include <midi++/types.h>
+#include <midi++/port_request.h>
+#include <mach/mach_time.h>
+
+#include <pbd/pthread_utils.h>
+
+using namespace std;
+using namespace MIDI;
+
+MIDITimeStamp CoreMidi_MidiPort::MIDIGetCurrentHostTime()
+{
+ return mach_absolute_time();
+}
+
+CoreMidi_MidiPort::CoreMidi_MidiPort (PortRequest &req) : Port (req)
+{
+ firstrecv = true;
+ int err;
+ if (0 == (err = Open(req))) {
+ _ok = true;
+ req.status = PortRequest::OK;
+ } else
+ req.status = PortRequest::Unknown;
+}
+
+CoreMidi_MidiPort::~CoreMidi_MidiPort () {Close();}
+
+void CoreMidi_MidiPort::Close ()
+{
+ if (midi_destination) MIDIEndpointDispose(midi_destination);
+ if (midi_source) MIDIEndpointDispose(midi_source);
+ if (midi_client) MIDIClientDispose(midi_client);
+}
+
+int CoreMidi_MidiPort::write (byte *msg, size_t msglen)
+{
+ OSStatus err;
+ MIDIPacketList* pktlist = (MIDIPacketList*)midi_buffer;
+ MIDIPacket* packet = MIDIPacketListInit(pktlist);
+ packet = MIDIPacketListAdd(pktlist,sizeof(midi_buffer),packet,MIDIGetCurrentHostTime(),msglen,msg);
+
+ if (packet) {
+
+ err = MIDIReceived(midi_source,pktlist);
+ if (err != noErr) {
+ //error << "MIDIReceived error" << err << endmsg.
+ }
+
+ bytes_written += msglen;
+ return msglen;
+ }else{
+ return 0;
+ }
+}
+
+int CoreMidi_MidiPort::Open (PortRequest &req)
+{
+ OSStatus err;
+ CFStringRef coutputStr;
+ string str;
+
+ coutputStr = CFStringCreateWithCString(0, req.devname, CFStringGetSystemEncoding());
+ err = MIDIClientCreate(coutputStr, 0, 0, &midi_client);
+ CFRelease(coutputStr);
+ if (!midi_client) {
+ //error << "Cannot open CoreMidi client : " << err << endmsg.
+ goto error;
+ }
+
+ str = req.tagname + string("_in");
+ coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding());
+ err = MIDIDestinationCreate(midi_client, coutputStr, read_proc, this, &midi_destination);
+ CFRelease(coutputStr);
+ if (!midi_destination) {
+ //error << "Cannot create CoreMidi destination : " << err << endmsg.
+ goto error;
+ }
+
+ str = req.tagname + string("_out");
+ coutputStr = CFStringCreateWithCString(0, str.c_str(), CFStringGetSystemEncoding());
+ err = MIDISourceCreate(midi_client, coutputStr, &midi_source);
+ CFRelease(coutputStr);
+ if (!midi_source) {
+ //error << "Cannot create CoreMidi source : " << err << endmsg.
+ goto error;
+ }
+
+ return err;
+
+error:
+ Close();
+ return err;
+}
+
+void CoreMidi_MidiPort::read_proc (const MIDIPacketList *pktlist, void *refCon, void *connRefCon)
+{
+ CoreMidi_MidiPort* driver = (CoreMidi_MidiPort*)refCon;
+ MIDIPacket *packet = (MIDIPacket *)pktlist->packet;
+
+ if (driver->firstrecv) {
+ driver->firstrecv = false;
+ PBD::ThreadCreated (pthread_self(), "COREMIDI");
+ }
+
+ for (unsigned int i = 0; i < pktlist->numPackets; ++i) {
+
+ driver->bytes_read += packet->length;
+
+ if (driver->input_parser) {
+ driver->input_parser->raw_preparse (*driver->input_parser, packet->data, packet->length);
+ for (int i = 0; i < packet->length; i++) {
+ driver->input_parser->scanner (packet->data[i]);
+ }
+ driver->input_parser->raw_postparse (*driver->input_parser, packet->data, packet->length);
+ }
+
+ packet = MIDIPacketNext(packet);
+ }
+}
+
diff --git a/libs/midi++2/fd_midiport.cc b/libs/midi++2/fd_midiport.cc
new file mode 100644
index 0000000000..2ced63c259
--- /dev/null
+++ b/libs/midi++2/fd_midiport.cc
@@ -0,0 +1,181 @@
+/*
+ Copyright (C) 1999 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <fcntl.h>
+#include <cerrno>
+
+#include <pbd/error.h>
+#include <pbd/pathscanner.h>
+
+#include <midi++/types.h>
+#include <midi++/fd_midiport.h>
+
+using namespace std;
+using namespace MIDI;
+
+string *FD_MidiPort::midi_dirpath = 0;
+string *FD_MidiPort::midi_filename_pattern = 0;
+
+FD_MidiPort::FD_MidiPort (PortRequest &req,
+ const string &dirpath,
+ const string &pattern)
+ : Port (req)
+{
+ open (req);
+
+ if (_fd < 0) {
+ switch (errno) {
+ case EBUSY:
+ error << "MIDI: port device in use" << endmsg;
+ req.status = PortRequest::Busy;
+ break;
+ case ENOENT:
+ error << "MIDI: no such port device" << endmsg;
+ req.status = PortRequest::NoSuchFile;
+ break;
+ case EACCES:
+ error << "MIDI: access to port denied" << endmsg;
+ req.status = PortRequest::NotAllowed;
+ break;
+ default:
+ req.status = PortRequest::Unknown;
+ }
+ } else {
+ _ok = true;
+ req.status = PortRequest::OK;
+
+ if (midi_dirpath == 0) {
+ midi_dirpath = new string (dirpath);
+ midi_filename_pattern = new string (pattern);
+ }
+
+ if (req.mode & O_NONBLOCK == 0) {
+ /* we unconditionally set O_NONBLOCK during
+ open, but the request didn't ask for it,
+ so remove it.
+ */
+
+ int flags = fcntl (_fd, F_GETFL, 0);
+ fcntl (_fd, F_SETFL, flags & ~(O_NONBLOCK));
+ }
+ }
+}
+
+void
+FD_MidiPort::open (PortRequest &req)
+
+{
+ int mode = req.mode | O_NONBLOCK;
+ _fd = ::open (req.devname, mode);
+}
+
+vector<string *> *
+FD_MidiPort::list_devices ()
+
+{
+ PathScanner scanner;
+
+ return scanner (*midi_dirpath, *midi_filename_pattern, false, true);
+}
+
+int
+FD_MidiPort::selectable () const
+
+{
+ long flags;
+
+ /* turn on non-blocking mode, since we plan to use select/poll
+ to tell us when there is data to read.
+ */
+
+ flags = fcntl (_fd, F_GETFL);
+ flags |= O_NONBLOCK;
+
+ if (fcntl (_fd, F_SETFL, flags)) {
+ error << "FD_MidiPort: could not turn on non-blocking mode"
+ << " (" << strerror (errno)
+ << ')'
+ << endmsg;
+
+ return -1;
+ }
+
+ return _fd;
+}
+
+
+int
+FD_MidiPort::do_slow_write (byte *msg, unsigned int msglen)
+
+{
+ size_t n;
+ size_t i;
+
+ for (n = 0; n < msglen; n++) {
+
+ if (::write (_fd, &msg[n], 1) != 1) {
+ break;
+ }
+
+ bytes_written++;
+ for (i = 0; i < slowdown * 10000; i++);
+ }
+
+
+ if (n && output_parser) {
+ output_parser->raw_preparse (*output_parser, msg, n);
+ for (unsigned int i = 0; i < n; i++) {
+ output_parser->scanner (msg[i]);
+ }
+ output_parser->raw_postparse (*output_parser, msg, n);
+ }
+
+ return n;
+}
+
+int
+FD_MidiPort::read (byte* buf, size_t max)
+{
+ int nread;
+
+ if ((_mode & O_ACCMODE) == O_WRONLY) {
+ return -EACCES;
+ }
+
+ // cerr << "MIDI: read up to " << max << " from " << _fd << " (" << name() << ')' << endl;
+
+ if ((nread = ::read (_fd, buf, max)) > 0) {
+ bytes_read += nread;
+
+ // cerr << " read " << nread << endl;
+
+ if (input_parser) {
+ input_parser->raw_preparse
+ (*input_parser, buf, nread);
+ for (int i = 0; i < nread; i++) {
+ input_parser->scanner (buf[i]);
+ }
+ input_parser->raw_postparse
+ (*input_parser, buf, nread);
+ }
+ }
+
+ return nread;
+}
diff --git a/libs/midi++2/fifomidi.cc b/libs/midi++2/fifomidi.cc
new file mode 100644
index 0000000000..7bb126ddeb
--- /dev/null
+++ b/libs/midi++2/fifomidi.cc
@@ -0,0 +1,44 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <midi++/types.h>
+#include <midi++/fifomidi.h>
+
+using namespace MIDI;
+
+FIFO_MidiPort::FIFO_MidiPort (PortRequest &req)
+ : FD_MidiPort (req, ".", "midi")
+
+{
+}
+
+void
+FIFO_MidiPort::open (PortRequest &req)
+
+{
+ /* This is a placeholder for the fun-and-games I think we will
+ need to do with FIFO's.
+ */
+
+ _fd = ::open (req.devname, req.mode|O_NDELAY);
+}
diff --git a/libs/midi++2/libmidi++.pc.in b/libs/midi++2/libmidi++.pc.in
new file mode 100644
index 0000000000..10be08c7a7
--- /dev/null
+++ b/libs/midi++2/libmidi++.pc.in
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@/midi++
+
+Name: libmidi++
+Version: @VERSION@
+Description: libmidi++, a C++ library for handling MIDI I/O, including MMC and MTC
+Requires: libpbd
+Libs: -L${libdir} -lmidipp @AUDIO_LIBS@ @NON_PKG_LIBS@
+Cflags: -I${includedir} @NON_PKG_CFLAGS@
diff --git a/libs/midi++2/libmidi++.spec.in b/libs/midi++2/libmidi++.spec.in
new file mode 100644
index 0000000000..fbeb6a263a
--- /dev/null
+++ b/libs/midi++2/libmidi++.spec.in
@@ -0,0 +1,71 @@
+Summary: A high level MIDI handling library.
+%define lib_name midi++
+Name: lib%{lib_name}
+Version: @VERSION@
+Release: 2
+Copyright: GPL
+Source: ftp://ftp.quasimodo.org/pub/libs/midi++/current/%{name}-%{version}.tar.gz
+Url: http://www.quasimodo.org
+Vendor: Paul Barton Davis <pbd@op.net>
+Packager: jfm3 <jfm3@acm.org>
+Group: System Environment/Libraries
+Prefix: %{_prefix}
+BuildRoot: %{_tmppath}/%{name}-%{version}-root
+
+%description
+
+libmidi++ is a C++ library that uses libsigc++ to make handling MIDI
+I/O from MIDI hardware trivial. You can attach "callbacks" to any
+MIDI input or output event, ranging from each individual byte, to a
+particular message type on a particular channel. It also provides
+channel "state".
+
+%prep
+%setup -q
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_FLAGS" ./configure $ARCH_FLAGS --prefix=%{prefix}
+make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+install -d -m 755 $RPM_BUILD_ROOT%{prefix}/{{include,lib}/%{lib_name}}
+make install INSTALL="%(which install) -p" prefix=$RPM_BUILD_ROOT%{prefix}
+
+%post
+/sbin/ldconfig
+
+%postun
+/sbin/ldconfig
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%doc README AUTHORS NEWS COPYING*
+%{prefix}/lib/libmidipp.so*
+
+%package devel
+Summary: A high level MIDI handling library -- develper version.
+Group: System Environment/Libraries
+
+%description devel
+
+libmidi++ is a C++ library that uses libsigc++ to make handling MIDI
+I/O from MIDI hardware trivial. You can attach "callbacks" to any
+MIDI input or output event, ranging from each individual byte, to a
+particular message type on a particular channel. It also provides
+channel "state".
+
+This package holds static libraries and headers needed by developers
+who wish to use libmidi++ in their programs.
+
+%files devel
+%defattr(-,root,root)
+%{prefix}/include/midi++/*
+%{prefix}/lib/libmidipp.a
+%{prefix}/lib/libmidipp.la
+%{prefix}/bin/midi++-config
+%{prefix}/share/aclocal/midi++.m4
+%{prefix}/share/aclocal/midi.m4
diff --git a/libs/midi++2/midi++/.DS_Store b/libs/midi++2/midi++/.DS_Store
new file mode 100644
index 0000000000..5008ddfcf5
--- /dev/null
+++ b/libs/midi++2/midi++/.DS_Store
Binary files differ
diff --git a/libs/midi++2/midi++/.cvsignore b/libs/midi++2/midi++/.cvsignore
new file mode 100644
index 0000000000..67020331ba
--- /dev/null
+++ b/libs/midi++2/midi++/.cvsignore
@@ -0,0 +1 @@
+version.h
diff --git a/libs/midi++2/midi++/alsa_rawmidi.h b/libs/midi++2/midi++/alsa_rawmidi.h
new file mode 100644
index 0000000000..655b673174
--- /dev/null
+++ b/libs/midi++2/midi++/alsa_rawmidi.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __alsa_rawmidi_h__
+#define __alsa_rawmidi_h__
+
+#include <vector>
+#include <string>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <midi++/port.h>
+#include <midi++/fd_midiport.h>
+
+class ALSA_RawMidiPort : public MIDI::FD_MidiPort
+
+{
+ public:
+ ALSA_RawMidiPort (MIDI::PortRequest &req)
+ : FD_MidiPort (req, "/dev/snd", "midi") {}
+ virtual ~ALSA_RawMidiPort () {}
+};
+
+
+#endif // __alsa_rawmidi_h__
+
diff --git a/libs/midi++2/midi++/alsa_sequencer.h b/libs/midi++2/midi++/alsa_sequencer.h
new file mode 100644
index 0000000000..8ddb2a7dd7
--- /dev/null
+++ b/libs/midi++2/midi++/alsa_sequencer.h
@@ -0,0 +1,63 @@
+/*
+ Copyright (C) 2004 Paul Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __alsa_sequencer_midiport_h__
+#define __alsa_sequencer_midiport_h__
+
+#include <vector>
+#include <string>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <alsa/asoundlib.h>
+#include <midi++/port.h>
+
+namespace MIDI {
+
+class ALSA_SequencerMidiPort : public Port
+
+{
+ public:
+ ALSA_SequencerMidiPort (PortRequest &req);
+ virtual ~ALSA_SequencerMidiPort ();
+
+ /* select(2)/poll(2)-based I/O */
+
+ virtual int selectable() const;
+
+ protected:
+ /* Direct I/O */
+
+ int write (byte *msg, size_t msglen);
+ int read (byte *buf, size_t max);
+
+ private:
+ snd_seq_t *seq;
+ snd_midi_event_t *decoder, *encoder;
+ int port_id;
+ snd_seq_event_t SEv;
+ int CreatePorts(PortRequest &req);
+
+};
+
+}; /* namespace MIDI */
+
+#endif // __alsa_sequencer_midiport_h__
+
diff --git a/libs/midi++2/midi++/channel.h b/libs/midi++2/midi++/channel.h
new file mode 100644
index 0000000000..1efde3cb93
--- /dev/null
+++ b/libs/midi++2/midi++/channel.h
@@ -0,0 +1,161 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midichannel_h__
+#define __midichannel_h__
+
+#include <queue>
+
+#include <sigc++/sigc++.h>
+
+#include <midi++/types.h>
+#include <midi++/parser.h>
+
+namespace MIDI {
+
+class Port;
+
+class Channel : public sigc::trackable {
+
+ public:
+ Channel (byte channel_number, Port &);
+
+ Port &midi_port() { return port; }
+ byte channel() { return channel_number; }
+ byte program() { return program_number; }
+ byte bank() { return bank_number; }
+ byte pressure () { return chanpress; }
+ byte poly_pressure (byte n) { return polypress[n]; }
+
+ byte last_note_on () {
+ return _last_note_on;
+ }
+ byte last_on_velocity () {
+ return _last_on_velocity;
+ }
+ byte last_note_off () {
+ return _last_note_off;
+ }
+ byte last_off_velocity () {
+ return _last_off_velocity;
+ }
+
+ pitchbend_t pitchbend () {
+ return pitch_bend;
+ }
+
+ controller_value_t controller_value (byte n) {
+ return controller_val[n%128];
+ }
+
+ controller_value_t *controller_addr (byte n) {
+ return &controller_val[n%128];
+ }
+
+ void set_controller (byte n, byte val) {
+ controller_val[n%128] = val;
+ }
+
+ int channel_msg (byte id, byte val1, byte val2);
+
+ int all_notes_off () {
+ return channel_msg (MIDI::controller, 123, 0);
+ }
+
+ int control (byte id, byte value) {
+ return channel_msg (MIDI::controller, id, value);
+ }
+
+ int note_on (byte note, byte velocity) {
+ return channel_msg (MIDI::on, note, velocity);
+ }
+
+ int note_off (byte note, byte velocity) {
+ return channel_msg (MIDI::off, note, velocity);
+ }
+
+ int aftertouch (byte value) {
+ return channel_msg (MIDI::chanpress, value, 0);
+ }
+
+ int poly_aftertouch (byte note, byte value) {
+ return channel_msg (MIDI::polypress, note, value);
+ }
+
+ int program_change (byte value) {
+ return channel_msg (MIDI::program, value, 0);
+ }
+
+ int pitchbend (byte msb, byte lsb) {
+ return channel_msg (MIDI::pitchbend, lsb, msb);
+ }
+
+ protected:
+ friend class Port;
+ void connect_input_signals ();
+ void connect_output_signals ();
+
+ private:
+ Port &port;
+
+ /* Current channel values */
+
+ byte channel_number;
+ byte bank_number;
+ byte program_number;
+ byte rpn_msb;
+ byte rpn_lsb;
+ byte nrpn_msb;
+ byte nrpn_lsb;
+ byte chanpress;
+ byte polypress[128];
+ bool controller_14bit[128];
+ controller_value_t controller_val[128];
+ byte controller_msb[128];
+ byte controller_lsb[128];
+ byte _last_note_on;
+ byte _last_on_velocity;
+ byte _last_note_off;
+ byte _last_off_velocity;
+ pitchbend_t pitch_bend;
+ bool _omni;
+ bool _poly;
+ bool _mono;
+ size_t _notes_on;
+
+ void reset (bool notes_off = true);
+
+ void process_note_off (Parser &, EventTwoBytes *);
+ void process_note_on (Parser &, EventTwoBytes *);
+ void process_controller (Parser &, EventTwoBytes *);
+ void process_polypress (Parser &, EventTwoBytes *);
+ void process_program_change (Parser &, byte);
+ void process_chanpress (Parser &, byte);
+ void process_pitchbend (Parser &, pitchbend_t);
+ void process_reset (Parser &);
+};
+
+}; /* namespace MIDI */
+
+#endif // __midichannel_h__
+
+
+
+
diff --git a/libs/midi++2/midi++/controllable.h b/libs/midi++2/midi++/controllable.h
new file mode 100644
index 0000000000..3fa108bb46
--- /dev/null
+++ b/libs/midi++2/midi++/controllable.h
@@ -0,0 +1,92 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __qm_midicontrollable_h__
+#define __qm_midicontrollable_h__
+
+#include <string>
+
+#include <sigc++/sigc++.h>
+
+#include <midi++/types.h>
+
+namespace MIDI {
+
+class Channel;
+class Port;
+class Parser;
+
+class Controllable : public sigc::trackable
+{
+ public:
+ Controllable (Port *, bool bistate = false);
+ virtual ~Controllable ();
+
+ void midi_rebind (Port *, channel_t channel=-1);
+ void midi_forget ();
+ void learn_about_external_control ();
+ void stop_learning ();
+ void drop_external_control ();
+
+ virtual void set_value (float) = 0;
+
+ sigc::signal<void> learning_started;
+ sigc::signal<void> learning_stopped;
+
+ bool get_control_info (channel_t&, eventType&, byte&);
+ void set_control_type (channel_t, eventType, byte);
+
+ bool get_midi_feedback () { return feedback; }
+ void set_midi_feedback (bool val) { feedback = val; }
+
+ Port * get_port() { return port; }
+
+ std::string control_description() const { return _control_description; }
+
+ void send_midi_feedback (float);
+
+ private:
+ bool bistate;
+ int midi_msg_id; /* controller ID or note number */
+ sigc::connection midi_sense_connection[2];
+ sigc::connection midi_learn_connection;
+ size_t connections;
+ Port* port;
+ eventType control_type;
+ byte control_additional;
+ channel_t control_channel;
+ std::string _control_description;
+ bool feedback;
+
+ void midi_receiver (Parser &p, byte *, size_t);
+ void midi_sense_note (Parser &, EventTwoBytes *, bool is_on);
+ void midi_sense_note_on (Parser &p, EventTwoBytes *tb);
+ void midi_sense_note_off (Parser &p, EventTwoBytes *tb);
+ void midi_sense_controller (Parser &, EventTwoBytes *);
+ void midi_sense_program_change (Parser &, byte);
+ void midi_sense_pitchbend (Parser &, pitchbend_t);
+
+ void bind_midi (channel_t, eventType, byte);
+};
+
+}; /* namespace MIDI */
+
+#endif // __qm_midicontrollable_h__
+
diff --git a/libs/midi++2/midi++/coremidi_midiport.h b/libs/midi++2/midi++/coremidi_midiport.h
new file mode 100644
index 0000000000..e02a225784
--- /dev/null
+++ b/libs/midi++2/midi++/coremidi_midiport.h
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2004 Paul Davis
+ Copyright (C) 2004 Grame
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ÊSee the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef __coremidi_midiport_h__
+#define __coremidi_midiport_h__
+
+#include <list>
+#include <string>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <midi++/port.h>
+
+#include <CoreMIDI/CoreMIDI.h>
+
+namespace MIDI {
+
+ class CoreMidi_MidiPort:public Port {
+ public:
+ CoreMidi_MidiPort(PortRequest & req);
+ virtual ~ CoreMidi_MidiPort();
+
+ virtual int selectable() const {
+ return -1;
+ }
+ protected:
+ /* Direct I/O */
+ int write(byte * msg, size_t msglen);
+ int read(byte * buf, size_t max) {
+ return 0;
+ } /* CoreMidi callback */
+ static void read_proc(const MIDIPacketList * pktlist,
+ void *refCon, void *connRefCon);
+
+ private:
+ byte midi_buffer[1024];
+ MIDIClientRef midi_client;
+ MIDIEndpointRef midi_destination;
+ MIDIEndpointRef midi_source;
+
+ int Open(PortRequest & req);
+ void Close();
+ static MIDITimeStamp MIDIGetCurrentHostTime();
+
+ bool firstrecv;
+ };
+
+}; /* namespace MIDI */
+
+#endif // __coremidi_midiport_h__
diff --git a/libs/midi++2/midi++/factory.h b/libs/midi++2/midi++/factory.h
new file mode 100644
index 0000000000..1543f68cdc
--- /dev/null
+++ b/libs/midi++2/midi++/factory.h
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midi_factory_h__
+#define __midi_factory_h__
+
+#include <vector>
+#include <string>
+
+#include <midi++/port.h>
+
+namespace MIDI {
+
+class PortFactory {
+ public:
+ Port *create_port (PortRequest &req);
+
+ static void add_port_request (std::vector<PortRequest *> &reqs,
+ const std::string &reqstr);
+};
+
+}; /* namespace MIDI */
+
+#endif // __midi_factory_h__
diff --git a/libs/midi++2/midi++/fd_midiport.h b/libs/midi++2/midi++/fd_midiport.h
new file mode 100644
index 0000000000..853af9d7b4
--- /dev/null
+++ b/libs/midi++2/midi++/fd_midiport.h
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 1999 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __fd_midiport_h__
+#define __fd_midiport_h__
+
+#include <vector>
+#include <string>
+#include <cerrno>
+
+#include <cerrno>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <midi++/port.h>
+#include <midi++/port_request.h>
+
+namespace MIDI {
+
+class FD_MidiPort : public Port
+
+{
+ public:
+ FD_MidiPort (PortRequest &req,
+ const std::string &dirpath,
+ const std::string &pattern);
+
+ virtual ~FD_MidiPort () {
+ ::close (_fd);
+ }
+
+ virtual int selectable() const;
+ static std::vector<std::string *> *list_devices ();
+
+ protected:
+ int _fd;
+ virtual void open (PortRequest &req);
+
+ virtual int write (byte *msg, size_t msglen) {
+ int nwritten;
+
+ if ((_mode & O_ACCMODE) == O_RDONLY) {
+ return -EACCES;
+ }
+
+ if (slowdown) {
+ return do_slow_write (msg, msglen);
+ }
+
+ if ((nwritten = ::write (_fd, msg, msglen)) > 0) {
+ bytes_written += nwritten;
+
+ if (output_parser) {
+ output_parser->raw_preparse
+ (*output_parser, msg, nwritten);
+ for (int i = 0; i < nwritten; i++) {
+ output_parser->scanner (msg[i]);
+ }
+ output_parser->raw_postparse
+ (*output_parser, msg, nwritten);
+ }
+ }
+ return nwritten;
+ }
+
+ virtual int read (byte *buf, size_t max);
+
+ private:
+ static std::string *midi_dirpath;
+ static std::string *midi_filename_pattern;
+
+ int do_slow_write (byte *msg, unsigned int msglen);
+};
+
+}; /*namespace MIDI */
+
+#endif // __fd_midiport_h__
diff --git a/libs/midi++2/midi++/fifomidi.h b/libs/midi++2/midi++/fifomidi.h
new file mode 100644
index 0000000000..eb8778d4d5
--- /dev/null
+++ b/libs/midi++2/midi++/fifomidi.h
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __fifomidi_h__
+#define __fifomidi_h__
+
+#include <fcntl.h>
+#include <vector>
+#include <string>
+#include <unistd.h>
+
+#include <midi++/port.h>
+#include <midi++/port_request.h>
+#include <midi++/fd_midiport.h>
+
+namespace MIDI {
+
+class FIFO_MidiPort : public MIDI::FD_MidiPort
+
+{
+ public:
+ FIFO_MidiPort (PortRequest &req);
+ ~FIFO_MidiPort () {};
+
+ private:
+ void open (PortRequest &req);
+};
+
+}; /* namespace MIDI */
+
+#endif // __fifomidi_h__
diff --git a/libs/midi++2/midi++/manager.h b/libs/midi++2/midi++/manager.h
new file mode 100644
index 0000000000..4889aad8c9
--- /dev/null
+++ b/libs/midi++2/midi++/manager.h
@@ -0,0 +1,88 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midi_manager_h__
+#define __midi_manager_h__
+
+#include <map>
+#include <string>
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+
+namespace MIDI {
+
+class Manager {
+ public:
+ ~Manager ();
+
+ Port *add_port (PortRequest &);
+ int remove_port (std::string port);
+
+ Port *port (std::string name);
+ Port *port (size_t number);
+
+ size_t nports () { return ports_by_device.size(); }
+
+ /* defaults for clients who are not picky */
+
+ Port *inputPort;
+ Port *outputPort;
+ channel_t inputChannelNumber;
+ channel_t outputChannelNumber;
+
+ int set_input_port (size_t port);
+ int set_input_port (std::string);
+ int set_output_port (size_t port);
+ int set_output_port (std::string);
+ int set_input_channel (channel_t);
+ int set_output_channel (channel_t);
+
+ int foreach_port (int (*func)(const Port &, size_t n, void *),
+ void *arg);
+
+ typedef std::map<std::string, Port *> PortMap;
+
+ const PortMap& get_midi_ports() const { return ports_by_tag; }
+
+ static Manager *instance () {
+ if (theManager == 0) {
+ theManager = new Manager;
+ }
+ return theManager;
+ }
+
+ static int parse_port_request (std::string str, Port::Type type);
+
+ private:
+ /* This is a SINGLETON pattern */
+
+ Manager ();
+
+ static Manager *theManager;
+ PortMap ports_by_device; /* canonical */
+ PortMap ports_by_tag; /* may contain duplicate Ports */
+
+ void close_ports ();
+};
+
+}; /* namespace MIDI */
+
+#endif // __midi_manager_h__
diff --git a/libs/midi++2/midi++/mmc.h b/libs/midi++2/midi++/mmc.h
new file mode 100644
index 0000000000..7b51b33a72
--- /dev/null
+++ b/libs/midi++2/midi++/mmc.h
@@ -0,0 +1,261 @@
+/*
+ Copyright (C) 2000 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midipp_mmc_h_h__
+#define __midipp_mmc_h_h__
+
+#include <sigc++/sigc++.h>
+#include <midi++/types.h>
+
+namespace MIDI {
+
+class Port;
+class Parser;
+
+class MachineControl : public sigc::trackable
+
+{
+ public:
+ typedef byte CommandSignature[60];
+ typedef byte ResponseSignature[60];
+
+ enum Command {
+ cmdStop = 0x1,
+ cmdPlay = 0x2,
+ cmdDeferredPlay = 0x3,
+ cmdFastForward = 0x4,
+ cmdRewind = 0x5,
+ cmdRecordStrobe = 0x6,
+
+ cmdRecordExit = 0x7,
+ cmdRecordPause = 0x8,
+ cmdPause = 0x9,
+ cmdEject = 0xA,
+ cmdChase = 0xB,
+ cmdCommandErrorReset = 0xC,
+ cmdMmcReset = 0xD,
+
+ cmdIllegalMackieJogStart = 0x20,
+ cmdIllegalMackieJogStop = 0x21,
+
+ cmdWrite = 0x40,
+ cmdMaskedWrite = 0x41,
+ cmdRead = 0x42,
+ cmdUpdate = 0x43,
+ cmdLocate = 0x44,
+ cmdVariablePlay = 0x45,
+ cmdSearch = 0x46,
+
+ cmdShuttle = 0x47,
+ cmdStep = 0x48,
+ cmdAssignSystemMaster = 0x49,
+ cmdGeneratorCommand = 0x4A,
+ cmdMtcCommand = 0x4B,
+ cmdMove = 0x4C,
+ cmdAdd = 0x4D,
+
+ cmdSubtract = 0x4E,
+ cmdDropFrameAdjust = 0x4F,
+ cmdProcedure = 0x50,
+ cmdEvent = 0x51,
+ cmdGroup = 0x52,
+ cmdCommandSegment = 0x53,
+ cmdDeferredVariablePlay = 0x54,
+
+ cmdRecordStrobeVariable = 0x55,
+
+ cmdWait = 0x7C,
+ cmdResume = 0x7F,
+ };
+
+ MachineControl (Port &port,
+ float MMCVersion,
+ CommandSignature &cs,
+ ResponseSignature &rs);
+
+ Port &port() { return _port; }
+
+ void set_device_id (byte id);
+ static bool is_mmc (byte *sysex_buf, size_t len);
+
+ /* Signals to connect to if you want to run "callbacks"
+ when certain MMC commands are received.
+ */
+
+ sigc::signal<void,MachineControl &> Stop;
+ sigc::signal<void,MachineControl &> Play;
+ sigc::signal<void,MachineControl &> DeferredPlay;
+ sigc::signal<void,MachineControl &> FastForward;
+ sigc::signal<void,MachineControl &> Rewind;
+ sigc::signal<void,MachineControl &> RecordStrobe;
+ sigc::signal<void,MachineControl &> RecordExit;
+ sigc::signal<void,MachineControl &> RecordPause;
+ sigc::signal<void,MachineControl &> Pause;
+ sigc::signal<void,MachineControl &> Eject;
+ sigc::signal<void,MachineControl &> Chase;
+ sigc::signal<void,MachineControl &> CommandErrorReset;
+ sigc::signal<void,MachineControl &> MmcReset;
+
+ sigc::signal<void,MachineControl &> JogStart;
+ sigc::signal<void,MachineControl &> JogStop;
+
+ sigc::signal<void,MachineControl &> Write;
+ sigc::signal<void,MachineControl &> MaskedWrite;
+ sigc::signal<void,MachineControl &> Read;
+ sigc::signal<void,MachineControl &> Update;
+ sigc::signal<void,MachineControl &> VariablePlay;
+ sigc::signal<void,MachineControl &> Search;
+ sigc::signal<void,MachineControl &> AssignSystemMaster;
+ sigc::signal<void,MachineControl &> GeneratorCommand;
+ sigc::signal<void,MachineControl &> MidiTimeCodeCommand;
+ sigc::signal<void,MachineControl &> Move;
+ sigc::signal<void,MachineControl &> Add;
+ sigc::signal<void,MachineControl &> Subtract;
+ sigc::signal<void,MachineControl &> DropFrameAdjust;
+ sigc::signal<void,MachineControl &> Procedure;
+ sigc::signal<void,MachineControl &> Event;
+ sigc::signal<void,MachineControl &> Group;
+ sigc::signal<void,MachineControl &> CommandSegment;
+ sigc::signal<void,MachineControl &> DeferredVariablePlay;
+ sigc::signal<void,MachineControl &> RecordStrobeVariable;
+ sigc::signal<void,MachineControl &> Wait;
+ sigc::signal<void,MachineControl &> Resume;
+
+ /* The second argument is the shuttle speed, the third is
+ true if the direction is "forwards", false for "reverse"
+ */
+
+ sigc::signal<void,MachineControl &,float,bool> Shuttle;
+
+ /* The second argument specifies the desired track record enabled
+ status.
+ */
+
+ sigc::signal<void,MachineControl &,size_t,bool>
+ TrackRecordStatusChange;
+
+ /* The second argument points to a byte array containing
+ the locate target value in MMC Standard Time Code
+ format (5 bytes, roughly: hrs/mins/secs/frames/subframes)
+ */
+
+ sigc::signal<void,MachineControl &, const byte *> Locate;
+
+ /* The second argument is the number of steps to jump */
+
+ sigc::signal<void,MachineControl &, int> Step;
+
+ protected:
+
+#define MMC_NTRACKS 48
+
+ /* MMC Information fields (think "registers") */
+
+ CommandSignature commandSignature;
+ ResponseSignature responseSignature;
+
+ byte updateRate;
+ byte responseError;
+ byte commandError;
+ byte commandErrorLevel;
+
+ byte motionControlTally;
+ byte velocityTally;
+ byte stopMode;
+ byte fastMode;
+ byte recordMode;
+ byte recordStatus;
+ bool trackRecordStatus[MMC_NTRACKS];
+ bool trackRecordReady[MMC_NTRACKS];
+ byte globalMonitor;
+ byte recordMonitor;
+ byte trackSyncMonitor;
+ byte trackInputMonitor;
+ byte stepLength;
+ byte playSpeedReference;
+ byte fixedSpeed;
+ byte lifterDefeat;
+ byte controlDisable;
+ byte trackMute;
+ byte failure;
+ byte selectedTimeCode;
+ byte shortSelectedTimeCode;
+ byte timeStandard;
+ byte selectedTimeCodeSource;
+ byte selectedTimeCodeUserbits;
+ byte selectedMasterCode;
+ byte requestedOffset;
+ byte actualOffset;
+ byte lockDeviation;
+ byte shortSelectedMasterCode;
+ byte shortRequestedOffset;
+ byte shortActualOffset;
+ byte shortLockDeviation;
+ byte resolvedPlayMode;
+ byte chaseMode;
+ byte generatorTimeCode;
+ byte shortGeneratorTimeCode;
+ byte generatorCommandTally;
+ byte generatorSetUp;
+ byte generatorUserbits;
+ byte vitcInsertEnable;
+ byte midiTimeCodeInput;
+ byte shortMidiTimeCodeInput;
+ byte midiTimeCodeCommandTally;
+ byte midiTimeCodeSetUp;
+ byte gp0;
+ byte gp1;
+ byte gp2;
+ byte gp3;
+ byte gp4;
+ byte gp5;
+ byte gp6;
+ byte gp7;
+ byte shortGp0;
+ byte shortGp1;
+ byte shortGp2;
+ byte shortGp3;
+ byte shortGp4;
+ byte shortGp5;
+ byte shortGp6;
+ byte shortGp7;
+ byte procedureResponse;
+ byte eventResponse;
+ byte responseSegment;
+ byte wait;
+ byte resume;
+
+ private:
+ byte _device_id;
+ MIDI::Port &_port;
+
+ void process_mmc_message (Parser &p, byte *, size_t len);
+
+ int do_masked_write (byte *, size_t len);
+ int do_locate (byte *, size_t len);
+ int do_step (byte *, size_t len);
+ int do_shuttle (byte *, size_t len);
+
+ void write_track_record_ready (byte *, size_t len);
+};
+
+}; /* namespace MIDI */
+
+#endif /* __midipp_mmc_h_h__ */
diff --git a/libs/midi++2/midi++/nullmidi.h b/libs/midi++2/midi++/nullmidi.h
new file mode 100644
index 0000000000..a94b1015b0
--- /dev/null
+++ b/libs/midi++2/midi++/nullmidi.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __nullmidi_h__
+#define __nullmidi_h__
+
+#include <fcntl.h>
+#include <vector>
+#include <string>
+
+#include <midi++/port.h>
+#include <midi++/port_request.h>
+
+namespace MIDI {
+
+class Null_MidiPort : public Port
+
+{
+ public:
+ Null_MidiPort (PortRequest &req)
+ : Port (req) {
+
+ /* reset devname and tagname */
+
+ _devname = "nullmidi";
+ _tagname = "null";
+ _type = Port::Null;
+ _ok = true;
+ }
+
+ virtual ~Null_MidiPort () {};
+
+ virtual int write (byte *msg, size_t msglen) {
+ return msglen;
+ }
+
+ virtual int read (byte *buf, size_t max) {
+ return 0;
+ }
+
+ virtual int selectable() const { return -1; }
+};
+
+}; /* namespace MIDI */
+
+#endif // __nullmidi_h__
diff --git a/libs/midi++2/midi++/parser.h b/libs/midi++2/midi++/parser.h
new file mode 100644
index 0000000000..4ac07cc15d
--- /dev/null
+++ b/libs/midi++2/midi++/parser.h
@@ -0,0 +1,189 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midi_parse_h__
+#define __midi_parse_h__
+
+#include <string>
+#include <iostream>
+
+#include <sigc++/sigc++.h>
+
+#include <midi++/types.h>
+
+namespace MIDI {
+
+class Port;
+class Parser;
+
+typedef sigc::signal<void, Parser &, byte> OneByteSignal;
+typedef sigc::signal<void, Parser &, EventTwoBytes *> TwoByteSignal;
+typedef sigc::signal<void, Parser &, pitchbend_t> PitchBendSignal;
+typedef sigc::signal<void, Parser &, byte *, size_t> Signal;
+
+class Parser : public sigc::trackable {
+ public:
+ Parser (Port &p);
+ ~Parser ();
+
+ /* signals that anyone can connect to */
+
+ OneByteSignal bank_change;
+ TwoByteSignal note_on;
+ TwoByteSignal note_off;
+ TwoByteSignal poly_pressure;
+ OneByteSignal pressure;
+ OneByteSignal program_change;
+ PitchBendSignal pitchbend;
+ TwoByteSignal controller;
+
+ OneByteSignal channel_bank_change[16];
+ TwoByteSignal channel_note_on[16];
+ TwoByteSignal channel_note_off[16];
+ TwoByteSignal channel_poly_pressure[16];
+ OneByteSignal channel_pressure[16];
+ OneByteSignal channel_program_change[16];
+ PitchBendSignal channel_pitchbend[16];
+ TwoByteSignal channel_controller[16];
+ sigc::signal<void, Parser &> channel_active_preparse[16];
+ sigc::signal<void, Parser &> channel_active_postparse[16];
+
+ OneByteSignal mtc_quarter_frame;
+
+ Signal raw_preparse;
+ Signal raw_postparse;
+ Signal any;
+ Signal sysex;
+ Signal mmc;
+ Signal position;
+ Signal song;
+
+ Signal mtc;
+ sigc::signal<void,Parser&> mtc_qtr;
+
+ sigc::signal<void, Parser &> all_notes_off;
+ sigc::signal<void, Parser &> tune;
+ sigc::signal<void, Parser &> timing;
+ sigc::signal<void, Parser &> start;
+ sigc::signal<void, Parser &> stop;
+ sigc::signal<void, Parser &> contineu; /* note spelling */
+ sigc::signal<void, Parser &> active_sense;
+ sigc::signal<void, Parser &> reset;
+ sigc::signal<void, Parser &> eox;
+
+ /* This should really be protected, but then derivatives of Port
+ can't access it.
+ */
+
+ void scanner (byte c);
+
+ size_t *message_counts() { return message_counter; }
+ const char *midi_event_type_name (MIDI::eventType);
+ void trace (bool onoff, std::ostream *o, const std::string &prefix = "");
+ bool tracing() { return trace_stream != 0; }
+ Port &port() { return _port; }
+
+ void set_offline (bool);
+ bool offline() const { return _offline; }
+ sigc::signal<void> OfflineStatusChanged;
+
+ sigc::signal<int, byte *, size_t> edit;
+
+ void set_mmc_forwarding (bool yn) {
+ _mmc_forward = yn;
+ }
+
+ /* MTC */
+
+ enum MTC_Status {
+ MTC_Stopped = 0,
+ MTC_Forward,
+ MTC_Backward
+ };
+
+ MTC_FPS mtc_fps() const { return _mtc_fps; }
+ MTC_Status mtc_running() const { return _mtc_running; }
+ const byte *mtc_current() const { return _mtc_time; }
+ bool mtc_locked() const { return _mtc_locked; }
+
+ sigc::signal<void,MTC_Status> mtc_status;
+ sigc::signal<bool> mtc_skipped;
+ sigc::signal<void,const byte*,bool> mtc_time;
+
+ void set_mtc_forwarding (bool yn) {
+ _mtc_forward = yn;
+ }
+
+ void reset_mtc_state ();
+
+ private:
+ Port &_port;
+ /* tracing */
+
+ std::ostream *trace_stream;
+ std::string trace_prefix;
+ void trace_event (Parser &p, byte *msg, size_t len);
+ sigc::connection trace_connection;
+
+ size_t message_counter[256];
+
+ enum ParseState {
+ NEEDSTATUS,
+ NEEDONEBYTE,
+ NEEDTWOBYTES,
+ VARIABLELENGTH
+ };
+ ParseState state;
+ unsigned char *msgbuf;
+ int msglen;
+ int msgindex;
+ MIDI::eventType msgtype;
+ channel_t channel;
+ bool _offline;
+ bool runnable;
+ bool was_runnable;
+ bool _mmc_forward;
+ bool _mtc_forward;
+ int expected_mtc_quarter_frame_code;
+ byte _mtc_time[4];
+ byte _qtr_mtc_time[4];
+ unsigned long consecutive_qtr_frame_cnt;
+ MTC_FPS _mtc_fps;
+ MTC_Status _mtc_running;
+ bool _mtc_locked;
+ byte last_qtr_frame;
+
+ ParseState pre_variable_state;
+ MIDI::eventType pre_variable_msgtype;
+ byte last_status_byte;
+
+ void channel_msg (byte);
+ void realtime_msg (byte);
+ void system_msg (byte);
+ void signal (byte *msg, size_t msglen);
+ bool possible_mmc (byte *msg, size_t msglen);
+ bool possible_mtc (byte *msg, size_t msglen);
+ void process_mtc_quarter_frame (byte *msg);
+};
+
+}; /* namespace MIDI */
+
+#endif // __midi_parse_h__
+
diff --git a/libs/midi++2/midi++/port.h b/libs/midi++2/midi++/port.h
new file mode 100644
index 0000000000..81e28615d0
--- /dev/null
+++ b/libs/midi++2/midi++/port.h
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __libmidi_port_h__
+#define __libmidi_port_h__
+
+#include <string>
+
+#include <sigc++/sigc++.h>
+
+#include <pbd/selectable.h>
+#include <midi++/types.h>
+#include <midi++/parser.h>
+
+namespace MIDI {
+
+class Channel;
+class PortRequest;
+
+class Port : public sigc::trackable {
+ public:
+ enum Type {
+ Unknown,
+ ALSA_RawMidi,
+ ALSA_Sequencer,
+ CoreMidi_MidiPort,
+ Null,
+ FIFO,
+ };
+
+
+ Port (PortRequest &);
+ virtual ~Port ();
+
+ /* Direct I/O */
+
+ virtual int write (byte *msg, size_t msglen) = 0;
+ virtual int read (byte *buf, size_t max) = 0;
+
+ /* slowdown i/o to a loop of single byte emissions
+ interspersed with a busy loop of 10000 * this value.
+
+ This may be ignored by a particular instance
+ of this virtual class. See FD_MidiPort for an
+ example of where it used.
+ */
+
+ void set_slowdown (size_t n) { slowdown = n; }
+
+ /* select(2)/poll(2)-based I/O */
+
+ virtual int selectable() const = 0;
+
+ void selector_read_callback (Select::Selectable *, Select::Condition);
+
+ static void xforms_read_callback (int cond, int fd, void *ptr);
+ static void gtk_read_callback (void *ptr, int fd, int cond);
+
+ static void write_callback (byte *msg, unsigned int len, void *);
+
+ Channel *channel (channel_t chn) {
+ return _channel[chn&0x7F];
+ }
+
+ Parser *input() { return input_parser; }
+ Parser *output() { return output_parser; }
+
+ void iostat (int *written, int *read,
+ const size_t **in_counts,
+ const size_t **out_counts) {
+
+ *written = bytes_written;
+ *read = bytes_read;
+ if (input_parser) {
+ *in_counts = input_parser->message_counts();
+ } else {
+ *in_counts = 0;
+ }
+ if (output_parser) {
+ *out_counts = output_parser->message_counts();
+ } else {
+ *out_counts = 0;
+ }
+ }
+
+ int midimsg (byte *msg, size_t len) {
+ return !(write (msg, len) == (int) len);
+ }
+
+ int three_byte_msg (byte a, byte b, byte c) {
+ byte msg[3];
+
+ msg[0] = a;
+ msg[1] = b;
+ msg[2] = c;
+
+ return !(write (msg, 3) == 3);
+ }
+
+ int clock ();
+
+ const char *device () const { return _devname.c_str(); }
+ const char *name () const { return _tagname.c_str(); }
+ Type type () const { return _type; }
+ int mode () const { return _mode; }
+ bool ok () const { return _ok; }
+ size_t number () const { return _number; }
+
+ protected:
+ bool _ok;
+ Type _type;
+ std::string _devname;
+ std::string _tagname;
+ int _mode;
+ size_t _number;
+ Channel *_channel[16];
+ sigc::connection thru_connection;
+ unsigned int bytes_written;
+ unsigned int bytes_read;
+ Parser *input_parser;
+ Parser *output_parser;
+ size_t slowdown;
+
+ private:
+ static size_t nports;
+};
+
+}; /* namespace MIDI */
+
+#endif // __libmidi_port_h__
+
diff --git a/libs/midi++2/midi++/port_request.h b/libs/midi++2/midi++/port_request.h
new file mode 100644
index 0000000000..28a0d1d70b
--- /dev/null
+++ b/libs/midi++2/midi++/port_request.h
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 1999 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#ifndef __midi_port_request_h__
+#define __midi_port_request_h__
+
+#include <string>
+
+namespace MIDI {
+
+struct PortRequest {
+ enum Status {
+ Unknown,
+ OK,
+ Busy,
+ NoSuchFile,
+ TypeUnsupported,
+ NotAllowed
+ };
+ const char *devname;
+ const char *tagname;
+ int mode;
+ Port::Type type;
+ Status status;
+
+ PortRequest () {
+ devname = 0;
+ tagname = 0;
+ mode = 0;
+ type = Port::Unknown;
+ status = Unknown;
+ }
+
+ PortRequest (const std::string &xdev,
+ const std::string &xtag,
+ const std::string &xmode,
+ const std::string &xtype);
+};
+
+}; /* namespace MIDI */
+
+#endif // __midi_port_request_h__
+
diff --git a/libs/midi++2/midi++/types.h b/libs/midi++2/midi++/types.h
new file mode 100644
index 0000000000..b9d9bf33e7
--- /dev/null
+++ b/libs/midi++2/midi++/types.h
@@ -0,0 +1,69 @@
+#ifndef __midi_types_h__
+#define __midi_types_h__
+
+namespace MIDI {
+
+ typedef char channel_t;
+ typedef float controller_value_t;
+ typedef unsigned char byte;
+ typedef unsigned short pitchbend_t;
+
+ enum eventType {
+ none = 0x0,
+ raw = 0xF4, /* undefined in MIDI spec */
+ any = 0xF5, /* undefined in MIDI spec */
+ off = 0x80,
+ on = 0x90,
+ controller = 0xB0,
+ program = 0xC0,
+ chanpress = 0xD0,
+ polypress = 0xA0,
+ pitchbend = 0xE0,
+ sysex = 0xF0,
+ mtc_quarter = 0xF1,
+ position = 0xF2,
+ song = 0xF3,
+ tune = 0xF6,
+ eox = 0xF7,
+ timing = 0xF8,
+ start = 0xFA,
+ contineu = 0xFB, /* note spelling */
+ stop = 0xFC,
+ active = 0xFE,
+ reset = 0xFF
+ };
+
+ extern const char *controller_names[];
+ byte decode_controller_name (const char *name);
+
+ struct EventTwoBytes {
+ union {
+ byte note_number;
+ byte controller_number;
+ };
+ union {
+ byte velocity;
+ byte value;
+ };
+ };
+
+ enum MTC_FPS {
+ MTC_24_FPS = 0,
+ MTC_25_FPS = 1,
+ MTC_30_FPS_DROP = 2,
+ MTC_30_FPS = 3
+ };
+
+ enum MTC_Status {
+ MTC_Stopped = 0,
+ MTC_Forward,
+ MTC_Backward,
+ };
+
+}; /* namespace MIDI */
+
+#endif // __midi_types_h__
+
+
+
+
diff --git a/libs/midi++2/midi.cc b/libs/midi++2/midi.cc
new file mode 100644
index 0000000000..7c2dc835fb
--- /dev/null
+++ b/libs/midi++2/midi.cc
@@ -0,0 +1,170 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <string>
+#include <cstdlib>
+#include <midi++/types.h>
+
+const char *MIDI::controller_names[] = {
+ "bank (0)",
+ "mod (1)",
+ "breath (2)",
+ "ctrl 3",
+ "foot (4)",
+ "port tm (5)",
+ "data msb (6)",
+ "volume (7)",
+ "balance (8)",
+ "ctrl 9",
+ "pan (10)",
+ "express (11)",
+ "ctrl 12",
+ "ctrl 13",
+ "ctrl 14",
+ "ctrl 15",
+ "gpc 1",
+ "gpc 2",
+ "gpc 3",
+ "gpc 4",
+ "ctrl 20",
+ "ctrl 21",
+ "ctrl 22",
+ "ctrl 23",
+ "ctrl 24",
+ "ctrl 25",
+ "ctrl 26",
+ "ctrl 27",
+ "ctrl 28",
+ "ctrl 29",
+ "ctrl 30",
+ "ctrl 31",
+ "lsb 0 (32)",
+ "lsb 1 (33)",
+ "lsb 2 (34)",
+ "lsb 3 (35)",
+ "lsb 4 (36)",
+ "lsb 5 (37)",
+ "lsb 6 (38)",
+ "lsb 7 (39)",
+ "lsb 8 (40)",
+ "lsb 9 (41)",
+ "lsb 10 (42)",
+ "lsb 11 (43)",
+ "lsb 12 (44)",
+ "lsb 13 (45)",
+ "lsb 14 (46)",
+ "lsb 15 (47)",
+ "lsb 16 (48)",
+ "lsb 17 (49)",
+ "lsb 18 (50)",
+ "lsb 19 (51)",
+ "lsb 20 (52)",
+ "lsb 21 (53)",
+ "lsb 22 (54)",
+ "lsb 23 (55)",
+ "lsb 24 (56)",
+ "lsb 25 (57)",
+ "lsb 26 (58)",
+ "lsb 27 (59)",
+ "lsb 28 (60)",
+ "lsb 29 (61)",
+ "lsb 30 (62)",
+ "lsb 31 (63)",
+ "sustain (64)",
+ "portamento (65)",
+ "sostenuto (66)",
+ "soft ped (67)",
+ "ctrl 68",
+ "hold 2 (69)",
+ "ctrl 70",
+ "ctrl 71",
+ "ctrl 72",
+ "ctrl 73",
+ "ctrl 74",
+ "ctrl 75",
+ "ctrl 76",
+ "ctrl 77",
+ "ctrl 78",
+ "ctrl 79",
+ "gpc 5 (80)",
+ "gpc 6 (81)",
+ "gpc 7 (82)",
+ "gpc 8 (83)",
+ "ctrl 84",
+ "ctrl 85",
+ "ctrl 86",
+ "ctrl 87",
+ "ctrl 88",
+ "ctrl 89",
+ "ctrl 90",
+ "fx dpth (91)",
+ "tremolo (92)",
+ "chorus (93)",
+ "detune (94)",
+ "phaser (95)",
+ "data inc (96)",
+ "data dec (97)",
+ "nrpn lsb (98)",
+ "nrpn msg (99)",
+ "rpn lsb (100)",
+ "rpn msb (101)",
+ "ctrl 102",
+ "ctrl 103",
+ "ctrl 104",
+ "ctrl 105",
+ "ctrl 106",
+ "ctrl 107",
+ "ctrl 108",
+ "ctrl 109",
+ "ctrl 110",
+ "ctrl 111",
+ "ctrl 112",
+ "ctrl 113",
+ "ctrl 114",
+ "ctrl 115",
+ "ctrl 116",
+ "ctrl 117",
+ "ctrl 118",
+ "ctrl 119",
+ "snd off (120)",
+ "rst ctrl (121)",
+ "local (122)",
+ "notes off (123)",
+ "omni off (124)",
+ "omni on (125)",
+ "mono on (126)",
+ "poly on (127)",
+ 0
+};
+
+MIDI::byte
+MIDI::decode_controller_name (const char *name)
+
+{
+ char *lparen;
+ size_t len;
+
+ if ((lparen = strrchr (name, '(')) != 0) {
+ return atoi (lparen+1);
+ } else {
+ len = strcspn (name, "0123456789");
+ return atoi (name+len);
+ }
+}
diff --git a/libs/midi++2/midichannel.cc b/libs/midi++2/midichannel.cc
new file mode 100644
index 0000000000..a6759b7962
--- /dev/null
+++ b/libs/midi++2/midichannel.cc
@@ -0,0 +1,304 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <midi++/channel.h>
+
+using namespace sigc;
+using namespace MIDI;
+
+Channel::Channel (byte channelnum, Port &p) : port (p)
+{
+ channel_number = channelnum;
+
+ reset (false);
+}
+
+void
+Channel::connect_input_signals ()
+
+{
+ port.input()->channel_pressure[channel_number].connect
+ (mem_fun (*this, &Channel::process_chanpress));
+ port.input()->channel_note_on[channel_number].connect
+ (mem_fun (*this, &Channel::process_note_on));
+ port.input()->channel_note_off[channel_number].connect
+ (mem_fun (*this, &Channel::process_note_off));
+ port.input()->channel_poly_pressure[channel_number].connect
+ (mem_fun (*this, &Channel::process_polypress));
+ port.input()->channel_program_change[channel_number].connect
+ (mem_fun (*this, &Channel::process_program_change));
+ port.input()->channel_controller[channel_number].connect
+ (mem_fun (*this, &Channel::process_controller));
+ port.input()->channel_pitchbend[channel_number].connect
+ (mem_fun (*this, &Channel::process_pitchbend));
+ port.input()->reset.connect (mem_fun (*this, &Channel::process_reset));
+}
+
+void
+Channel::connect_output_signals ()
+
+{
+ port.output()->channel_pressure[channel_number].connect
+ (mem_fun (*this, &Channel::process_chanpress));
+ port.output()->channel_note_on[channel_number].connect
+ (mem_fun (*this, &Channel::process_note_on));
+ port.output()->channel_note_off[channel_number].connect
+ (mem_fun (*this, &Channel::process_note_off));
+ port.output()->channel_poly_pressure[channel_number].connect
+ (mem_fun (*this, &Channel::process_polypress));
+ port.output()->channel_program_change[channel_number].connect
+ (mem_fun (*this, &Channel::process_program_change));
+ port.output()->channel_controller[channel_number].connect
+ (mem_fun (*this, &Channel::process_controller));
+ port.output()->channel_pitchbend[channel_number].connect
+ (mem_fun (*this, &Channel::process_pitchbend));
+ port.output()->reset.connect (mem_fun (*this, &Channel::process_reset));
+}
+
+void
+Channel::reset (bool notes_off)
+{
+ program_number = channel_number;
+ bank_number = 0;
+ pitch_bend = 0;
+
+ _last_note_on = 0;
+ _last_note_off = 0;
+ _last_on_velocity = 0;
+ _last_off_velocity = 0;
+
+ if (notes_off) {
+ all_notes_off ();
+ }
+
+ memset (polypress, 0, sizeof (polypress));
+ memset (controller_msb, 0, sizeof (controller_msb));
+ memset (controller_lsb, 0, sizeof (controller_lsb));
+
+ /* zero all controllers XXX not necessarily the right thing */
+
+ memset (controller_val, 0, sizeof (controller_val));
+
+ for (int n = 0; n < 128; n++) {
+ controller_14bit[n] = false;
+ }
+
+ rpn_msb = 0;
+ rpn_lsb = 0;
+ nrpn_msb = 0;
+ nrpn_lsb = 0;
+
+ _omni = true;
+ _poly = false;
+ _mono = true;
+ _notes_on = 0;
+}
+
+void
+Channel::process_note_off (Parser &parser, EventTwoBytes *tb)
+
+{
+ _last_note_off = tb->note_number;
+ _last_off_velocity = tb->velocity;
+
+ if (_notes_on) {
+ _notes_on--;
+ }
+}
+
+void
+Channel::process_note_on (Parser &parser, EventTwoBytes *tb)
+
+{
+ _last_note_on = tb->note_number;
+ _last_on_velocity = tb->velocity;
+ _notes_on++;
+}
+
+void
+Channel::process_controller (Parser &parser, EventTwoBytes *tb)
+
+{
+ unsigned short cv;
+
+ /* XXX arguably need a lock here to protect non-atomic changes
+ to controller_val[...]. or rather, need to make sure that
+ all changes *are* atomic.
+ */
+
+ if (tb->controller_number <= 31) { /* unsigned: no test for >= 0 */
+
+ /* if this controller is already known to use 14 bits,
+ then treat this value as the MSB, and combine it
+ with the existing LSB.
+
+ otherwise, just treat it as a 7 bit value, and set
+ it directly.
+ */
+
+ cv = (unsigned short) controller_val[tb->controller_number];
+
+ if (controller_14bit[tb->controller_number]) {
+ cv = ((tb->value << 7) | (cv & 0x7f));
+ } else {
+ cv = tb->value;
+ }
+
+ controller_val[tb->controller_number] = (controller_value_t)cv;
+
+ } else if ((tb->controller_number >= 32 &&
+ tb->controller_number <= 63)) {
+
+ cv = (unsigned short) controller_val[tb->controller_number];
+
+ /* LSB for CC 0-31 arrived.
+
+ If this is the first time (i.e. its currently
+ flagged as a 7 bit controller), mark the
+ controller as 14 bit, adjust the existing value
+ to be the MSB, and OR-in the new LSB value.
+
+ otherwise, OR-in the new low 7bits with the old
+ high 7.
+ */
+
+ int cn = tb->controller_number - 32;
+
+ if (controller_14bit[cn] == false) {
+ controller_14bit[cn] = true;
+ cv = (cv << 7) | (tb->value & 0x7f);
+ } else {
+ cv = (cv & 0x3f80) | (tb->value & 0x7f);
+ }
+
+ controller_val[tb->controller_number] =
+ (controller_value_t) cv;
+ } else {
+
+ /* controller can only take 7 bit values */
+
+ controller_val[tb->controller_number] =
+ (controller_value_t) tb->value;
+ }
+
+ /* bank numbers are special, in that they have their own signal
+ */
+
+ if (tb->controller_number == 0) {
+ bank_number = (unsigned short) controller_val[0];
+ if (port.input()) {
+ port.input()->bank_change (*port.input(), bank_number);
+ port.input()->channel_bank_change[channel_number]
+ (*port.input(), bank_number);
+ }
+ }
+
+}
+
+void
+Channel::process_program_change (Parser &parser, byte val)
+
+{
+ program_number = val;
+}
+
+void
+Channel::process_chanpress (Parser &parser, byte val)
+
+{
+ chanpress = val;
+}
+
+void
+Channel::process_polypress (Parser &parser, EventTwoBytes *tb)
+
+{
+ polypress[tb->note_number] = tb->value;
+}
+
+void
+Channel::process_pitchbend (Parser &parser, pitchbend_t val)
+
+{
+ pitch_bend = val;
+}
+
+void
+Channel::process_reset (Parser &parser)
+
+{
+ reset ();
+}
+
+int
+Channel::channel_msg (byte id, byte val1, byte val2)
+
+{
+ unsigned char msg[3];
+ int len = 0;
+
+ msg[0] = id | (channel_number & 0xf);
+
+ switch (id) {
+ case off:
+ msg[1] = val1 & 0x7F;
+ msg[2] = val2 & 0x7F;
+ len = 3;
+ break;
+
+ case on:
+ msg[1] = val1 & 0x7F;
+ msg[2] = val2 & 0x7F;
+ len = 3;
+ break;
+
+ case MIDI::polypress:
+ msg[1] = val1 & 0x7F;
+ msg[2] = val2 & 0x7F;
+ len = 3;
+ break;
+
+ case controller:
+ msg[1] = val1 & 0x7F;
+ msg[2] = val2 & 0x7F;
+ len = 3;
+ break;
+
+ case MIDI::program:
+ msg[1] = val1 & 0x7F;
+ len = 2;
+ break;
+
+ case MIDI::chanpress:
+ msg[1] = val1 & 0x7F;
+ len = 2;
+ break;
+
+ case MIDI::pitchbend:
+ msg[1] = val1 & 0x7F;
+ msg[2] = val2 & 0x7F;
+ len = 3;
+ break;
+ }
+
+ return port.midimsg (msg, len);
+}
diff --git a/libs/midi++2/midicontrollable.cc b/libs/midi++2/midicontrollable.cc
new file mode 100644
index 0000000000..f17782f3c4
--- /dev/null
+++ b/libs/midi++2/midicontrollable.cc
@@ -0,0 +1,325 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <cstdio> /* for sprintf, sigh */
+#include <pbd/error.h>
+#include <midi++/port.h>
+#include <midi++/channel.h>
+#include <midi++/controllable.h>
+
+using namespace sigc;
+using namespace MIDI;
+
+Controllable::Controllable (Port *p, bool is_bistate)
+{
+ control_type = none;
+ _control_description = "MIDI Control: none";
+ control_additional = (byte) -1;
+ bistate = is_bistate;
+ connections = 0;
+ feedback = true; // for now
+
+ /* use channel 0 ("1") as the initial channel */
+
+ midi_rebind (p, 0);
+}
+
+Controllable::~Controllable ()
+{
+ drop_external_control ();
+}
+
+void
+Controllable::midi_forget ()
+{
+ /* stop listening for incoming messages, but retain
+ our existing event + type information.
+ */
+
+ if (connections > 0) {
+ midi_sense_connection[0].disconnect ();
+ }
+
+ if (connections > 1) {
+ midi_sense_connection[1].disconnect ();
+ }
+
+ connections = 0;
+ midi_learn_connection.disconnect ();
+
+}
+
+void
+Controllable::midi_rebind (Port *p, channel_t c)
+{
+ if ((port = p) == 0) {
+ midi_forget ();
+ } else {
+ if (c >= 0) {
+ bind_midi (c, control_type, control_additional);
+ } else {
+ midi_forget ();
+ }
+ }
+}
+
+void
+Controllable::learn_about_external_control ()
+{
+ drop_external_control ();
+
+ if (port) {
+ midi_learn_connection = port->input()->any.connect (mem_fun (*this, &Controllable::midi_receiver));
+ learning_started ();
+
+ } else {
+ info << "No MIDI port specified - external control disabled" << endmsg;
+ }
+}
+
+void
+Controllable::stop_learning ()
+{
+ midi_learn_connection.disconnect ();
+}
+
+void
+Controllable::drop_external_control ()
+{
+ if (connections > 0) {
+ midi_sense_connection[0].disconnect ();
+ }
+ if (connections > 1) {
+ midi_sense_connection[1].disconnect ();
+ }
+
+ connections = 0;
+ midi_learn_connection.disconnect ();
+
+ control_type = none;
+ control_additional = (byte) -1;
+}
+
+void
+Controllable::midi_sense_note_on (Parser &p, EventTwoBytes *tb)
+{
+ midi_sense_note (p, tb, true);
+}
+
+void
+Controllable::midi_sense_note_off (Parser &p, EventTwoBytes *tb)
+{
+ midi_sense_note (p, tb, false);
+}
+
+void
+Controllable::midi_sense_note (Parser &p, EventTwoBytes *msg, bool is_on)
+{
+ if (!bistate) {
+ set_value (msg->note_number/127.0);
+ } else {
+
+ /* Note: parser handles the use of zero velocity to
+ mean note off. if we get called with is_on=true, then we
+ got a *real* note on.
+ */
+
+ if (msg->note_number == control_additional) {
+ set_value (is_on ? 1 : 0);
+ }
+ }
+}
+
+void
+Controllable::midi_sense_controller (Parser &, EventTwoBytes *msg)
+{
+ if (control_additional == msg->controller_number) {
+ if (!bistate) {
+ set_value (msg->value/127.0);
+ } else {
+ if (msg->value > 64.0) {
+ set_value (1);
+ } else {
+ set_value (0);
+ }
+ }
+ }
+}
+
+void
+Controllable::midi_sense_program_change (Parser &p, byte msg)
+{
+ /* XXX program change messages make no sense for bistates */
+
+ if (!bistate) {
+ set_value (msg/127.0);
+ }
+}
+
+void
+Controllable::midi_sense_pitchbend (Parser &p, pitchbend_t pb)
+{
+ /* pitchbend messages make no sense for bistates */
+
+ /* XXX gack - get rid of assumption about typeof pitchbend_t */
+
+ set_value ((pb/(float) SHRT_MAX));
+}
+
+void
+Controllable::midi_receiver (Parser &p, byte *msg, size_t len)
+{
+ /* we only respond to channel messages */
+
+ if ((msg[0] & 0xF0) < 0x80 || (msg[0] & 0xF0) > 0xE0) {
+ return;
+ }
+
+ /* if the our port doesn't do input anymore, forget it ... */
+
+ if (!port->input()) {
+ return;
+ }
+
+ bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
+
+ learning_stopped ();
+}
+
+void
+Controllable::bind_midi (channel_t chn, eventType ev, MIDI::byte additional)
+{
+ char buf[64];
+
+ drop_external_control ();
+
+ control_type = ev;
+ control_channel = chn;
+ control_additional = additional;
+
+ if (port == 0 || port->input() == 0) {
+ return;
+ }
+
+ Parser& p = *port->input();
+
+ int chn_i = chn;
+ switch (ev) {
+ case MIDI::off:
+ midi_sense_connection[0] = p.channel_note_off[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_note_off));
+
+ /* if this is a bistate, connect to noteOn as well,
+ and we'll toggle back and forth between the two.
+ */
+
+ if (bistate) {
+ midi_sense_connection[1] = p.channel_note_on[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_note_on));
+ connections = 2;
+ } else {
+ connections = 1;
+ }
+ _control_description = "MIDI control: NoteOff";
+ break;
+
+ case MIDI::on:
+ midi_sense_connection[0] = p.channel_note_on[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_note_on));
+ if (bistate) {
+ midi_sense_connection[1] = p.channel_note_off[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_note_off));
+ connections = 2;
+ } else {
+ connections = 1;
+ }
+ _control_description = "MIDI control: NoteOn";
+ break;
+
+ case MIDI::controller:
+ midi_sense_connection[0] = p.channel_controller[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_controller));
+ connections = 1;
+ snprintf (buf, sizeof (buf), "MIDI control: Controller %d", control_additional);
+ _control_description = buf;
+ break;
+
+ case MIDI::program:
+ if (!bistate) {
+ midi_sense_connection[0] = p.channel_program_change[chn_i].connect
+ (mem_fun (*this,
+ &Controllable::midi_sense_program_change));
+ connections = 1;
+ _control_description = "MIDI control: ProgramChange";
+ }
+ break;
+
+ case MIDI::pitchbend:
+ if (!bistate) {
+ midi_sense_connection[0] = p.channel_pitchbend[chn_i].connect
+ (mem_fun (*this, &Controllable::midi_sense_pitchbend));
+ connections = 1;
+ _control_description = "MIDI control: Pitchbend";
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+Controllable::set_control_type (channel_t chn, eventType ev, MIDI::byte additional)
+{
+ bind_midi (chn, ev, additional);
+}
+
+bool
+Controllable::get_control_info (channel_t& chn, eventType& ev, byte& additional)
+{
+ if (control_type == none) {
+ chn = -1;
+ return false;
+ }
+
+ ev = control_type;
+ chn = control_channel;
+ additional = control_additional;
+
+ return true;
+}
+
+
+void
+Controllable::send_midi_feedback (float val)
+{
+ byte msg[3];
+
+ if (port == 0 || control_type == none) {
+ return;
+ }
+
+ msg[0] = (control_type & 0xF0) | (control_channel & 0xF);
+ msg[1] = control_additional;
+ msg[2] = (byte) (val * 127.0f);
+
+ port->write (msg, 3);
+}
+
diff --git a/libs/midi++2/midifactory.cc b/libs/midi++2/midifactory.cc
new file mode 100644
index 0000000000..38baada204
--- /dev/null
+++ b/libs/midi++2/midifactory.cc
@@ -0,0 +1,95 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <midi++/types.h>
+#include <midi++/factory.h>
+#include <midi++/nullmidi.h>
+#include <midi++/fifomidi.h>
+
+#ifdef WITH_ALSA
+#include <midi++/alsa_sequencer.h>
+#include <midi++/alsa_rawmidi.h>
+#endif // WITH_ALSA
+
+#ifdef WITH_COREMIDI
+#include <midi++/coremidi_midiport.h>
+#endif // WITH_COREMIDI
+
+
+using namespace std;
+using namespace MIDI;
+
+Port *
+PortFactory::create_port (PortRequest &req)
+
+{
+ Port *port;
+
+ switch (req.type) {
+#ifdef WITH_ALSA
+ case Port::ALSA_RawMidi:
+ port = new ALSA_RawMidiPort (req);
+ break;
+
+ case Port::ALSA_Sequencer:
+ port = new ALSA_SequencerMidiPort (req);
+ break;
+#endif // WITH_ALSA
+
+#if WITH_COREMIDI
+ case Port::CoreMidi_MidiPort:
+ port = new CoreMidi_MidiPort (req);
+ break;
+#endif // WITH_COREMIDI
+
+ case Port::Null:
+ port = new Null_MidiPort (req);
+ break;
+
+ case Port::FIFO:
+ port = new FIFO_MidiPort (req);
+ break;
+
+ default:
+ req.status = PortRequest::TypeUnsupported;
+ return 0;
+ }
+
+ req.status = PortRequest::OK;
+
+ return port;
+}
+
+void
+PortFactory::add_port_request (vector<PortRequest *> &reqs,
+ const string &str)
+
+{
+ PortRequest *req;
+
+ req = new PortRequest;
+ req->devname = strdup (str.c_str());
+ req->tagname = strdup (str.c_str());
+
+ req->mode = O_RDWR;
+ req->type = Port::ALSA_RawMidi;
+
+ reqs.push_back (req);
+}
+
diff --git a/libs/midi++2/midimanager.cc b/libs/midi++2/midimanager.cc
new file mode 100644
index 0000000000..7b3ed7d336
--- /dev/null
+++ b/libs/midi++2/midimanager.cc
@@ -0,0 +1,374 @@
+/*
+ Copyright (C) 1998-99 Paul Barton-Davis
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <fcntl.h>
+#include <pbd/error.h>
+#include <pbd/basename.h>
+
+#include <midi++/types.h>
+#include <midi++/manager.h>
+#include <midi++/factory.h>
+#include <midi++/channel.h>
+#include <midi++/port_request.h>
+
+using namespace std;
+using namespace MIDI;
+
+Manager *Manager::theManager = 0;
+
+Manager::Manager ()
+
+{
+ inputPort = 0;
+ outputPort = 0;
+ inputChannelNumber = 0;
+ outputChannelNumber = 0;
+}
+
+Manager::~Manager ()
+
+{
+ PortMap::iterator i;
+
+ for (i = ports_by_device.begin(); i != ports_by_device.end(); i++) {
+ delete (*i).second;
+ }
+
+ ports_by_device.erase (ports_by_device.begin(), ports_by_device.end());
+ ports_by_tag.erase (ports_by_tag.begin(), ports_by_tag.end());
+
+ if (theManager == this) {
+ theManager = 0;
+ }
+}
+
+Port *
+Manager::add_port (PortRequest &req)
+
+{
+ PortFactory factory;
+ Port *port;
+ PortMap::iterator existing;
+ pair<string, Port *> newpair;
+
+ if ((existing = ports_by_device.find (req.devname)) !=
+ ports_by_device.end()) {
+
+ port = (*existing).second;
+ if (port->mode() == req.mode) {
+
+ /* Same mode - reuse the port, and just
+ create a new tag entry.
+ */
+
+ newpair.first = req.tagname;
+ newpair.second = port;
+
+ ports_by_tag.insert (newpair);
+ return port;
+ }
+
+ /* If the existing is duplex, and this request
+ is not, then fail, because most drivers won't
+ allow opening twice with duplex and non-duplex
+ operation.
+ */
+
+ if ((req.mode == O_RDWR && port->mode() != O_RDWR) ||
+ (req.mode != O_RDWR && port->mode() == O_RDWR)) {
+ error << "MIDIManager: port tagged \""
+ << req.tagname
+ << "\" cannot be opened duplex and non-duplex"
+ << endmsg;
+ return 0;
+ }
+
+ /* modes must be different or complementary */
+ }
+
+
+ port = factory.create_port (req);
+
+ if (port == 0) {
+ return 0;
+ }
+
+ if (!port->ok()) {
+ delete port;
+ return 0;
+ }
+
+ newpair.first = port->name();
+ newpair.second = port;
+ ports_by_tag.insert (newpair);
+
+ newpair.first = port->device();
+ newpair.second = port;
+ ports_by_device.insert (newpair);
+
+ /* first port added becomes the default input
+ port.
+ */
+
+ if (inputPort == 0) {
+ inputPort = port;
+ }
+
+ if (outputPort == 0) {
+ outputPort = port;
+ }
+
+ return port;
+}
+
+int
+Manager::remove_port (string name)
+{
+ PortMap::iterator res;
+
+ if ((res = ports_by_device.find (name)) == ports_by_device.end()) {
+ return -1;
+ }
+
+ ports_by_device.erase (res);
+ ports_by_device.erase ((*res).second->name());
+
+ delete (*res).second;
+
+ return 0;
+}
+
+int
+Manager::set_input_port (string tag)
+{
+ PortMap::iterator res;
+ bool found = false;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if (tag == (*res).first) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return -1;
+ }
+
+ inputPort = (*res).second;
+
+ return 0;
+}
+
+int
+Manager::set_input_port (size_t portnum)
+
+{
+ PortMap::iterator res;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if ((*res).second->number() == portnum) {
+ inputPort = (*res).second;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+int
+Manager::set_output_port (string tag)
+
+{
+ PortMap::iterator res;
+ bool found = false;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if (tag == (*res).first) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return -1;
+ }
+
+ // XXX send a signal to say we're about to change output ports
+
+ if (outputPort) {
+ for (channel_t chan = 0; chan < 16; chan++) {
+ outputPort->channel (chan)->all_notes_off ();
+ }
+ }
+ outputPort = (*res).second;
+
+ // XXX send a signal to say we've changed output ports
+
+ return 0;
+}
+
+int
+Manager::set_output_port (size_t portnum)
+
+{
+ PortMap::iterator res;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if ((*res).second->number() == portnum) {
+ outputPort = (*res).second;
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+Port *
+Manager::port (string name)
+{
+ PortMap::iterator res;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if (name == (*res).first) {
+ return (*res).second;
+ }
+ }
+
+ return 0;
+}
+
+Port *
+Manager::port (size_t portnum)
+
+{
+ PortMap::iterator res;
+
+ for (res = ports_by_tag.begin(); res != ports_by_tag.end(); res++) {
+ if ((*res).second->number() == portnum) {
+ return (*res).second;
+ }
+ }
+
+ return 0;
+}
+
+int
+Manager::foreach_port (int (*func)(const Port &, size_t, void *),
+ void *arg)
+
+{
+ PortMap::const_iterator i;
+ int retval;
+ int n;
+
+ for (n = 0, i = ports_by_device.begin();
+ i != ports_by_device.end(); i++, n++) {
+
+ if ((retval = func (*((*i).second), n, arg)) != 0) {
+ return retval;
+ }
+ }
+
+ return 0;
+}
+
+int
+Manager::parse_port_request (string str, Port::Type type)
+{
+ PortRequest *req;
+ string::size_type colon;
+ string tag;
+
+ if (str.length() == 0) {
+ error << "MIDI: missing port specification" << endmsg;
+ return -1;
+ }
+
+ /* Port specifications look like:
+
+ devicename
+ devicename:tagname
+ devicename:tagname:mode
+
+ where
+
+ "devicename" is the full path to the requested file
+
+ "tagname" (optional) is the name used to refer to the
+ port. If not given, PBD::basename (devicename)
+ will be used.
+
+ "mode" (optional) is either "r" or "w" or something else.
+ if it is "r", the port will be opened
+ read-only, if "w", the port will be opened
+ write-only. Any other value, or no mode
+ specification at all, will cause the port to
+ be opened for reading and writing.
+ */
+
+ req = new PortRequest;
+ colon = str.find_first_of (':');
+
+ if (colon != string::npos) {
+ req->devname = strdup (str.substr (0, colon).c_str());
+ } else {
+ req->devname = strdup (str.c_str());
+ }
+
+ if (colon < str.length()) {
+
+ tag = str.substr (colon+1);
+
+ /* see if there is a mode specification in the tag part */
+
+ colon = tag.find_first_of (':');
+
+ if (colon != string::npos) {
+ string modestr;
+
+ req->tagname = strdup (tag.substr (0, colon).c_str());
+
+ modestr = tag.substr (colon+1);
+ if (modestr == "r") {
+ req->mode = O_RDONLY;
+ } else if (modestr == "w") {
+ req->mode = O_WRONLY;
+ } else {
+ req->mode = O_RDWR;
+ }
+
+ } else {
+ req->tagname = strdup (tag.c_str());
+ req->mode = O_RDWR;
+ }
+
+ } else {
+ req->tagname = strdup (PBD::basename (req->devname));
+ req->mode = O_RDWR;
+ }
+
+ req->type = type;
+
+ if (MIDI::Manager::instance()->add_port (*req) == 0) {
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/libs/midi++2/midiparser.cc b/libs/midi++2/midiparser.cc
new file mode 100644
index 0000000000..04ac2728f1
--- /dev/null
+++ b/libs/midi++2/midiparser.cc
@@ -0,0 +1,800 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This file was inspired by the MIDI parser for KeyKit by
+ Tim Thompson.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <cstdlib>
+#include <unistd.h>
+#include <string>
+#include <iostream>
+
+#include <midi++/types.h>
+#include <midi++/parser.h>
+#include <midi++/port.h>
+#include <midi++/mmc.h>
+#include <pbd/transmitter.h>
+
+using namespace std;
+using namespace sigc;
+using namespace MIDI;
+
+const char *
+Parser::midi_event_type_name (eventType t)
+
+{
+ switch (t) {
+ case none:
+ return "no midi messages";
+
+ case raw:
+ return "raw midi data";
+
+ case MIDI::any:
+ return "any midi message";
+
+ case off:
+ return "note off";
+
+ case on:
+ return "note on";
+
+ case polypress:
+ return "aftertouch";
+
+ case MIDI::controller:
+ return "controller";
+
+ case program:
+ return "program change";
+
+ case chanpress:
+ return "channel pressure";
+
+ case MIDI::pitchbend:
+ return "pitch bend";
+
+ case MIDI::sysex:
+ return "system exclusive";
+
+ case MIDI::song:
+ return "song position";
+
+ case MIDI::tune:
+ return "tune";
+
+ case MIDI::eox:
+ return "end of sysex";
+
+ case MIDI::timing:
+ return "timing";
+
+ case MIDI::start:
+ return "start";
+
+ case MIDI::stop:
+ return "continue";
+
+ case MIDI::contineu:
+ return "stop";
+
+ case active:
+ return "active sense";
+
+ default:
+ return "unknow MIDI event type";
+ }
+};
+
+Parser::Parser (Port &p)
+ : _port (p)
+
+{
+ trace_stream = 0;
+ trace_prefix = "";
+ memset (message_counter, 0, sizeof (message_counter[0]) * 256);
+ msgindex = 0;
+ msgtype = none;
+ msglen = 256;
+ msgbuf = (unsigned char *) malloc (msglen);
+ msgbuf[msgindex++] = 0x90;
+ _mmc_forward = false;
+ reset_mtc_state ();
+ _offline = false;
+
+ /* this hack deals with the possibility of our first MIDI
+ bytes being running status messages.
+ */
+
+ channel_msg (0x90);
+ state = NEEDSTATUS;
+
+ pre_variable_state = NEEDSTATUS;
+ pre_variable_msgtype = none;
+}
+
+Parser::~Parser ()
+
+{
+ delete msgbuf;
+}
+
+void
+Parser::trace_event (Parser &p, byte *msg, size_t len)
+
+{
+ eventType type;
+ ostream *o;
+
+ if ((o = trace_stream) == NULL) { /* can be asynchronously removed */
+ return;
+ }
+
+ type = (eventType) (msg[0]&0xF0);
+
+ switch (type) {
+ case off:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " NoteOff NoteNum "
+ << (int) msg[1]
+ << " Vel "
+ << (int) msg[2]
+ << endmsg;
+ break;
+
+ case on:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " NoteOn NoteNum "
+ << (int) msg[1]
+ << " Vel "
+ << (int) msg[2]
+ << endmsg;
+ break;
+
+ case polypress:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " PolyPressure"
+ << (int) msg[1]
+ << endmsg;
+ break;
+
+ case MIDI::controller:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " Controller "
+ << (int) msg[1]
+ << " Value "
+ << (int) msg[2]
+ << endmsg;
+ break;
+
+ case program:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " Program Change ProgNum "
+ << (int) msg[1]
+ << endmsg;
+ break;
+
+ case chanpress:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " Channel Pressure "
+ << (int) msg[1]
+ << endmsg;
+ break;
+
+ case MIDI::pitchbend:
+ *o << trace_prefix
+ << "Channel "
+ << (msg[0]&0xF)+1
+ << " Pitch Bend "
+ << ((msg[1]<<7)|msg[2])
+ << endmsg;
+ break;
+
+ case MIDI::sysex:
+ if (len == 1) {
+ switch (msg[0]) {
+ case 0xf8:
+ *o << trace_prefix
+ << "Clock"
+ << endmsg;
+ break;
+ case 0xfa:
+ *o << trace_prefix
+ << "Start"
+ << endmsg;
+ break;
+ case 0xfb:
+ *o << trace_prefix
+ << "Continue"
+ << endmsg;
+ break;
+ case 0xfc:
+ *o << trace_prefix
+ << "Stop"
+ << endmsg;
+ break;
+ case 0xfe:
+ *o << trace_prefix
+ << "Active Sense"
+ << endmsg;
+ break;
+ case 0xff:
+ *o << trace_prefix
+ << "System Reset"
+ << endmsg;
+ break;
+ default:
+ *o << trace_prefix
+ << "System Exclusive (1 byte : " << hex << (int) *msg << dec << ')'
+ << endmsg;
+ break;
+ }
+ } else {
+ *o << trace_prefix
+ << "System Exclusive (" << len << ") = [ " << hex;
+ for (unsigned int i = 0; i < len; ++i) {
+ *o << (int) msgbuf[i] << ' ';
+ }
+ *o << dec << ']' << endmsg;
+
+ }
+ break;
+
+ case MIDI::song:
+ *o << trace_prefix << "Song" << endmsg;
+ break;
+
+ case MIDI::tune:
+ *o << trace_prefix << "Tune" << endmsg;
+ break;
+
+ case MIDI::eox:
+ *o << trace_prefix << "End-of-System Exclusive" << endmsg;
+ break;
+
+ case MIDI::timing:
+ *o << trace_prefix << "Timing" << endmsg;
+ break;
+
+ case MIDI::start:
+ *o << trace_prefix << "Start" << endmsg;
+ break;
+
+ case MIDI::stop:
+ *o << trace_prefix << "Stop" << endmsg;
+ break;
+
+ case MIDI::contineu:
+ *o << trace_prefix << "Continue" << endmsg;
+ break;
+
+ case active:
+ *o << trace_prefix << "Active Sense" << endmsg;
+ break;
+
+ default:
+ *o << trace_prefix << "Unrecognized MIDI message" << endmsg;
+ break;
+ }
+}
+
+void
+Parser::trace (bool onoff, ostream *o, const string &prefix)
+
+{
+ trace_connection.disconnect ();
+
+ if (onoff) {
+ trace_stream = o;
+ trace_prefix = prefix;
+ trace_connection = any.connect
+ (mem_fun (*this, &Parser::trace_event));
+ } else {
+ trace_prefix = "";
+ trace_stream = 0;
+ }
+}
+
+void
+Parser::scanner (unsigned char inbyte)
+{
+ bool statusbit;
+
+ // cerr << "parse: " << hex << (int) inbyte << dec << " state = " << state << " msgindex = " << msgindex << " runnable = " << runnable << endl;
+
+ /* Check active sensing early, so it doesn't interrupt sysex.
+
+ NOTE: active sense messages are not considered to fit under
+ "any" for the purposes of callbacks. If a caller wants
+ active sense messages handled, which is unlikely, then
+ they can just ask for it specifically. They are so unlike
+ every other MIDI message in terms of semantics that its
+ counter-productive to treat them similarly.
+ */
+
+ if (inbyte == 0xfe) {
+ message_counter[inbyte]++;
+ if (!_offline) {
+ active_sense (*this);
+ }
+ return;
+ }
+
+ /* If necessary, allocate larger message buffer. */
+
+ if (msgindex >= msglen) {
+ msglen *= 2;
+ msgbuf = (unsigned char *) realloc (msgbuf, msglen);
+ }
+
+ /*
+ Real time messages can occur ANYPLACE,
+ but do not interrupt running status.
+ */
+
+ bool rtmsg = false;
+
+ switch (inbyte) {
+ case 0xf8:
+ rtmsg = true;
+ break;
+ case 0xfa:
+ rtmsg = true;
+ break;
+ case 0xfb:
+ rtmsg = true;
+ break;
+ case 0xfc:
+ rtmsg = true;
+ break;
+ case 0xfd:
+ rtmsg = true;
+ break;
+ case 0xfe:
+ rtmsg = true;
+ break;
+ case 0xff:
+ rtmsg = true;
+ break;
+ }
+
+ if (rtmsg) {
+ if (edit (&inbyte, 1) >= 0 && !_offline) {
+ realtime_msg (inbyte);
+ }
+
+ return;
+ }
+
+ statusbit = (inbyte & 0x80);
+
+ /*
+ * Variable length messages (ie. the 'system exclusive')
+ * can be terminated by the next status byte, not necessarily
+ * an EOX. Actually, since EOX is a status byte, this
+ * code ALWAYS handles the end of a VARIABLELENGTH message.
+ */
+
+ if (state == VARIABLELENGTH && statusbit) {
+ /* The message has ended, so process it */
+
+ /* add EOX to any sysex message */
+
+ if (inbyte == MIDI::eox) {
+ msgbuf[msgindex++] = inbyte;
+ }
+
+#if 0
+ cerr << "SYSEX: " << hex;
+ for (unsigned int i = 0; i < msgindex; ++i) {
+ cerr << (int) msgbuf[i] << ' ';
+ }
+ cerr << dec << endl;
+#endif
+ if (msgindex > 0 && edit (msgbuf, msgindex) >= 0) {
+ if (!possible_mmc (msgbuf, msgindex) || _mmc_forward) {
+ if (!possible_mtc (msgbuf, msgindex) || _mtc_forward) {
+ if (!_offline) {
+ sysex (*this, msgbuf, msgindex);
+ }
+ }
+ }
+ if (!_offline) {
+ any (*this, msgbuf, msgindex);
+ }
+ }
+ }
+
+ /*
+ * Status bytes always start a new message, except EOX
+ */
+
+ if (statusbit) {
+
+ msgindex = 0;
+
+ if (inbyte == MIDI::eox) {
+ /* return to the state we had pre-sysex */
+
+ state = pre_variable_state;
+ runnable = was_runnable;
+ msgtype = pre_variable_msgtype;
+
+ if (state != NEEDSTATUS && runnable) {
+ msgbuf[msgindex++] = last_status_byte;
+ }
+ } else {
+ msgbuf[msgindex++] = inbyte;
+ if ((inbyte & 0xf0) == 0xf0) {
+ system_msg (inbyte);
+ runnable = false;
+ } else {
+ channel_msg (inbyte);
+ }
+ }
+
+ return;
+ }
+
+ /*
+ * We've got a Data byte.
+ */
+
+ msgbuf[msgindex++] = inbyte;
+
+ switch (state) {
+ case NEEDSTATUS:
+ /*
+ * We shouldn't get here, since in NEEDSTATUS mode
+ * we're expecting a new status byte, NOT any
+ * data bytes. On the other hand, some equipment
+ * with leaky modwheels and the like might be
+ * sending data bytes as part of running controller
+ * messages, so just handle it silently.
+ */
+ break;
+
+ case NEEDTWOBYTES:
+ /* wait for the second byte */
+ if (msgindex < 3)
+ return;
+ /*FALLTHRU*/
+
+ case NEEDONEBYTE:
+ /* We've completed a 1 or 2 byte message. */
+
+ if (edit (msgbuf, msgindex) == 0) {
+
+ /* message not cancelled by an editor */
+
+ message_counter[msgbuf[0] & 0xF0]++;
+
+ if (!_offline) {
+ signal (msgbuf, msgindex);
+ }
+ }
+
+ if (runnable) {
+ /* In Runnable mode, we reset the message
+ index, but keep the callbacks_pending and state the
+ same. This provides the "running status
+ byte" feature.
+ */
+ msgindex = 1;
+ } else {
+ /* If not Runnable, reset to NEEDSTATUS mode */
+ state = NEEDSTATUS;
+ }
+ break;
+
+ case VARIABLELENGTH:
+ /* nothing to do */
+ break;
+ }
+ return;
+}
+
+/*
+ * realtime_msg(inbyte)
+ *
+ * Call the real-time function for the specified byte, immediately.
+ * These can occur anywhere, so they don't change the state.
+ */
+
+void
+Parser::realtime_msg(unsigned char inbyte)
+
+{
+ message_counter[inbyte]++;
+
+ if (_offline) {
+ return;
+ }
+
+ switch (inbyte) {
+ case 0xf8:
+ timing (*this);
+ break;
+ case 0xfa:
+ start (*this);
+ break;
+ case 0xfb:
+ contineu (*this);
+ break;
+ case 0xfc:
+ stop (*this);
+ break;
+ case 0xfe:
+ /* !!! active sense message in realtime_msg: should not reach here
+ */
+ break;
+ case 0xff:
+ reset (*this);
+ break;
+ }
+
+ any (*this, &inbyte, 1);
+}
+
+/*
+ * channel_msg(inbyte)
+ *
+ * Interpret a Channel (voice or mode) Message status byte.
+ */
+
+void
+Parser::channel_msg(unsigned char inbyte)
+{
+ last_status_byte = inbyte;
+ runnable = true; /* Channel messages can use running status */
+
+ /* The high 4 bits, which determine the type of channel message. */
+
+ switch (inbyte&0xF0) {
+ case 0x80:
+ msgtype = off;
+ state = NEEDTWOBYTES;
+ break;
+ case 0x90:
+ msgtype = on;
+ state = NEEDTWOBYTES;
+ break;
+ case 0xa0:
+ msgtype = polypress;
+ state = NEEDTWOBYTES;
+ break;
+ case 0xb0:
+ msgtype = MIDI::controller;
+ state = NEEDTWOBYTES;
+ break;
+ case 0xc0:
+ msgtype = program;
+ state = NEEDONEBYTE;
+ break;
+ case 0xd0:
+ msgtype = chanpress;
+ state = NEEDONEBYTE;
+ break;
+ case 0xe0:
+ msgtype = MIDI::pitchbend;
+ state = NEEDTWOBYTES;
+ break;
+ }
+}
+
+/*
+ * system_msg(inbyte)
+ *
+ * Initialize (and possibly emit) the signals for the
+ * specified byte. Set the state that the state-machine
+ * should go into. If the signal is not emitted
+ * immediately, it will be when the state machine gets to
+ * the end of the MIDI message.
+ */
+
+void
+Parser::system_msg (unsigned char inbyte)
+{
+ message_counter[inbyte]++;
+
+ switch (inbyte) {
+ case 0xf0:
+ pre_variable_msgtype = msgtype;
+ pre_variable_state = state;
+ was_runnable = runnable;
+ msgtype = MIDI::sysex;
+ state = VARIABLELENGTH;
+ break;
+ case 0xf1:
+ msgtype = MIDI::mtc_quarter;
+ state = NEEDONEBYTE;
+ break;
+ case 0xf2:
+ msgtype = MIDI::position;
+ state = NEEDTWOBYTES;
+ break;
+ case 0xf3:
+ msgtype = MIDI::song;
+ state = NEEDONEBYTE;
+ break;
+ case 0xf6:
+ if (!_offline) {
+ tune (*this);
+ }
+ state = NEEDSTATUS;
+ break;
+ case 0xf7:
+ break;
+ }
+
+ // all these messages will be sent via any()
+ // when they are complete.
+ // any (*this, &inbyte, 1);
+}
+
+void
+Parser::signal (byte *msg, size_t len)
+{
+ channel_t chan = msg[0]&0xF;
+ int chan_i = chan;
+
+ switch (msgtype) {
+ case none:
+ break;
+
+ case off:
+ channel_active_preparse[chan_i] (*this);
+ note_off (*this, (EventTwoBytes *) &msg[1]);
+ channel_note_off[chan_i]
+ (*this, (EventTwoBytes *) &msg[1]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case on:
+ channel_active_preparse[chan_i] (*this);
+
+ /* Hack to deal with MIDI sources that use velocity=0
+ instead of noteOff.
+ */
+
+ if (msg[2] == 0) {
+ note_off (*this, (EventTwoBytes *) &msg[1]);
+ channel_note_off[chan_i]
+ (*this, (EventTwoBytes *) &msg[1]);
+ } else {
+ note_on (*this, (EventTwoBytes *) &msg[1]);
+ channel_note_on[chan_i]
+ (*this, (EventTwoBytes *) &msg[1]);
+ }
+
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case MIDI::controller:
+ channel_active_preparse[chan_i] (*this);
+ controller (*this, (EventTwoBytes *) &msg[1]);
+ channel_controller[chan_i]
+ (*this, (EventTwoBytes *) &msg[1]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case program:
+ channel_active_preparse[chan_i] (*this);
+ program_change (*this, msg[1]);
+ channel_program_change[chan_i] (*this, msg[1]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case chanpress:
+ channel_active_preparse[chan_i] (*this);
+ pressure (*this, msg[1]);
+ channel_pressure[chan_i] (*this, msg[1]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case polypress:
+ channel_active_preparse[chan_i] (*this);
+ poly_pressure (*this, (EventTwoBytes *) &msg[1]);
+ channel_poly_pressure[chan_i]
+ (*this, (EventTwoBytes *) &msg[1]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case MIDI::pitchbend:
+ channel_active_preparse[chan_i] (*this);
+ pitchbend (*this, (msg[1]<<7)|msg[2]);
+ channel_pitchbend[chan_i] (*this, (msg[1]<<7)|msg[2]);
+ channel_active_postparse[chan_i] (*this);
+ break;
+
+ case MIDI::sysex:
+ sysex (*this, msg, len);
+ break;
+
+ case MIDI::mtc_quarter:
+ process_mtc_quarter_frame (msg);
+ mtc_quarter_frame (*this, *msg);
+ break;
+
+ case MIDI::position:
+ position (*this, msg, len);
+ break;
+
+ case MIDI::song:
+ song (*this, msg, len);
+ break;
+
+ case MIDI::tune:
+ tune (*this);
+
+ default:
+ /* XXX some kind of warning ? */
+ break;
+ }
+
+ any (*this, msg, len);
+}
+
+bool
+Parser::possible_mmc (byte *msg, size_t msglen)
+{
+ if (!MachineControl::is_mmc (msg, msglen)) {
+ return false;
+ }
+
+ /* hand over the just the interior MMC part of
+ the sysex msg without the leading 0xF0
+ */
+
+ if (!_offline) {
+ mmc (*this, &msg[1], msglen - 1);
+ }
+
+ return true;
+}
+
+void
+Parser::set_offline (bool yn)
+{
+ if (_offline != yn) {
+ _offline = yn;
+ OfflineStatusChanged ();
+
+ /* this hack deals with the possibility of our first MIDI
+ bytes being running status messages.
+ */
+
+ channel_msg (0x90);
+ state = NEEDSTATUS;
+ }
+}
+
diff --git a/libs/midi++2/midiport.cc b/libs/midi++2/midiport.cc
new file mode 100644
index 0000000000..6d374ed8c0
--- /dev/null
+++ b/libs/midi++2/midiport.cc
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 1998 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <cstdio>
+#include <fcntl.h>
+
+#include <midi++/types.h>
+#include <midi++/port.h>
+#include <midi++/channel.h>
+#include <midi++/port_request.h>
+
+using namespace Select;
+using namespace MIDI;
+
+size_t Port::nports = 0;
+
+Port::Port (PortRequest &req)
+
+{
+ _ok = false; /* derived class must set to true if constructor
+ succeeds.
+ */
+
+ bytes_written = 0;
+ bytes_read = 0;
+ input_parser = 0;
+ output_parser = 0;
+ slowdown = 0;
+
+ _devname = req.devname;
+ _tagname = req.tagname;
+ _mode = req.mode;
+ _number = nports++;
+
+ if (_mode == O_RDONLY || _mode == O_RDWR) {
+ input_parser = new Parser (*this);
+ } else {
+ input_parser = 0;
+ }
+
+ if (_mode == O_WRONLY || _mode == O_RDWR) {
+ output_parser = new Parser (*this);
+ } else {
+ output_parser = 0;
+ }
+
+ for (int i = 0; i < 16; i++) {
+ _channel[i] = new Channel (i, *this);
+
+ if (input_parser) {
+ _channel[i]->connect_input_signals ();
+ }
+
+ if (output_parser) {
+ _channel[i]->connect_output_signals ();
+ }
+ }
+}
+
+
+Port::~Port ()
+
+{
+ for (int i = 0; i < 16; i++) {
+ delete _channel[i];
+ }
+}
+
+int
+Port::clock ()
+
+{
+ static byte clockmsg = 0xf8;
+
+ if (_mode != O_RDONLY) {
+ return midimsg (&clockmsg, 1);
+ }
+
+ return 0;
+}
+
+void
+Port::selector_read_callback (Selectable *s, Select::Condition cond)
+
+{
+ byte buf[64];
+ read (buf, sizeof (buf));
+}
+
+void
+Port::xforms_read_callback (int cond, int fd, void *ptr)
+
+{
+ byte buf[64];
+
+ ((Port *)ptr)->read (buf, sizeof (buf));
+}
+
+void
+Port::gtk_read_callback (void *ptr, int fd, int cond)
+
+{
+ byte buf[64];
+
+ ((Port *)ptr)->read (buf, sizeof (buf));
+}
+
+void
+Port::write_callback (byte *msg, unsigned int len, void *ptr)
+
+{
+ ((Port *)ptr)->write (msg, len);
+}
+
diff --git a/libs/midi++2/miditrace.cc b/libs/midi++2/miditrace.cc
new file mode 100644
index 0000000000..d7c65d9f29
--- /dev/null
+++ b/libs/midi++2/miditrace.cc
@@ -0,0 +1,64 @@
+#include <cstdio>
+#include <fcntl.h>
+
+#include <pbd/error.h>
+#include <pbd/textreceiver.h>
+
+Transmitter error (Transmitter::Error);
+Transmitter info (Transmitter::Info);
+Transmitter warning (Transmitter::Warning);
+Transmitter fatal (Transmitter::Fatal);
+TextReceiver text_receiver ("mmctest");
+
+#include "midi++/port.h"
+#include "midi++/port_request.h"
+#include "midi++/manager.h"
+
+using namespace MIDI;
+
+Port *port;
+PortRequest midi_device;
+
+int
+setup_midi ()
+
+{
+ midi_device.devname = "/dev/snd/midiC0D0";
+ midi_device.tagname = "trident";
+ midi_device.mode = O_RDWR;
+ midi_device.type = Port::ALSA_RawMidi;
+
+ if ((port = MIDI::Manager::instance()->add_port (midi_device)) == 0) {
+ info << "MIDI port is not valid" << endmsg;
+ return -1;
+ }
+
+ return 0;
+}
+
+main (int argc, char *argv[])
+
+{
+ byte buf[1];
+
+ text_receiver.listen_to (error);
+ text_receiver.listen_to (info);
+ text_receiver.listen_to (fatal);
+ text_receiver.listen_to (warning);
+
+ if (setup_midi ()) {
+ exit (1);
+ }
+
+ port->input()->trace (true, &cout);
+
+ while (1) {
+ if (port->read (buf, 1) < 0) {
+ error << "cannot read byte"
+ << endmsg;
+ break;
+ }
+ }
+}
+
+
diff --git a/libs/midi++2/mmc.cc b/libs/midi++2/mmc.cc
new file mode 100644
index 0000000000..a0de774329
--- /dev/null
+++ b/libs/midi++2/mmc.cc
@@ -0,0 +1,577 @@
+/*
+ Copyright (C) 2000 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <map>
+
+#include <pbd/error.h>
+#include <midi++/mmc.h>
+#include <midi++/port.h>
+#include <midi++/parser.h>
+
+using namespace std;
+using namespace MIDI;
+
+static std::map<int,string> mmc_cmd_map;
+static void build_mmc_cmd_map ()
+{
+ pair<int,string> newpair;
+
+ newpair.first = 0x1;
+ newpair.second = "Stop";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x2;
+ newpair.second = "Play";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x3;
+ newpair.second = "DeferredPlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4;
+ newpair.second = "FastForward";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x5;
+ newpair.second = "Rewind";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x6;
+ newpair.second = "RecordStrobe";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7;
+ newpair.second = "RecordExit";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x8;
+ newpair.second = "RecordPause";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x9;
+ newpair.second = "Pause";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xA;
+ newpair.second = "Eject";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xB;
+ newpair.second = "Chase";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xC;
+ newpair.second = "CommandErrorReset";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0xD;
+ newpair.second = "MmcReset";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x20;
+ newpair.second = "Illegal Mackie Jog Start";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x21;
+ newpair.second = "Illegal Mackie Jog Stop";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x40;
+ newpair.second = "Write";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x41;
+ newpair.second = "MaskedWrite";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x42;
+ newpair.second = "Read";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x43;
+ newpair.second = "Update";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x44;
+ newpair.second = "Locate";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x45;
+ newpair.second = "VariablePlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x46;
+ newpair.second = "Search";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x47;
+ newpair.second = "Shuttle";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x48;
+ newpair.second = "Step";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x49;
+ newpair.second = "AssignSystemMaster";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4A;
+ newpair.second = "GeneratorCommand";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4B;
+ newpair.second = "MtcCommand";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4C;
+ newpair.second = "Move";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4D;
+ newpair.second = "Add";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4E;
+ newpair.second = "Subtract";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x4F;
+ newpair.second = "DropFrameAdjust";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x50;
+ newpair.second = "Procedure";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x51;
+ newpair.second = "Event";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x52;
+ newpair.second = "Group";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x53;
+ newpair.second = "CommandSegment";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x54;
+ newpair.second = "DeferredVariablePlay";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x55;
+ newpair.second = "RecordStrobeVariable";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7C;
+ newpair.second = "Wait";
+ mmc_cmd_map.insert (newpair);
+
+ newpair.first = 0x7F;
+ newpair.second = "Resume";
+ mmc_cmd_map.insert (newpair);
+}
+
+
+MachineControl::MachineControl (Port &p, float version,
+ CommandSignature &csig,
+ ResponseSignature &rsig)
+
+ : _port (p)
+{
+ Parser *parser;
+
+ build_mmc_cmd_map ();
+
+ _device_id = 1;
+
+ if ((parser = _port.input()) != 0) {
+ parser->mmc.connect
+ (mem_fun (*this, &MachineControl::process_mmc_message));
+ } else {
+ warning << "MMC connected to a non-input port: useless!"
+ << endmsg;
+ }
+}
+
+void
+MachineControl::set_device_id (byte id)
+
+{
+ _device_id = id & 0x7f;
+}
+
+bool
+MachineControl::is_mmc (byte *sysex_buf, size_t len)
+
+{
+ if (len < 4 || len > 48) {
+ return false;
+ }
+
+ if (sysex_buf[1] != 0x7f) {
+ return false;
+ }
+
+ if (sysex_buf[3] != 0x6 && /* MMC Command */
+ sysex_buf[3] != 0x7) { /* MMC Response */
+ return false;
+ }
+
+ return true;
+}
+
+void
+MachineControl::process_mmc_message (Parser &p, byte *msg, size_t len)
+
+{
+ size_t skiplen;
+ byte *mmc_msg;
+ bool single_byte;
+
+ /* Reject if its not for us. 0x7f is the "all-call" device ID */
+
+ /* msg[0] = 0x7f (MMC sysex ID(
+ msg[1] = device ID
+ msg[2] = 0x6 (MMC command) or 0x7 (MMC response)
+ msg[3] = MMC command code
+ msg[4] = (typically) byte count for following part of command
+ */
+
+#if 0
+ cerr << "*** MMC message: len = " << len << "\n\t";
+ for (size_t i = 0; i < len; i++) {
+ cerr << hex << (int) msg[i] << dec << ' ';
+ }
+ cerr << endl;
+#endif
+
+ if (msg[1] != 0x7f && msg[1] != _device_id) {
+ return;
+ }
+
+ mmc_msg = &msg[3];
+ len -= 3;
+
+ do {
+
+ single_byte = false;
+
+ /* this works for all non-single-byte "counted"
+ commands. we set it to 1 for the exceptions.
+ */
+
+ std::map<int,string>::iterator x = mmc_cmd_map.find ((int)mmc_msg[0]);
+ string cmdname = "unknown";
+
+ if (x != mmc_cmd_map.end()) {
+ cmdname = (*x).second;
+ }
+
+#if 0
+ cerr << "+++ MMC type "
+ << hex
+ << ((int) *mmc_msg)
+ << dec
+ << " \"" << cmdname << "\" "
+ << " len = " << len
+ << endl;
+#endif
+
+ switch (*mmc_msg) {
+
+ /* SINGLE-BYTE, UNCOUNTED COMMANDS */
+
+ case cmdStop:
+ Stop (*this);
+ single_byte = true;
+ break;
+
+ case cmdPlay:
+ Play (*this);
+ single_byte = true;
+ break;
+
+ case cmdDeferredPlay:
+ DeferredPlay (*this);
+ single_byte = true;
+ break;
+
+ case cmdFastForward:
+ FastForward (*this);
+ single_byte = true;
+ break;
+
+ case cmdRewind:
+ Rewind (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordStrobe:
+ RecordStrobe (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordExit:
+ RecordExit (*this);
+ single_byte = true;
+ break;
+
+ case cmdRecordPause:
+ RecordPause (*this);
+ single_byte = true;
+ break;
+
+ case cmdPause:
+ Pause (*this);
+ single_byte = true;
+ break;
+
+ case cmdEject:
+ Eject (*this);
+ single_byte = true;
+ break;
+
+ case cmdChase:
+ Chase (*this);
+ single_byte = true;
+ break;
+
+ case cmdCommandErrorReset:
+ CommandErrorReset (*this);
+ single_byte = true;
+ break;
+
+ case cmdMmcReset:
+ MmcReset (*this);
+ single_byte = true;
+ break;
+
+ case cmdIllegalMackieJogStart:
+ JogStart (*this);
+ single_byte = true;
+ break;
+
+ case cmdIllegalMackieJogStop:
+ JogStop (*this);
+ single_byte = true;
+ break;
+
+ /* END OF SINGLE-BYTE, UNCOUNTED COMMANDS */
+
+ case cmdMaskedWrite:
+ do_masked_write (mmc_msg, len);
+ break;
+
+ case cmdLocate:
+ do_locate (mmc_msg, len);
+ break;
+
+ case cmdShuttle:
+ do_shuttle (mmc_msg, len);
+ break;
+
+ case cmdStep:
+ do_step (mmc_msg, len);
+ break;
+
+ case cmdWrite:
+ case cmdRead:
+ case cmdUpdate:
+ case cmdVariablePlay:
+ case cmdSearch:
+ case cmdAssignSystemMaster:
+ case cmdGeneratorCommand:
+ case cmdMtcCommand:
+ case cmdMove:
+ case cmdAdd:
+ case cmdSubtract:
+ case cmdDropFrameAdjust:
+ case cmdProcedure:
+ case cmdEvent:
+ case cmdGroup:
+ case cmdCommandSegment:
+ case cmdDeferredVariablePlay:
+ case cmdRecordStrobeVariable:
+ case cmdWait:
+ case cmdResume:
+ error << "MIDI::MachineControl: unimplemented MMC command "
+ << hex << (int) *mmc_msg << dec
+ << endmsg;
+
+ break;
+
+ default:
+ error << "MIDI::MachineControl: unknown MMC command "
+ << hex << (int) *mmc_msg << dec
+ << endmsg;
+
+ break;
+ }
+
+ /* increase skiplen to cover the command byte and
+ count byte (if it existed).
+ */
+
+ if (!single_byte) {
+ skiplen = mmc_msg[1] + 2;
+ } else {
+ skiplen = 1;
+ }
+
+ if (len <= skiplen) {
+ break;
+ }
+
+ mmc_msg += skiplen;
+ len -= skiplen;
+
+ } while (len > 1); /* skip terminating EOX byte */
+}
+
+int
+MachineControl::do_masked_write (byte *msg, size_t len)
+
+{
+ /* return the number of bytes "consumed" */
+
+ int retval = msg[1] + 2; /* bytes following + 2 */
+
+ switch (msg[2]) {
+ case 0x4f: /* Track Record Ready Status */
+ write_track_record_ready (&msg[3], len - 3);
+ break;
+
+ default:
+ warning << "MIDI::MachineControl: masked write to "
+ << hex << (int) msg[2] << dec
+ << " not implemented"
+ << endmsg;
+ }
+
+ return retval;
+}
+
+void
+MachineControl::write_track_record_ready (byte *msg, size_t len)
+
+{
+ size_t n;
+ size_t base_track;
+
+ /* Bits 0-4 of the first byte are for special tracks:
+
+ bit 0: video
+ bit 1: reserved
+ bit 2: time code
+ bit 3: aux track a
+ bit 4: aux track b
+
+ */
+
+ /* XXX check needed to make sure we don't go outside the
+ support number of tracks.
+ */
+
+ base_track = (msg[0] * 7) - 5;
+
+ for (n = 0; n < 7; n++) {
+ if (msg[1] & (1<<n)) {
+
+ /* Only touch tracks that have the "mask"
+ bit set.
+ */
+
+ if (msg[2] & (1<<n)) {
+ trackRecordStatus[base_track+n] = true;
+ TrackRecordStatusChange (*this, base_track+n,
+ true);
+ } else {
+ trackRecordStatus[base_track+n] = false;
+ TrackRecordStatusChange (*this, base_track+n,
+ false);
+ }
+ }
+
+ }
+}
+
+int
+MachineControl::do_locate (byte *msg, size_t msglen)
+
+{
+ if (msg[2] == 0) {
+ warning << "MIDI::MMC: locate [I/F] command not supported"
+ << endmsg;
+ return 0;
+ }
+
+ /* regular "target" locate command */
+
+ Locate (*this, &msg[3]);
+ return 0;
+}
+
+int
+MachineControl::do_step (byte *msg, size_t msglen)
+{
+ int steps = msg[2] & 0x3f;
+
+ if (msg[2] & 0x40) {
+ steps = -steps;
+ }
+
+ Step (*this, steps);
+ return 0;
+}
+
+int
+MachineControl::do_shuttle (byte *msg, size_t msglen)
+
+{
+ size_t forward;
+ byte sh = msg[2];
+ byte sm = msg[3];
+ byte sl = msg[4];
+ size_t left_shift;
+ size_t integral;
+ size_t fractional;
+ float shuttle_speed;
+
+ if (sh & (1<<6)) {
+ forward = false;
+ } else {
+ forward = true;
+ }
+
+ left_shift = (sh & 0x38);
+
+ integral = ((sh & 0x7) << left_shift) | (sm >> (7 - left_shift));
+ fractional = ((sm << left_shift) << 7) | sl;
+
+ shuttle_speed = integral +
+ ((float)fractional / (1 << (14 - left_shift)));
+
+ Shuttle (*this, shuttle_speed, forward);
+
+ return 0;
+}
+
diff --git a/libs/midi++2/mmctest.cc b/libs/midi++2/mmctest.cc
new file mode 100644
index 0000000000..25ea964ded
--- /dev/null
+++ b/libs/midi++2/mmctest.cc
@@ -0,0 +1,112 @@
+#include <cstdio>
+#include <fcntl.h>
+
+#include <pbd/error.h>
+#include <pbd/textreceiver.h>
+
+Transmitter error (Transmitter::Error);
+Transmitter info (Transmitter::Info);
+Transmitter warning (Transmitter::Warning);
+Transmitter fatal (Transmitter::Fatal);
+TextReceiver text_receiver ("mmctest");
+
+#include "midi++/port.h"
+#include "midi++/port_request.h"
+#include "midi++/manager.h"
+#include "midi++/mmc.h"
+
+using namespace MIDI;
+
+Port *port;
+PortRequest midi_device;
+Parser *parser;
+MachineControl *mmc;
+MachineControl::CommandSignature cs;
+MachineControl::ResponseSignature rs;
+
+int
+setup_midi ()
+
+{
+ midi_device.devname = "/dev/snd/midiC0D0";
+ midi_device.tagname = "trident";
+ midi_device.mode = O_RDWR;
+ midi_device.type = Port::ALSA_RawMidi;
+
+ if ((port = MIDI::Manager::instance()->add_port (midi_device)) == 0) {
+ info << "MIDI port is not valid" << endmsg;
+ return -1;
+ }
+
+ mmc = new MachineControl (*port, 0.0, cs, rs);
+
+ return 0;
+}
+
+void
+do_deferred_play (MachineControl &mmc)
+
+{
+ cout << "Deferred Play" << endl;
+}
+
+void
+do_stop (MachineControl &mmc)
+
+{
+ cout << "Stop" << endl;
+}
+
+void
+do_ffwd (MachineControl &mmc)
+
+{
+ cout << "Fast Forward" << endl;
+}
+
+void
+do_rewind (MachineControl &mmc)
+
+{
+ cout << "Rewind" << endl;
+}
+
+void
+do_record_status (MachineControl &mmc, size_t track, bool enabled)
+
+{
+ cout << "Track " << track + 1 << (enabled ? " enabled" : " disabled")
+ << endl;
+}
+
+main (int argc, char *argv[])
+
+{
+ byte buf[1];
+
+ text_receiver.listen_to (error);
+ text_receiver.listen_to (info);
+ text_receiver.listen_to (fatal);
+ text_receiver.listen_to (warning);
+
+ if (setup_midi ()) {
+ exit (1);
+ }
+
+
+ mmc->DeferredPlay.connect (mem_fun (do_deferred_play));
+ mmc->FastForward.connect (mem_fun (do_ffwd));
+ mmc->Rewind.connect (mem_fun (do_rewind));
+ mmc->Stop.connect (mem_fun (do_stop));
+ mmc->TrackRecordStatusChange.connect (mem_fun (do_record_status));
+
+ while (1) {
+ if (port->read (buf, 1) < 0) {
+ error << "cannot read byte"
+ << endmsg;
+ break;
+ }
+ }
+}
+
+
diff --git a/libs/midi++2/mtc.cc b/libs/midi++2/mtc.cc
new file mode 100644
index 0000000000..19fdb1fabd
--- /dev/null
+++ b/libs/midi++2/mtc.cc
@@ -0,0 +1,329 @@
+/*
+ Copyright (C) 2004 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <cstdlib>
+#include <unistd.h>
+#include <string>
+#include <iostream>
+
+#include <midi++/types.h>
+#include <midi++/parser.h>
+#include <midi++/port.h>
+#include <midi++/mmc.h>
+#include <pbd/transmitter.h>
+
+using namespace std;
+using namespace sigc;
+using namespace MIDI;
+
+bool
+Parser::possible_mtc (byte *sysex_buf, size_t msglen)
+{
+ byte fake_mtc_time[4];
+
+ if (msglen != 10 || sysex_buf[0] != 0xf0 || sysex_buf[1] != 0x7f || sysex_buf[3] != 0x01 || sysex_buf[4] != 0x01) {
+ return false;
+ }
+
+ /* full MTC */
+
+ fake_mtc_time[0] = sysex_buf[8]; // frames
+ fake_mtc_time[1] = sysex_buf[7]; // minutes
+ fake_mtc_time[2] = sysex_buf[6]; // seconds
+ fake_mtc_time[3] = (sysex_buf[5] & 0x1f); // hours
+
+ _mtc_fps = MTC_FPS ((sysex_buf[5] & 0x60) >> 5); // fps
+
+ /* wait for first quarter frame, which could indicate forwards
+ or backwards ...
+ */
+
+ reset_mtc_state ();
+
+ /* emit signals */
+
+ mtc (*this, &sysex_buf[1], msglen - 1);
+ mtc_time (fake_mtc_time, true);
+ mtc_status (MTC_Stopped);
+
+ return true;
+}
+
+void
+Parser::reset_mtc_state ()
+{
+ _mtc_forward = false;
+ _mtc_running = MTC_Stopped;
+ _mtc_locked = false;
+ expected_mtc_quarter_frame_code = 0;
+ memset (_mtc_time, 0, sizeof (_mtc_time));
+ memset (_qtr_mtc_time, 0, sizeof (_mtc_time));
+ consecutive_qtr_frame_cnt = 0;
+ last_qtr_frame = 0;
+}
+
+void
+Parser::process_mtc_quarter_frame (byte *msg)
+{
+ int which_quarter_frame = (msg[1] & 0xf0) >> 4;
+
+ /* Is it an expected frame?
+ Remember, the first can be frame 7 or frame 0,
+ depending on the direction of the MTC generator ...
+ */
+
+#if 0
+ cerr << "MTC: (state = " << _mtc_running << ") "
+ << which_quarter_frame << " vs. " << expected_mtc_quarter_frame_code
+ << " consecutive ? " << consecutive_qtr_frame_cnt
+ << endl;
+#endif
+
+ if (_mtc_running == MTC_Stopped) {
+
+ /* we are stopped but are seeing qtr frame messages */
+
+ if (consecutive_qtr_frame_cnt == 0) {
+
+ /* first quarter frame */
+
+ if (which_quarter_frame != 0 && which_quarter_frame != 7) {
+
+ last_qtr_frame = which_quarter_frame;
+ consecutive_qtr_frame_cnt++;
+ }
+
+ // cerr << "first seen qframe = " << (int) last_qtr_frame << endl;
+
+ return;
+
+ } else if (consecutive_qtr_frame_cnt == 1) {
+
+ /* third quarter frame */
+
+ // cerr << "second seen qframe = " << (int) which_quarter_frame << endl;
+
+ if (last_qtr_frame < which_quarter_frame) {
+ _mtc_running = MTC_Forward;
+ } else if (last_qtr_frame > which_quarter_frame) {
+ _mtc_running = MTC_Backward;
+ }
+
+ mtc_status (_mtc_running);
+ }
+
+ switch (_mtc_running) {
+ case MTC_Forward:
+ if (which_quarter_frame == 7) {
+ expected_mtc_quarter_frame_code = 0;
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame + 1;
+ }
+ break;
+
+ case MTC_Backward:
+ if (which_quarter_frame == 0) {
+ expected_mtc_quarter_frame_code = 7;
+
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame - 1;
+ }
+ break;
+
+ case MTC_Stopped:
+ break;
+ }
+
+ } else {
+
+ /* already running */
+
+// for testing bad MIDI connections etc.
+// if ((random() % 500) < 10) {
+
+ if (which_quarter_frame != expected_mtc_quarter_frame_code) {
+
+ consecutive_qtr_frame_cnt = 0;
+
+#ifdef DEBUG_MTC
+ cerr << "MTC: (state = " << _mtc_running << ") "
+ << which_quarter_frame << " vs. " << expected_mtc_quarter_frame_code << endl;
+#endif
+
+ /* tell listener(s) that we skipped. if they return
+ true, just ignore this in terms of it being an error.
+ */
+
+ if (1) { /* mtc_skipped () */
+
+ /* no error, reset next expected frame */
+
+ switch (_mtc_running) {
+ case MTC_Forward:
+ if (which_quarter_frame == 7) {
+ expected_mtc_quarter_frame_code = 0;
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame + 1;
+ }
+ break;
+
+ case MTC_Backward:
+ if (which_quarter_frame == 0) {
+ expected_mtc_quarter_frame_code = 7;
+
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame - 1;
+ }
+ break;
+
+ case MTC_Stopped:
+ break;
+ }
+
+#ifdef DEBUG_MTC
+ cerr << "SKIPPED, next expected = " << expected_mtc_quarter_frame_code << endl;
+#endif
+ return;
+ }
+
+ /* go back to waiting for the first frame */
+
+ expected_mtc_quarter_frame_code = 0;
+ memset (_qtr_mtc_time, 0, sizeof (_qtr_mtc_time));
+
+ _mtc_running = MTC_Stopped;
+ _mtc_locked = false;
+ mtc_status (MTC_Stopped);
+
+ return;
+
+ } else {
+
+ /* received qtr frame matched expected */
+ consecutive_qtr_frame_cnt++;
+
+ }
+ }
+
+ /* time code is looking good */
+
+ switch (which_quarter_frame) {
+ case 0: // frames LS nibble
+ _qtr_mtc_time[0] |= msg[1] & 0xf;
+ break;
+
+ case 1: // frames MS nibble
+ _qtr_mtc_time[0] |= (msg[1] & 0xf)<<4;
+ break;
+
+ case 2: // seconds LS nibble
+ _qtr_mtc_time[1] |= msg[1] & 0xf;
+ break;
+
+ case 3: // seconds MS nibble
+ _qtr_mtc_time[1] |= (msg[1] & 0xf)<<4;
+ break;
+
+ case 4: // minutes LS nibble
+ _qtr_mtc_time[2] |= msg[1] & 0xf;
+ break;
+
+ case 5: // minutes MS nibble
+ _qtr_mtc_time[2] |= (msg[1] & 0xf)<<4;
+ break;
+
+ case 6: // hours LS nibble
+ _qtr_mtc_time[3] |= msg[1] & 0xf;
+ break;
+
+ case 7:
+
+ /* last quarter frame msg has the MS bit of
+ the hour in bit 0, and the SMPTE FPS type
+ in bits 5 and 6
+ */
+
+ _qtr_mtc_time[3] |= ((msg[1] & 0x1) << 4);
+ _mtc_fps = MTC_FPS ((msg[1] & 0x6) >> 1);
+ break;
+
+ default:
+ /*NOTREACHED*/
+ break;
+
+ }
+
+ mtc_qtr (*this); /* EMIT_SIGNAL */
+
+ // mtc (*this, &msg[1], msglen - 1);
+
+ switch (_mtc_running) {
+ case MTC_Forward:
+ if ((which_quarter_frame == 7)) {
+
+ /* we've reached the final of 8 quarter frame messages.
+ store the time, reset the pending time holder,
+ and signal anyone who wants to know the time.
+ */
+
+ if (consecutive_qtr_frame_cnt >= 8) {
+ memcpy (_mtc_time, _qtr_mtc_time, sizeof (_mtc_time));
+ memset (_qtr_mtc_time, 0, sizeof (_qtr_mtc_time));
+ if (!_mtc_locked) {
+ _mtc_locked = true;
+ }
+ mtc_time (_mtc_time, false);
+ }
+ expected_mtc_quarter_frame_code = 0;
+
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame + 1;
+ }
+ break;
+
+ case MTC_Backward:
+ if (which_quarter_frame == 0) {
+
+ /* we've reached the final of 8 quarter frame messages.
+ store the time, reset the pending time holder,
+ and signal anyone who wants to know the time.
+ */
+
+ if (consecutive_qtr_frame_cnt >= 8) {
+ memcpy (_mtc_time, _qtr_mtc_time, sizeof (_mtc_time));
+ memset (_qtr_mtc_time, 0, sizeof (_qtr_mtc_time));
+ if (!_mtc_locked) {
+ _mtc_locked = true;
+ }
+ mtc_time (_mtc_time, false);
+ }
+
+ expected_mtc_quarter_frame_code = 7;
+
+ } else {
+ expected_mtc_quarter_frame_code = which_quarter_frame - 1;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+}
diff --git a/libs/midi++2/port_request.cc b/libs/midi++2/port_request.cc
new file mode 100644
index 0000000000..d081bdb570
--- /dev/null
+++ b/libs/midi++2/port_request.cc
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2000 Paul Barton-Davis
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ $Id$
+*/
+
+#include <fcntl.h>
+#include <string.h>
+#include <midi++/port.h>
+#include <midi++/port_request.h>
+
+using namespace std;
+using namespace MIDI;
+
+PortRequest::PortRequest (const string &xdev,
+ const string &xtag,
+ const string &xmode,
+ const string &xtype)
+
+{
+ status = OK;
+
+ devname = strdup (xdev.c_str());
+ tagname = strdup (xtag.c_str());
+
+ if (xmode == "output" ||
+ xmode == "out" ||
+ xmode == "OUTPUT" ||
+ xmode == "OUT") {
+ mode = O_WRONLY;
+
+ } else if (xmode == "input" ||
+ xmode == "in" ||
+ xmode == "INPUT" ||
+ xmode == "IN") {
+ mode = O_RDONLY;
+
+ } else if (xmode == "duplex" ||
+ xmode == "DUPLEX" ||
+ xmode == "inout" ||
+ xmode == "INOUT") {
+ mode = O_RDWR;
+ } else {
+ status = Unknown;
+ }
+
+ if (xtype == "ALSA/RAW" ||
+ xtype == "alsa/raw") {
+ type = Port::ALSA_RawMidi;
+ } else if (xtype == "ALSA/SEQUENCER" ||
+ xtype == "alsa/sequencer") {
+ type = Port::ALSA_Sequencer;
+ } else if (xtype == "COREMIDI" ||
+ xtype == "coremidi") {
+ type = Port::CoreMidi_MidiPort;
+ } else if (xtype == "NULL" ||
+ xtype == "null") {
+ type = Port::Null;
+ } else if (xtype == "FIFO" ||
+ xtype == "fifo") {
+ type = Port::FIFO;
+ } else {
+ status = Unknown;
+ }
+}
+