From 0524a6a89db566881e3acf3cee9b39614d22a511 Mon Sep 17 00:00:00 2001 From: mahlzeit Date: Tue, 11 Mar 2003 18:52:03 +0000 Subject: [PATCH] Added Midi Kit end-user documentation. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@2881 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- docs/user/Doxyfile | 969 ++++++++++++++++++++++++++++++ docs/user/HOWTO | 34 ++ docs/user/book.dox | 8 + docs/user/footer.html | 7 + docs/user/midi/midi1intro.dox | 40 ++ docs/user/midi2/midi2concepts.png | Bin 0 -> 28518 bytes docs/user/midi2/midi2intro.dox | 303 ++++++++++ docs/user/midi2/midiconsumer.dox | 54 ++ docs/user/midi2/mididefs.dox | 89 +++ docs/user/midi2/midiendpoint.dox | 284 +++++++++ docs/user/midi2/midilocalcons.dox | 227 +++++++ docs/user/midi2/midilocalprod.dox | 222 +++++++ docs/user/midi2/midiproducer.dox | 75 +++ docs/user/midi2/midiroster.dox | 292 +++++++++ 14 files changed, 2604 insertions(+) create mode 100644 docs/user/Doxyfile create mode 100644 docs/user/HOWTO create mode 100644 docs/user/book.dox create mode 100644 docs/user/footer.html create mode 100644 docs/user/midi/midi1intro.dox create mode 100644 docs/user/midi2/midi2concepts.png create mode 100644 docs/user/midi2/midi2intro.dox create mode 100644 docs/user/midi2/midiconsumer.dox create mode 100644 docs/user/midi2/mididefs.dox create mode 100644 docs/user/midi2/midiendpoint.dox create mode 100644 docs/user/midi2/midilocalcons.dox create mode 100644 docs/user/midi2/midilocalprod.dox create mode 100644 docs/user/midi2/midiproducer.dox create mode 100644 docs/user/midi2/midiroster.dox diff --git a/docs/user/Doxyfile b/docs/user/Doxyfile new file mode 100644 index 0000000000..144e6c3394 --- /dev/null +++ b/docs/user/Doxyfile @@ -0,0 +1,969 @@ +# Doxyfile 1.2.17 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# General configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = "The OpenBeOS Book" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = R1 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = ../../distro/x86.R1/beos/docs + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, +# Finnish, French, German, Greek, Hungarian, Italian, Japanese, Japanese-en +# (Japanese with english messages), Korean, Norwegian, Polish, Portuguese, +# Romanian, Russian, Serbian, Slovak, Slovene, Spanish, Swedish and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these class will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all inherited +# members of a class in the documentation of that class as if those members were +# ordinary class members. Constructors, destructors and assignment operators of +# the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. It is allowed to use relative paths in the argument list. + +STRIP_FROM_PATH = + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower case letters. If set to YES upper case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# users are adviced to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explict @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# reimplements. + +INHERIT_DOCS = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consist of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. +# For instance some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = . midi midi2 ../../headers/os/midi2 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp +# *.h++ *.idl *.odl + +FILE_PATTERNS = *.dox *.h *.c *.cpp + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or directories +# that are symbolic links (a Unix filesystem feature) are excluded from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = . midi2 + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. + +INPUT_FILTER = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse. + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = footer.html + +# The HTML_STYLESHEET tag can be used to specify a user defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output dir. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non empty doxygen will try to run +# the html help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the Html help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 1 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript and frames is required (for instance Mozilla, Netscape 4.0+, +# or Internet explorer 4.0+). Note that for large projects the tree generation +# can take a very long time. In such cases it is better to disable this feature. +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimised for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assigments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_XML = NO + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_PREDEF_ONLY tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse the +# parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES tag can be used to specify one or more tagfiles. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in Html, RTF and LaTeX) for classes with base or +# super classes. Setting the tag to NO turns the diagrams off. Note that this +# option is superceded by the HAVE_DOT option below. This is only a fallback. It is +# recommended to install and use dot, since it yield more powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found on the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermedate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::addtions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO + +# The CGI_NAME tag should be the name of the CGI script that +# starts the search engine (doxysearch) with the correct parameters. +# A script with this name will be generated by doxygen. + +CGI_NAME = search.cgi + +# The CGI_URL tag should be the absolute URL to the directory where the +# cgi binaries are located. See the documentation of your http daemon for +# details. + +CGI_URL = + +# The DOC_URL tag should be the absolute URL to the directory where the +# documentation is located. If left blank the absolute path to the +# documentation, with file:// prepended to it, will be used. + +DOC_URL = + +# The DOC_ABSPATH tag should be the absolute path to the directory where the +# documentation is located. If left blank the directory on the local machine +# will be used. + +DOC_ABSPATH = + +# The BIN_ABSPATH tag must point to the directory where the doxysearch binary +# is installed. + +BIN_ABSPATH = /usr/local/bin/ + +# The EXT_DOC_PATHS tag can be used to specify one or more paths to +# documentation generated for other projects. This allows doxysearch to search +# the documentation for these projects as well. + +EXT_DOC_PATHS = diff --git a/docs/user/HOWTO b/docs/user/HOWTO new file mode 100644 index 0000000000..933bd8b784 --- /dev/null +++ b/docs/user/HOWTO @@ -0,0 +1,34 @@ + +THE OPENBEOS BOOK HOWTO + +The end user documentation for OpenBeOS is automatically generated from the +source code using the Doxygen tool. We are talking BeBook-style documentation +here, not development related docs (those belong in /current/docs/develop). + +This HOWTO only explains how to include your kit into the "OpenBeOS Book", it +is not a Doxygen tutorial. For information about using Doxygen, see the Doxygen +manual, www.doxygen.org, and OpenBeOS newletters 31 and 29. + +There are two ways to document your kit: + + 1) Put the Doxygen comments in your headers and/or source files. + 2) Put the Doxygen comments in separate files. + +Either way is fine. The documentation for the Midi Kit, for example, uses the +latter option. The files with the Doxygen comments all live in the midi2 subdir +of /current/docs/user. Of course, if you embed the Doxygen comments directly in +your source code, you don't need to make a subdir in /current/docs/user. + +There is one Doxygen config file (Doxyfile) for the entire book, so you don't +have to make your own Doxyfile. You just have to add the directories with your +commented files to the INPUT directive, so doxygen will know where to find them. +You probably also want to add a link to your kit on the main page (book.dox). + +To generate the docs, simply type "doxygen" in the Terminal. The script puts +the resulting HTML docs in "/current/distro/x86.R1/beos/docs". + +Note: theoretically, Doxygen allows us to treat each kit as a separate "module", +using the \defgroup and \ingroup tags. In practice, the results of this are a +little disappointing. That's why, at least for the time being, we simply lump +everything together, and give each kit one or more \page's. Feel free to take a +peek at the Midi Kit docs to figure out what the hell that means ;-) diff --git a/docs/user/book.dox b/docs/user/book.dox new file mode 100644 index 0000000000..dcfa514ac3 --- /dev/null +++ b/docs/user/book.dox @@ -0,0 +1,8 @@ +/*! +\mainpage The OpenBeOS Book + +\section kits Kits and Servers + +- \ref midi1 +- \ref midi2 +*/ diff --git a/docs/user/footer.html b/docs/user/footer.html new file mode 100644 index 0000000000..61cb3acb27 --- /dev/null +++ b/docs/user/footer.html @@ -0,0 +1,7 @@ +
+ +$projectname $projectnumber - $title
+Generated on $date by Doxygen $doxygenversion + + + diff --git a/docs/user/midi/midi1intro.dox b/docs/user/midi/midi1intro.dox new file mode 100644 index 0000000000..d2fcde9f19 --- /dev/null +++ b/docs/user/midi/midi1intro.dox @@ -0,0 +1,40 @@ +/*! +\page midi1 The old Midi Kit (libmidi.so) + +The old Midi Kit, or midi1 for short, goes all the way back to DR8, when the +BeOS only ran on BeBoxen. Fortunately for us, it is pretty well documented: + +- Midi +Kit chapter in the online Be Book +- Midi +Kit section of the old Be Developer Library +- Be Newsletter Volume 1, Issue 49 - Introduces the MIDI synth +- Be Newsletter Volume 1, Issue 52 - Follow-up on issue 49 +- Be Newsletter Volume 1, Issue 91 - How to use BSynth +- Be Newsletter Volume 1, Issue 102 - Axe sample code +- Be Newsletter Volume 1, Issue 104 - How to use BMidiPort +- Be Newsletter Volume 2, Issue 23 - EdMidi sample code +- Be Newsletter Volume 2, Issue 37 - How to use the MIDI synth +- Be Newsletter Volume 3, Issue 37 - Whistle sample code + +To summarize, there are four basic MIDI classes: + +- BMidi is the base class for most other classes from the Midi Kit +- BMidiPort can talk to a MIDI hardware port +- BMidiStore can read, write, and perform Standard MIDI files +- BMidiText is a debugging aid that dumps MIDI messages to stdout + +The following classes let you use the Midi Kit's General MIDI synthesizer: + +- BSynth controls the synthesizer +- BMidiSynth connects a BMidi object to the synth +- BMidiSynthFile connects a MIDI file to the synth +- BSamples lets you access the synth's sound data stream + +To make MIDI data stream through your application, you create a "network" of +BMidi-derived objects that send and receive MIDI messages. + +The old Midi Kit is slowly fading into obscurity. You may want to use the +\ref midi2 "new kit" instead. + +*/ diff --git a/docs/user/midi2/midi2concepts.png b/docs/user/midi2/midi2concepts.png new file mode 100644 index 0000000000000000000000000000000000000000..2278a4d53b08c2b342e7199607a06bce7cb1c2fa GIT binary patch literal 28518 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 B-^l;~ literal 0 HcmV?d00001 diff --git a/docs/user/midi2/midi2intro.dox b/docs/user/midi2/midi2intro.dox new file mode 100644 index 0000000000..5ba88c7654 --- /dev/null +++ b/docs/user/midi2/midi2intro.dox @@ -0,0 +1,303 @@ +/*! +\page midi2 The new Midi Kit (libmidi2.so) + +The Midi Kit is the API that implements support for generating, processing, and +playing music in MIDI format. MIDI, which +stands for 'Musical Instrument Digital Interface', is a well-established +standard for representing and communicating musical data. + +\section midi2twokits The two kits + +The BeOS comes with two different, but compatible Midi Kits. This documentation +focuses on the "new" Midi Kit, or midi2 as we like to call it, that was +introduced with BeOS R5. The old kit, which we'll refer to as midi1, is more +complete than the new kit, but less powerful. + +Both kits let you create so-called MIDI endpoints, but the endpoints from midi1 +cannot be shared between different applications. The midi2 kit solves that +problem, but unlike midi1 it does not include a General MIDI softsynth, nor +does it have a facility for reading and playing Standard MIDI Files. Don't +worry: both kits are compatible and you can mix-and-match them in your +applications. + +The main differences between the two kits: + +- Instead of one BMidi object that both produces and consumes events, we have +BMidiProducer and BMidiConsumer. +- Applications are capable of sharing MIDI producers and consumers with other +applications via the centralized Midi Roster. +- Physical MIDI ports are now sharable without apps "stealing" events from each +other. +- Applications can now send/receive raw MIDI byte streams (useful if an +application has its own MIDI parser/engine). +- Channels are numbered 0..15, not 1..16 +- Timing is now specified in microseconds instead of milliseconds. + +\section midi2concepts Midi Kit concepts + +A brief overview of the elements that comprise the Midi Kit: + +- \b Endpoints. This is what the Midi Kit is all about: sending MIDI messages +between endpoints. An endpoint is like a MIDI In or MIDI Out socket on your +equipment; it either receives information or it sends information. Endpoints +that send MIDI events are called \b producers; the endpoints that receive those +events are called \b consumers. An endpoint that is created by your own +application is called \b local; endpoints from other applications are \b +remote. You can access remote endpoints using \b proxies. + +- \b Filters. A filter is an object that has a consumer and a producer +endpoint. It reads incoming events from its consumer, performs some operation, +and tells its producer to send out the results. In its current form, the Midi +Kit doesn't provide any special facilities for writing filters. + +- \b Midi \b Roster. The roster is the list of all published producers and +consumers. By publishing an endpoint, you allow other applications to talk to +it. You are not required to publish your endpoints, in which case only your own +application can use them. + +- \b Midi \b Server. The Midi Server does the behind-the-scenes work. It +manages the roster, it connects endpoints, it makes sure that endpoints can +communicate, and so on. The Midi Server is started automatically when BeOS +boots, and you never have to deal with it directly. Just remember that it runs +the show. + +- \b libmidi. The BMidi* classes live inside two shared libraries: libmidi.so +and libmidi2.so. If you write an application that uses old Midi Kit, you must +link it to libmidi.so. Applications that use the new Midi Kit must link to +libmidi2.so. If you want to mix-and-match both kits, you should also link to +both libraries. + +Here is a pretty picture: + +\image html midi2concepts.png + +\section midi2mediakit Midi Kit != Media Kit + +Be chose not to integrate the Midi Kit into the Media Kit as another media +type, mainly because MIDI doesn't require any of the format negotiation that +other media types need. Although the two kits look similar -- both have a +"roster" for finding or registering "consumers" and "producers" -- there are +some very important differences. + +The first and most important point to note is that BMidiConsumer and +BMidiProducer in the Midi Kit are NOT directly analogous to BBufferConsumer and +BBufferProducer in the Media Kit! In the Media Kit, consumers and producers are +the data consuming and producing properties of a media node. A filter in the +Media Kit, therefore, inherits from both BBufferConsumer and BBufferProducer, +and implements their virtual member functions to do its work. + +In the Midi Kit, consumers and producers act as endpoints of MIDI data +connections, much as media_source and media_destination do in the Media Kit. +Thus, a MIDI filter does not derive from BMidiConsumer and BMidiProducer; +instead, it contains BMidiConsumer and BMidiProducer objects for each of its +distinct endpoints that connect to other MIDI objects. The Midi Kit does not +allow the use of multiple virtual inheritance, so you can't create an object +that's both a BMidiConsumer and a BMidiProducer. + +This also contrasts with the old Midi Kit's conception of a BMidi object, which +stood for an object that both received and sent MIDI data. In the new Midi Kit, +the endpoints of MIDI connections are all that matters. What lies between the +endpoints, i.e., how a MIDI filter is actually structured, is entirely at your +discretion. + +Also, rather than use token structs like media_node to make connections via the +MediaRoster, the new kit makes the connections directly via the BMidiProducer +object. + +\section midi2remotelocal Remote and local objects + +The Midi Kit makes a distinction between remote and local MIDI objects. You can +only create local MIDI endpoints, which derive from either BMidiLocalConsumer +or BMidiLocalProducer. Remote endpoints are endpoints that live in other +applications, and you access them through BMidiRoster. + +BMidiRoster only gives you access to BMidiEndpoints, BMidiConsumers, and +BMidiProducers. When you want to talk to remote MIDI objects, you do so through +the proxy objects that BMidiRoster provides. Unlike BMidiLocalConsumer and +BMidiLocalProducer, these classes do not provide a lot of functions. That is +intentional. In order to hide the details of communication with MIDI endpoints +in other applications, the Midi Kit must hide the details of how a particular +endpoint is implemented. + +So, what can you do with remote objects? Only what BMidiConsumer, +BMidiProducer, and BMidiEndpoint will let you do. You can connect objects, get +the properties of these objects -- and that's about it. + +\section midi2lifespan Creating and destroying objects + +The constructors and destructors of most midi2 classes are private, which mean +you cannot directly create them using the C++ new operator, on the +stack, or as globals. Nor can you delete them. Instead, these +objects are obtained through BMidiRoster. The only two exceptions to this rule +are BMidiLocalConsumer and BMidiLocalProducer. These two objects may be +directly created and subclassed by developers. + +\section midi2refcount Reference counting + +Each MIDI endpoint has a reference count associated with it, so that the Midi +Roster can do proper bookkeeping. When you construct a BMidiLocalProducer or +BMidiLocalConsumer endpoint, it starts with a reference count of 1. In +addition, BMidiRoster increments the reference count of any object it hands to +you as a result of \link BMidiRoster::NextEndpoint() NextEndpoint() \endlink or +\link BMidiRoster::FindEndpoint() FindEndpoint() \endlink. Once the count hits +0, the endpoint will be deleted. + +This means that, to delete an endpoint, you don't call the delete +operator directly; instead, you call \link BMidiEndpoint::Release() Release() +\endlink. To balance this call, there's also an \link BMidiEndpoint::Acquire() +Acquire() \endlink, in case you have two disparate parts of your application +working with the endpoint, and you don't want to have to keep track of who +needs to Release() the endpoint. + +When you're done with any endpoint object, you must Release() it. This is true +for both local and remote objects. Repeat after me: Release() when you're done. + +\section midi2events MIDI events + +To make some actual music, you need to \link BMidiProducer::Connect() Connect() +\endlink your consumers to your producers. Then you tell the producer to +"spray" MIDI events to all the connected consumers. The consumers are notified +of these incoming events through a set of hook functions. + +The Midi Kit already provides a set of commonly used spray functions, such as +\link BMidiLocalProducer::SprayNoteOn() SprayNoteOn() \endlink, \link +BMidiLocalProducer::SprayControlChange() SprayControlChange() \endlink, and so +on. These correspond one-to-one with the message types from the MIDI spec. You +don't need to be a MIDI expert to use the kit, but of course some knowledge of +the protocol helps. If you are really hardcore, you can also use the \link +BMidiLocalProducer::SprayData() SprayData() \endlink to send raw MIDI events to +the consumers. + +At the consumer side, a dedicated thread invokes a hook function for every +incoming MIDI event. For every spray function, there is a corresponding hook +function, e.g. \link BMidiLocalConsumer::NoteOn() NoteOn() \endlink and \link +BMidiLocalConsumer::ControlChange() ControlChange() \endlink. The hardcore MIDI +fanatics among you will be pleased to know that you can also tap into the \link +BMidiLocalConsumer::Data() Data() \endlink hook and get your hands dirty with +the raw MIDI data. + +\section midi2time Time + +The spray and hook functions accept a bigtime_t parameter named "time". This +indicates when the MIDI event should be performed. The time is given in +microseconds since the computer booted. To get the current tick measurement, +you call the system_time() function from the Kernel Kit. + +If you override a hook function in one of your consumer objects, it should look +at the time argument, wait until the designated time, and then perform its +action. The preferred method is to use the Kernel Kit's +snooze_until() function, which sends the consumer thread to sleep +until the requested time has come. (Or, if the time has already passed, returns +immediately.) + +Like this: + +\code +void MyConsumer::NoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time) +{ + snooze_until(time, B_SYSTEM_TIMEBASE); + ...do your thing... +} +\endcode + +If you want your producers to run in real time, i.e. they produce MIDI data +that needs to be performed immediately, you should pass time 0 to the spray +functions (which also happens to be the default value). Since time 0 has +already passed, snooze_until() returns immediately, and the +consumer will process the events as soon as they are received. + +To schedule MIDI events for a performance time that lies somewhere in the +future, the producer must take into account the consumer's latency. Producers +should attempt to get notes to the consumer by or before +(scheduled_performance_time - latency). The time argument is still the +scheduled performance time, so if your consumer has latency, it should snooze +like this before it starts to perform the events: + +\code +snooze_until(time - Latency(), B_SYSTEM_TIMEBASE); +\endcode + +Note that a typical producer sends out its events as soon as it can; unlike a +consumer, it does not have to snooze. + +\section midi2ports Other timing issues + +Each consumer object uses a Kernel Kit port to receive MIDI events from +connected producers. The queue for this port is only 1 message deep. This means +that if the consumer thread is asleep in a snooze_until(), it will +not read its port. Consequently, any producer that tries to write a new event +to this port will block until the consumer thread is ready to receive a new +message. This is intentional, because it prevents producers from generating and +queueing up thousands of events. + +This mechanism, while simple, puts on the producer the responsibility for +sorting the events in time. Suppose your producer sends three Note On events, +the first on t + 0, the second on t + 4, and the third on t + 2. This last +event won't be received until after t + 4, so it will be two ticks too late. If +this sort of thing can happen with your producer, you should somehow sort the +events before you spray them. Of course, if you have two or more producers +connected to the same consumer, it is nearly impossible to sort this all out +(pardon the pun). So it is not wise to send the same kinds of events from more +than one producer to one consumer at the same time. + +The article Introduction to MIDI, Part 2 in OpenBeOS +Newsletter 36 describes this problem in more detail, and provides a +solution. Go read it now! + +\section midi2filters Writing a filter + +A typical filter contains a consumer and a producer endpoint. It receives +events from the consumer, processes them, and sends them out again using the +producer. The consumer endpoint is a subclass of BMidiLocalConsumer, whereas +the producer is simply a BMidiLocalProducer, not a subclass. This is a common +configuration, because consumers work by overriding the event hooks to do work +when MIDI data arrives. Producers work by sending an event when you call their +member functions. You should hardly ever need to derive from BMidiLocalProducer +(unless you need to know when the producer gets connected or disconnected, +perhaps), but you'll always have to override one or more of +BMidiLocalConsumer's member functions to do something useful with incoming +data. + +Filters should ignore the time argument from the spray and hook functions, and +simply pass it on unchanged. Objects that only filter data should process the +event as quickly as possible and be done with it. Do not +snooze_until() in the consumer endpoint of a filter! + +\section midi2apidiffs API differences + +As far as the end user is concerned, the OpenBeOS Midi Kit is mostly the same +as the BeOS R5 kits, although there are a few small differences in the API +(mostly bug fixes): + +- BMidiEndpoint::IsPersistent() always returns false. +- The B_MIDI_CHANGE_LATENCY notification is now properly sent. The Be kit + incorrectly set be:op to B_MIDI_CHANGED_NAME, even though the rest of the + message was properly structured. +- If creating a local endpoint fails, you can still Release() the object + without crashing into the debugger. + +\section midi2seealso See also + +More about the Midi Kit: + +- \ref midi2defs +- Be Newsletter Volume 3, Issue 47 - Motor Mix sample code +- Be Newsletter Volume 4, Issue 3 - Overview of the new kit +- OpenBeOS + Newsletter 33, Introduction to MIDI, Part 1 +- OpenBeOS + Newsletter 36, Introduction to MIDI, Part 2 +- Sample code and other goodies at the + OpenBeOS Midi Kit team page + +Information about MIDI in general: + +- MIDI Manufacturers Association +- MIDI Tutorials +- MIDI Specification +- Standard MIDI File Format +- Jim Menard's MIDI Reference + +*/ diff --git a/docs/user/midi2/midiconsumer.dox b/docs/user/midi2/midiconsumer.dox new file mode 100644 index 0000000000..5083f5d64e --- /dev/null +++ b/docs/user/midi2/midiconsumer.dox @@ -0,0 +1,54 @@ +/*! +\class BMidiConsumer MidiConsumer.h +\brief Receives MIDI events from a producer + +A consumer is an object that knows how to deal with incoming MIDI events. A +consumer can be connected to multiple producers at the same time. There is no +way to find out which producers are connected to this consumer just by looking +at the BMidiConsumer object; you will have to consult BMidiRoster for that. + +A BMidiConsumer either represents a local consumer, i.e. a class extending from +BMidiLocalConsumer, or is a proxy for a remote object published by another app. + +*/ + +/*! +\fn bigtime_t BMidiConsumer::Latency() const +\brief Returns the latency of this consumer + +The latency is measured in microseconds. Producers should attempt to get MIDI +events to this consumer by (when - latency). You do this by subtracting +the latency from the performance time when you spray the events (provided that +you spray these events ahead of time, of course). + +You cannot set the latency on a BMidiConsumer, only on a +BMidiLocalConsumer. + +The latency issue gets slightly more complicated when multiple endpoints are +chained together, as in the following picture: + +\verbatim ++-------+ +-------------+ +-------+ +| | | | | | +| prodA |---->| consB prodB |---->| consC | +| | | | | | ++-------+ +-------------+ +-------+ + appA appB (filter) appC +\endverbatim + +Suppose consC has 200ms latency, and consB has 100ms latency. If consB simply +reports 100ms, then prodA will schedule its events for (t - 100), which is +really 200ms too late. (Of course, producers send out their events as soon as +possible, so depending on the load of the system, everything may work out just +fine.) + +ConsB should report the latency of the consumer that is hooked up to its +output, consC, in addition to its own latency. In other words, the full +downstream latency. So, the reported latency in this case would be 300ms. This +also means that appB should change the latency of consB when prodB makes or +breaks a connection, and when consC reports a latency change. (If multiple +consumers are connected to prodB, you should take the slowest one.) +Unfortunately, the Midi Kit provides no easy mechanism for doing any of this, +so you are on your own here. + +*/ diff --git a/docs/user/midi2/mididefs.dox b/docs/user/midi2/mididefs.dox new file mode 100644 index 0000000000..a2c92f2ded --- /dev/null +++ b/docs/user/midi2/mididefs.dox @@ -0,0 +1,89 @@ +/*! +\page midi2defs Midi Kit definitions (Midi2Defs.h) + +\subsection channelmasks Channel message masks + + + + + + + + + +
B_NOTE_OFF0x80
B_NOTE_ON0x90
B_KEY_PRESSURE0xa0
B_CONTROL_CHANGE0xb0
B_PROGRAM_CHANGE0xc0
B_CHANNEL_PRESSURE0xd0
B_PITCH_BEND0xe0
+ +\subsection systemmessages System messages + + + + + + + + + + + + + + + +
B_SYS_EX_START0xf0
B_MIDI_TIME_CODE0xf1
B_SONG_POSITION0xf2
B_SONG_SELECT0xf3
B_CABLE_MESSAGE0xf5
B_TUNE_REQUEST0xf6
B_SYS_EX_END0xf7
B_TIMING_CLOCK0xf8
B_START0xfa
B_CONTINUE0xfb
B_STOP0xfc
B_ACTIVE_SENSING0xfe
B_SYSTEM_RESET0xff
+ +\subsection controllernumbers Controller numbers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
B_MODULATION0x01
B_BREATH_CONTROLLER0x02
B_FOOT_CONTROLLER0x04
B_PORTAMENTO_TIME0x05
B_DATA_ENTRY0x06
B_MAIN_VOLUME0x07
B_MIDI_BALANCE0x08
B_PAN0x0a
B_EXPRESSION_CTRL0x0b
B_GENERAL_CTRL_10x10
B_GENERAL_CTRL_20x11
B_GENERAL_CTRL_30x12
B_GENERAL_CTRL_40x13
B_SUSTAIN_PEDAL0x40
B_PORTAMENTO0x41
B_SOSTENUTO0x42
B_SOFT_PEDAL0x43
B_HOLD_20x45
B_GENERAL_CTRL_50x50
B_GENERAL_CTRL_60x51
B_TEMPO_CHANGE0x51
B_GENERAL_CTRL_70x52
B_GENERAL_CTRL_80x53
B_EFFECTS_DEPTH0x5b
B_TREMOLO_DEPTH0x5c
B_CHORUS_DEPTH0x5d
B_CELESTE_DEPTH0x5e
B_PHASER_DEPTH0x5f
B_DATA_INCREMENT0x60
B_DATA_DECREMENT0x61
B_RESET_ALL_CONTROLLERS0x79
B_LOCAL_CONTROL0x7a
B_ALL_NOTES_OFF0x7b
B_OMNI_MODE_OFF0x7c
B_OMNI_MODE_ON0x7d
B_MONO_MODE_ON0x7e
B_POLY_MODE_ON0x7f
+ +\subsection bmidiop BMidiOp + + + + + + + + + + +
B_MIDI_NO_OP
B_MIDI_REGISTERED
B_MIDI_UNREGISTERED
B_MIDI_CONNECTED
B_MIDI_DISCONNECTED
B_MIDI_CHANGED_NAME
B_MIDI_CHANGED_LATENCY
B_MIDI_CHANGED_PROPERTIES
+ +*/ diff --git a/docs/user/midi2/midiendpoint.dox b/docs/user/midi2/midiendpoint.dox new file mode 100644 index 0000000000..87bbb4e8de --- /dev/null +++ b/docs/user/midi2/midiendpoint.dox @@ -0,0 +1,284 @@ +/*! +\class BMidiEndpoint MidiEndpoint.h +\brief Base class for all MIDI endpoints + +BMidiEndpoint is the abstract base class that represents either a producer or +consumer endpoint. It may be used to obtain the state, name, properties, or +system-wide ID of the object. BMidiEndpoint also provides the ability to change +the name and properties of endpoints that were created locally. + +Remember, you cannot call the destructor of BMidiEndpoint and its subclasses +directly. Endpoint objects are destructed automatically when their reference +count drops to zero. If necessary, the destructor of a local endpoint first +breaks off any connections and Unregister()'s the endpoint before it is +deleted. However, for good style and bonus points you should really \link +BMidiProducer::Disconnect() Disconnect() \endlink and Unregister() the object +yourself and not rely on the destructor to do this. + +*/ + +/*! +\fn const char* BMidiEndpoint::Name() const +\brief Returns the name of the endpoint + +The function never returns NULL. If you created a local endpoint by passing a +NULL name into its constructor (or passing no name, which is the same thing), +then Name() will return an empty string, not NULL. + +\sa SetName() + +*/ + +/*! +\fn void BMidiEndpoint::SetName(const char* name) +\brief Changes the name of the endpoint. + +Names don't have to be unique, but it is recommended that you give any +endpoints you publish meaningful and unique names, so users can easily +recognize what each endpoint does. There is no limit to the size of endpoint +names. + +Even though you can call this function on both remote and local objects, you +are only allowed to change the names of local endpoints; SetName() calls on +remote endpoints are ignored. + +\param name The new name. If you pass NULL, the name simply won't be changed. + +\sa Name() + +*/ + +/*! +\fn int32 BMidiEndpoint::ID() const +\brief Returns the ID of the endpoint + +An ID uniquely identifies an endpoint in the system. The ID is a signed 32-bit +number that is assigned by the Midi Server when the endpoint is created. (So +even if a local endpoint is not published, it still has a unique ID.) Valid IDs +range from 1 to 0x7FFFFFFF, the largest value an int32 can have. 0 and negative +values are not valid IDs. + +*/ + +/*! +\fn bool BMidiEndpoint::IsProducer() const +\brief Determines whether this endpoint is a BMidiProducer + +If it is, you can use a dynamic_cast to convert this object into a producer: + +\code +if (endp->IsProducer()) +{ + BMidiProducer* prod = dynamic_cast(endp); + .... +} +\endcode +*/ + +/*! +\fn bool BMidiEndpoint::IsConsumer() const +\brief Determines whether this endpoint is a BMidiConsumer + +If it is, you can use a dynamic_cast to convert this object into a consumer: + +\code +if (endp->IsConsumer()) +{ + BMidiConsumer* cons = dynamic_cast(endp); + .... +} +\endcode +*/ + +/*! +\fn bool BMidiEndpoint::IsRemote() const +\brief Determines whether this endpoint is a proxy for a remote object + +An endpoint is "remote" when it is created by another application. Obviously, +the remote object is Register()'ed as well, otherwise you would not be able to +see it. + +*/ + +/*! +\fn bool BMidiEndpoint::IsLocal() const +\brief Determines whether this endpoint represents a local object + +An endpoint is "local" when it is created by this application; in other words, +a BMidiLocalConsumer or BMidiLocalProducer. + +*/ + +/*! +\fn bool BMidiEndpoint::IsPersistent() const +\brief Not used + +The purpose of this function is unclear, and as a result it doesn't do anything +in the OpenBeOS implementation of the Midi Kit. + +\return Always returns false. +*/ + +/*! +\fn bool BMidiEndpoint::IsValid() const +\brief Determines whether the endpoint still exists + +Suppose you obtained a proxy object for a remote endpoint by querying the +BMidiRoster. What if the application that published this endpoint quits, or +less drastically, Unregister()'s that endpoint? Even though you still have a +BMidiEndpoint proxy object, the real endpoint no longer exists. You can use +IsValid() to check for this. + +Don't worry, operations on invalid objects, such as GetProperties(), will +return an error code (typically B_ERROR), but not cause a crash. Local objects +are always are considered to be valid, even if you did not Register() them. +(The only time a local endpoint is not valid is when there was a problem +constructing it.) + +If the application that created the remote endpoint crashes, then there is no +guarantee that the Midi Server immediately recognizes this. In that case, +IsValid() may still return true. Eventually, the stale endpoint will be removed +from the roster, though. From then on, IsValid() correctly returns false. + +*/ + +/*! +\fn status_t BMidiEndpoint::Acquire() +\brief Increments the endpoint's reference count + +Each BMidiEndpoint has a reference count associated with it, so that +BMidiRoster can do proper bookkeeping. Acquire() increments this reference +count, and Release() decrements it. Once the count reaches zero, the endpoint +is deleted. + +When you are done with the endpoint, whether local or remote, you should +always Release() it! + +Upon construction, local endpoints start with a reference count of 1. Any +objects you obtain from BMidiRoster using the NextXXX() or FindXXX() functions +have their reference counts incremented in the process. If you forget to call +Release(), the objects won't be properly cleaned up and you'll make a fool out +of yourself. + +After you Release() an object, you are advised not to use it any further. If +you do, your app will probably crash. That also happens if you Release() an +object too many times. + +Typically, you don't need to call Acquire(), unless you have two disparate +parts of your application working with the same endpoint, and you don't want to +have to keep track of who needs to Release() the endpoint. Now you simply have +both of them release it. + +\return Always returns B_OK +\sa Release() +*/ + +/*! +\fn status_t BMidiEndpoint::Release() +\brief Decrements the endpoint's reference count +\return Always returns B_OK +\sa Acquire() +*/ + +/*! +\fn status_t BMidiEndpoint::Register() +\brief Publishes the endpoint on the roster + +MIDI objects created by an application are invisible to other applications +until they are published. To publish an object use the Register() method. The +corresponding Unregister() method will cause an object to once again become +invisible to remote applications. + +BMidiRoster also has Register() and Unregister() methods. You may also use +those methods to publish or hide your endpoints; both do the same thing. + +Although it is considered bad style, calling Register() on local endpoints that +are already registered won't mess things up. The Midi Server will simply ignore +your request. Likewise for Unregister()'ing more than once. Attempts to +Register() or Unregister() remote endpoints will fail, of course. + +If you are \link BMidiRoster::StartWatching() watching \endlink, you will +not receive notifications for any local endpoints you register or +unregister. Of course, other applications will be notified about your +endpoints. + +Existing connections will not be broken when an object is unregistered, but +future remote connections will be denied. When objects are destroyed, they +automatically become unregistered. + +\return B_OK on success, or a negative error code (typically B_ERROR) if +something went wrong. + +\sa Unregister() +*/ + +/*! +\fn status_t BMidiEndpoint::Unregister() +\brief Hides the endpoint from the roster +\sa Register() +*/ + +/*! +\fn status_t BMidiEndpoint::SetProperties(const BMessage* props) +\brief Changes the properties of the endpoint + +Endpoints can have properties, which is any kind of information that might be +useful to associate with a MIDI object. The properties are stored in a +BMessage. + +Usage example: + +\code +BMessage props; +if (endpoint->GetProperties(&props) == B_OK) +{ + ...add data to the message... + endpoint->SetProperties(&props); +} +\endcode + +You are only allowed to call SetProperties() on a local object. + +Properties should follow a protocol, so different applications will know how to +read each other's properties. The current protocol is very limited -- it only +allows you to associate icons with your endpoints. Be planned to publish a more +complete protocol that included additional information, such as vendor/model +names, copyright/version info, category, etc., but they never got around to it. + + + + + +
propertyLarge (32x32) icon
field name"be:large_icon"
field type'ICON'
+ + + + + +
propertySmall (16x16) icon
field name"be:mini_icon"
field type'MICN'
+ +The MidiUtil package (downloadable from the OpenBeOS website) contains a number +of convenient functions to associate icons with endpoints, so you don't have to +write that code all over again. + +\sa GetProperties() +*/ + +/*! +\fn status_t BMidiEndpoint::GetProperties(BMessage* props) const +\brief Reads the properties of the endpoint + +Usage example: + +\code +BMessage props; +if (endpoint->GetProperties(&props) == B_OK) +{ + ...examine the contents of the message... +} +\endcode + +Note that GetProperties() overwrites the contents of your BMessage. + +\sa SetProperties() +*/ diff --git a/docs/user/midi2/midilocalcons.dox b/docs/user/midi2/midilocalcons.dox new file mode 100644 index 0000000000..1873c0c09f --- /dev/null +++ b/docs/user/midi2/midilocalcons.dox @@ -0,0 +1,227 @@ +/*! +\class BMidiLocalConsumer MidiConsumer.h +\brief A consumer endpoint that is created by your own application + +If you want to create a consumer that reacts to MIDI events, you should +subclass BMidiLocalConsumer. + +Each local consumer has its own thread that receives and dispatches the MIDI +events. Whenever MIDI data arrives, the Data() hook passes the MIDI event on to +a more specific hook function: NoteOn(), NoteOff(), SystemExclusive(), and so +on. Calls to these hook functions are serialized -- they will never have to be +re-entrant. They also should not be called from outside the thread that is +invoking them. + +Your subclass can override any of the MIDI event hooks. BMidiLocalConsumer +doesn't provide default implementations for them, so you don't have to call a +hook's default implementation if you override it. For complete control, you can +also override Data(). + +Most hook functions take a channel argument. Even though MIDI channels are +really numbered 1 through 16, the hook functions work with channels 0 through +15. The performance time for the event is specified in microseconds relative to +the system time base. A performance time that is 0 (or really any time in the +past) means "play as soon as possible". See the \ref midi2time "introduction" +for more information about timing and consumers. + +The thread driving the consumer's events is a very high priority real time +thread. Events should be handled as quickly as possible (not counting +snoozing). If non-time-critical computation is needed it may be wise to queue +events up for a lower priority thread to handle them external to the main event +thread. + +*/ + +/*! +\fn BMidiLocalConsumer::BMidiLocalConsumer(const char *name = NULL) +\brief Creates a new local consumer endpoint + +The new endpoint is not visible to other applications until you Register() it. + +You can tell the constructor what the name of the new consumer will be. If you +pass NULL (or use the default argument), then the consumer's name will be an +empty string. It won't be NULL, since endpoint names cannot be NULL. + +There is no guarantee that the endpoint will be successfully created. For +example, the Midi Server may not be running. Therefore, you should always call +IsValid() after creating a new endpoint to make sure that everything went okay. +If not, Release() the object to reclaim memory and abort gracefully. + +\code +MyConsumer* cons = new MyConsumer(...); +if (!cons->IsValid()) +{ + cons->Release(); + ...exit gracefully... +} +\endcode + +*/ + +/*! +\fn void BMidiLocalConsumer::SetLatency(bigtime_t latency) +\brief Changes the published latency of the consumer +\sa Latency() +*/ + +/*! +\fn int32 BMidiLocalConsumer::GetProducerID() +\brief Returns the ID of the producer that most recently sent a MIDI event to +this consumer + +You can call this from one of the hooks to determine which producer the event +came from. + +*/ + +/*! +\fn void BMidiLocalConsumer::SetTimeout(bigtime_t when, void* data) +\brief Requests that the Timeout() hook will be called at some point + +This method asks the consumer thread to call the Timeout() hook as soon as +possible after the timeout expires. For every call to SetTimeout(), the +Timeout() hook is only called once. Note: the term "timeout" may be a little +misleading; the hook will always be called, even if events are received +in the mean time. Apparently, this facility is handy for dealing with early +events. + +Note that the event thread blocks on the consumer's port as long as no events +arrive. By default no timeout is set, and as a result the thread blocks +forever. Your call to SetTimeout() doesn't change this. The new timeout value +will go into effect the next time the thread tries to read from the port, i.e. +after the first event has been received. If no event ever comes in, the +Timeout() hook will never be called. This also means that you cannot cancel a +timeout once you have set it. To repeat, calling SetTimeout() only takes effect +after at least one new event has been received. + +\param when An absolute time that's measured against the system clock. + +\param data A pointer to a "cookie" that you can pass along to Timeout(). The +data is not copied, so you must ensure that the pointer remains valid until +Timeout() is called. You typically delete the data inside Timeout(). + +*/ + +/*! +\fn void BMidiLocalConsumer::Timeout(void* data) +\brief Hook function that is called per your own request +\sa SetTimeout() +*/ + +/*! +\fn void BMidiLocalConsumer::Data( + uchar* data, size_t length, bool atomic, bigtime_t time) +\brief Invoked when raw MIDI is received + +What the default implementation of Data() does depends on the value of atomic. +If atomic is true, the data received comprises a single MIDI event; i.e. one +status byte followed by the appropriate number of data bytes and nothing else. +In this case, Data() calls the event-specific hook function that corresponds to +that status byte. This optimization is used by the Midi Kit to allow faster +dispatch of events generated by the specific Spray functions from +BMidiLocalProducer. + +If atomic is false, Data() ignores the MIDI event. If you want a consumer to +handle non-atomic events, you have to override Data() and program this +yourself. In that case, you probably also want to call the default +implementation to handle the "normal" MIDI events. + +Data() is rarely overridden, but you can override it if you want to. If you do, +remember that the data buffer is owned by the Midi Kit. Do not attempt to +modify or free it, lest you wish to be laughed at by other developers. + +\param data the MIDI event data +\param length byte size of the data buffer +\param atomic whether the data buffer contains a single complete MIDI event +\param time the requested performance time of the event + +\sa BMidiLocalProducer::SprayData() +*/ + +/*! +\fn void BMidiLocalConsumer::NoteOff( + uchar channel, uchar note, uchar velocity, bigtime_t time) +\brief Invoked when a Note Off event is received +\sa BMidiLocalProducer::SprayNoteOff() +*/ + +/*! +\fn void BMidiLocalConsumer::NoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time) +\brief Invoked when a Note On event is received +\sa BMidiLocalProducer::SprayNoteOn() +*/ + +/*! +\fn void BMidiLocalConsumer::KeyPressure( + uchar channel, uchar note, uchar pressure, bigtime_t time) +\brief Invoked when a Polyphonic Pressure (Aftertouch) event is received +\sa BMidiLocalProducer::SprayKeyPressure() +*/ + +/*! +\fn void BMidiLocalConsumer::ControlChange( + uchar channel, uchar controlNumber, uchar controlValue, bigtime_t time) +\brief Invoked when a Controller Change event is received +\sa BMidiLocalProducer::SprayControlChange() +*/ + +/*! +\fn void BMidiLocalConsumer::ProgramChange( + uchar channel, uchar programNumber, bigtime_t time) +\brief Invoked when a Program Change event is received +\sa BMidiLocalProducer::SprayProgramChange() +*/ + +/*! +\fn void BMidiLocalConsumer::ChannelPressure( + uchar channel, uchar pressure, bigtime_t time) +\brief Invoked when a Channel Pressure event is received +\sa BMidiLocalProducer::SprayChannelPressure() +*/ + +/*! +\fn void BMidiLocalConsumer::PitchBend( + uchar channel, uchar lsb, uchar msb, bigtime_t time) +\brief Invoked when a Pitch Bend event is received +\sa BMidiLocalProducer::SprayPitchBend() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemExclusive( + void* data, size_t length, bigtime_t time) +\brief Invoked when a System Exclusive event is received + +The data belongs to the Midi Kit and is only valid for the duration of this +event. You may not modify or free it. + +\sa BMidiLocalProducer::SpraySystemExclusive() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemCommon( + uchar status, uchar data1, uchar data2, bigtime_t time) +\brief Invoked when a System Common event is received + +Not all data bytes are used for all common events. Unused bytes are set to 0. + +\sa BMidiLocalProducer::SpraySystemCommon() +*/ + +/*! +\fn void BMidiLocalConsumer::SystemRealTime( + uchar status, bigtime_t time) +\brief Invoked when a Real Time event is received +\sa BMidiLocalProducer::SpraySystemRealTime() +*/ + +/*! +\fn void BMidiLocalConsumer::TempoChange(int32 beatsPerMinute, bigtime_t time) +\brief Never invoked, just like in the BeOS R5 implementation +\sa BMidiLocalProducer::SprayTempoChange() +*/ + +/*! +\fn void BMidiLocalConsumer::AllNotesOff(bool justChannel, bigtime_t time) +\brief Not used +*/ diff --git a/docs/user/midi2/midilocalprod.dox b/docs/user/midi2/midilocalprod.dox new file mode 100644 index 0000000000..e1ec09d2eb --- /dev/null +++ b/docs/user/midi2/midilocalprod.dox @@ -0,0 +1,222 @@ +/*! +\class BMidiLocalProducer MidiProducer.h +\brief A producer endpoint that is created by your own application + +You create a BMidiLocalProducer if you want your application to send MIDI +events. You use the various spray functions to send events to all connected +consumers. If no consumers are connected to the producer, any calls to the +spray functions are ignored. + +Most spray functions accept a channel argument. Even though MIDI channels are +really numbered 1 through 16, the spray functions work with channels 0 through +15. You can also specify the performance time for the event using the time +argument. Specify 0 (or any time in the past) to perform the event "now", i.e. +as soon as possible. You can also schedule events to be performed in the +future, by passing a time such as system_time() + 5000000, which means 5 +seconds from now. + +Unlike BMidiLocalConsumer, which should be subclassed almost always, you hardly +ever need to derive a class from BMidiLocalProducer. The only reason for +subclassing is when you need to know when the producer gets connected or +disconnected. + +Also unlike consumers, local producers have no thread of control directly +associated with them. If you want to send out the MIDI events from a different +thread, you will have to create one yourself. + +*/ + +/*! +\fn BMidiLocalProducer::BMidiLocalProducer(const char *name = NULL) +\brief Creates a new local producer endpoint + +The new endpoint is not visible to other applications until you Register() it. + +You can tell the constructor what the name of the new producer will be. If you +pass NULL (or use the default argument), then the producer's name will be an +empty string. It won't be NULL, since endpoint names cannot be NULL. + +There is no guarantee that the endpoint will be successfully created. For +example, the Midi Server may not be running. Therefore, you should always call +IsValid() after creating a new endpoint to make sure that everything went okay. +If not, Release() the object to reclaim memory and abort gracefully. + +\code +BMidiLocalProducer* prod = new BMidiLocalProducer(...); +if (!prod->IsValid()) +{ + prod->Release(); + ...exit gracefully... +} +\endcode + +*/ + +/*! +\fn void BMidiLocalProducer::Connected(BMidiConsumer* cons) +\brief Invoked when a new consumer is connected to this producer + +Although typical notifications (i.e. from BMidiRoster's "watching" facility) +are only sent if it is some other app that is performing the operation, +Connected() is also called if you are making the connection yourself. + +If you override this hook, you don't have to call the default implementation, +because that does nothing. + +\param cons The newly connected consumer. The reference count of the consumer +object is not increased, so you should not Release() it. However, if you want +to keep track of the consumer beyond this function, you should first Acquire() +it, and Release() it when you are done. + +\sa Disconnected() +*/ + +/*! +\fn void BMidiLocalProducer::Disconnected(BMidiConsumer* cons) +\brief Invoked when a consumer is disconnected from this producer +\sa Connected() +*/ + +/*! +\fn void BMidiLocalProducer::SprayData( + void* data, size_t length, bool atomic = false, bigtime_t time = 0) const +\brief Sends raw MIDI data downstream to all connected consumers + +Typically you won't have to call SprayData(); the other spray functions will do +just fine. If you do call it, remember that you retain ownership of the data +and that you are responsible for freeing it at some point. (Even though data is +not declared const, the function does not change it.) + +With atomic set to false, you can send a MIDI message in segments (perhaps for +a large sysex dump). However, when you do this, you are on your own. The Midi +Kit only tags the data as being non-atomic, but offers no additional support. +The default implementation of BMidiLocalConsumer completely ignores such +events. To handle non-atomic MIDI data, you should override the +BMidiLocalConsumer::Data() hook and process the MIDI event yourself. All of +BMidiLocalProducer's other spray functions always send atomic data. + +\param data the MIDI event data +\param length byte size of the data buffer +\param atomic whether the data buffer contains a single complete MIDI event +\param time the required performance time of the event + +\sa BMidiLocalConsumer::Data() +*/ + +/*! +\fn void BMidiLocalProducer::SprayNoteOff( + uchar channel, uchar note, uchar velocity, bigtime_t time = 0) const +\brief Sends a Note Off event to all connected consumers +\sa BMidiLocalConsumer::NoteOff() +*/ + +/*! +\fn void BMidiLocalProducer::SprayNoteOn( + uchar channel, uchar note, uchar velocity, bigtime_t time = 0) const +\brief Sends a Note On event to all connected consumers +\sa BMidiLocalConsumer::NoteOn() +*/ + +/*! +\fn void BMidiLocalProducer::SprayKeyPressure( + uchar channel, uchar note, uchar pressure, bigtime_t time = 0) const +\brief Sends a Polyphonic Pressure (Aftertouch) event to all connected +consumers +\sa BMidiLocalConsumer::KeyPressure() +*/ + +/*! +\fn void BMidiLocalProducer::SprayControlChange( + uchar channel, uchar controlNumber, uchar controlValue, + bigtime_t time = 0) const +\brief Sends a Controller Change event to all connected consumers + +\sa \ref controllernumbers +\sa BMidiLocalConsumer::ControlChange() +*/ + +/*! +\fn void BMidiLocalProducer::SprayProgramChange( + uchar channel, uchar programNumber, bigtime_t time = 0) const +\brief Sends a Program Change event to all connected consumers +\sa BMidiLocalConsumer::ProgramChange() +*/ + +/*! +\fn void BMidiLocalProducer::SprayChannelPressure( + uchar channel, uchar pressure, bigtime_t time = 0) const +\brief Sends a Channel Pressure event to all connected consumers +\sa BMidiLocalConsumer::ChannelPressure() +*/ + +/*! +\fn void BMidiLocalProducer::SprayPitchBend( + uchar channel, uchar lsb, uchar msb, bigtime_t time = 0) const +\brief Sends a Pitch Bend event to all connected consumers +\sa BMidiLocalConsumer::PitchBend() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemExclusive( + void* data, size_t length, bigtime_t time = 0) const +\brief Sends a System Exclusive event to all connected consumers + +You retain ownership of the data and are responsible for freeing it. Even +though data is not declared const, the function does not change it. Even though +the amount of data may be quite large, this function always sends sysex +messages as an atomic block of data. + +\sa BMidiLocalConsumer::SystemExclusive() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemCommon( + uchar status, uchar data1, uchar data2, bigtime_t time = 0) const +\brief Sends a System Common event to the connected consumers + +The status byte must be one of the following: + + + + + + + + +
0xF1B_MIDI_TIME_CODEdata1 only
0xF2B_SONG_POSITIONdata1 and data2
0xF3B_SONG_SELECTdata1 only
0xF5B_CABLE_MESSAGEdata1 only
0xF6B_TUNE_REQUESTno data
0xF7B_SYS_EX_ENDno data
+ +\sa BMidiLocalConsumer::SystemCommon() +*/ + +/*! +\fn void BMidiLocalProducer::SpraySystemRealTime( + uchar status, bigtime_t time = 0) const +\brief Sends a Real Time event to the connected consumers + +The status byte must be one of the following: + + + + + + + + +
0xF8B_TIMING_CLOCK
0xFAB_START
0xFBB_CONTINUE
0xFCB_STOP
0xFEB_ACTIVE_SENSING
0xFFB_SYSTEM_RESET
+ +Because of their high priority, the MIDI specification allows real time +messages to "interleave" with other MIDI messages. A large sysex dump, for +example, may be interrupted by a real time event. The Midi Kit, however, +doesn't care. If you (or another producer) have just sent a big system +exclusive to a consumer, any following real time message will simply have to +wait until the consumer has dealt with the sysex. + +\sa BMidiLocalConsumer::SystemRealTime() +*/ + +/*! +\fn void BMidiLocalProducer::SprayTempoChange( + int32 bpm, bigtime_t time = 0) const +\brief Not used +\sa BMidiLocalConsumer::TempoChange() +*/ diff --git a/docs/user/midi2/midiproducer.dox b/docs/user/midi2/midiproducer.dox new file mode 100644 index 0000000000..9af0f395f0 --- /dev/null +++ b/docs/user/midi2/midiproducer.dox @@ -0,0 +1,75 @@ +/*! +\class BMidiProducer MidiProducer.h +\brief Streams MIDI events to connected consumers + +A producer is an object that generate a stream of MIDI events. Each producer +has a list of BMidiConsumer objects to which it is connected, and may be asked +to connect to or disconnect from a BMidiConsumer. A producer can spray its +events to multiple consumers at the same time. + +A BMidiProducer either represents a local producer, i.e. a class extending from +BMidiLocalProducer, or is a proxy for a remote object published by another app. + +*/ + +/*! +\fn status_t BMidiProducer::Connect(BMidiConsumer* cons) +\brief Connects a consumer to this producer + +Establishes a connection between this producer and the specified consumer +endpoint. From now on, any events that this producer sprays will be sent to +that consumer. You may connect multiple consumers to a producer. + +\return B_OK on success, or an error code when the connection could not be +established. If the consumer is a proxy for a remote object and that object no +longer exists, Connect() returns B_ERROR. It also returns B_ERROR if you try to +connect the same producer and consumer more than once. + +\sa Disconnect() +*/ + +/*! +\fn status_t BMidiProducer::Disconnect(BMidiConsumer* cons) +\brief Disconnects a consumer from this producer + +Terminates the connection between this producer and the specified consumer +endpoint. From now on, any events that this producer sprays no longer go to +that consumer. + +\return B_OK on success, or an error code if there was no connection to break + +\sa Connect() +*/ + +/*! +\fn bool BMidiProducer::IsConnected(BMidiConsumer* cons) const +\brief Determines whether a consumer is connected to this producer +\sa Connect() +\sa Disconnect() +*/ + +/*! +\fn BList* BMidiProducer::Connections() const +\brief Returns a list with all connected consumers + +Returns a BList with pointers to BMidiEndpoint objects for all consumers that +are connected to this producer. You can examine the contents of the list as +follows: + +\code +BList* list = prod->Connections(); +for (int32 t = 0; t < list->CountItems(); ++t) +{ + BMidiEndpoint* endp = (BMidiEndpoint*) list->ItemAt(t); + ...do stuff... + endp->Release(); // yes, here too! +} +delete list; +\endcode + +Every time you call this function, a new BList is allocated. The caller (that +is you) is responsible for freeing this list. The BMidiEndpoint objects in the +list have their reference counts bumped, so you need to Release() them before +you delete the list or they will go all leaky on you. + +*/ diff --git a/docs/user/midi2/midiroster.dox b/docs/user/midi2/midiroster.dox new file mode 100644 index 0000000000..5389882805 --- /dev/null +++ b/docs/user/midi2/midiroster.dox @@ -0,0 +1,292 @@ +/*! +\class BMidiRoster MidiRoster.h +\brief Interface to the system-wide Midi Roster + +BMidiRoster allows you to find available MIDI consumer and producer objects. +You can locate these objects using the iterative NextEndpoint(), +NextProducer(), and NextConsumer() methods or by requesting notification +messages to be sent with StartWatching(). Notification messages may contain +object IDs which can be resolved using the FindEndpoint(), FindProducer(), and +FindConsumer() methods. + +The constructor and destructor of BMidiRoster are private, which means that you +cannot create or delete your own BMidiRoster objects. Every application can +have only one instance of BMidiRoster, which is automatically created the very +first time you use a Midi Kit function. + +You can call BMidiRoster's functions like this: + +\code +producer = BMidiRoster::FindProducer(someID); +\endcode + +Or using the slightly more annoying: + +\code +BMidiRoster* roster = BMidiRoster::MidiRoster(); +if (roster != NULL) +{ + producer = roster->FindProducer(someID); +} +\endcode +*/ + +/*! +\fn BMidiEndpoint* BMidiRoster::NextEndpoint(int32* id) +\brief Returns the next endpoint from the roster + +The "next endpoint" means: the endpoint with the ID that follows id. So if you +set id to 3, the first possible endpoint it returns is endpoint 4. No endpoint +can have ID 0, so passing 0 gives you the first endpoint. If you pass NULL +instead of an ID, NextEndpoint() always returns NULL. When the function +returns, it sets id to the ID of the endpoint that was found. If no more +endpoints exist, NextEndpoint() returns NULL and id is not changed. +NextEndpoint() does not return locally created endpoints, even if they +are Register()'ed. + +Usage example: + +\code +int32 id = 0; +BMidiEndpoint* endp; +while ((endp = BMidiRoster::NextEndpoint(&id)) != NULL) +{ + ...do something with endpoint ... + endp->Release(); // don't forget! +} +\endcode + +Remember that NextEndpoint() bumps the endpoint's reference count, so you +should always \link BMidiEndpoint::Release() Release() \endlink it when you are +done. + +*/ + +/*! +\fn BMidiProducer* BMidiRoster::NextProducer(int32* id) +\brief Returns the next producer from the roster + +Like NextEndpoint(), but only returns producer endpoints. + +\sa NextConsumer +\sa NextEndpoint +*/ + +/*! +\fn BMidiConsumer* BMidiRoster::NextConsumer(int32* id) +\brief Returns the next consumer from the roster + +Like NextEndpoint(), but only returns consumer endpoints. + +\sa NextProducer +\sa NextEndpoint +*/ + +/*! +\fn BMidiEndpoint* BMidiRoster::FindEndpoint( + int32 id, bool localOnly = false) +\brief Returns the endpoint with the specified ID + +FindEndpoint() will always find any local endpoints created by this +application; they do not have to be published with Register() first. If +localOnly is false, FindEndpoint() also looks at remote endpoints, otherwise +only local endpoints will be resolved. Returns NULL if no such endpoint could +be found. + +You should use a dynamic_cast to convert the BMidiEndpoint into a producer or +consumer: + +\code +BMidiEndpoint* endp = ...; +BMidiProducer* prod = NULL; +BMidiConsumer* cons = NULL; + +if (endp->IsProducer()) +{ + prod = dynamic_cast(endp); +} +else if (endp->IsConsumer()) +{ + cons = dynamic_cast(endp); +} +\endcode + +Remember that FindEndpoint() increments the endpoint's reference count, so you +should always \link BMidiEndpoint::Release() Release() \endlink an endpoint +when you are done with it: + +\code +BMidiEndpoint* endp = BMidiRoster::FindEndpoint(someID); +if (endp != NULL) +{ + ...do stuff with the endpoint... + endp->Release(); +} +\endcode + +*/ + +/*! +\fn BMidiProducer* BMidiRoster::FindProducer( + int32 id, bool localOnly = false) +\brief Finds the producer with the specified ID + +Like FindEndpoint(), but only looks for producer endpoints. Returns NULL if no +endpoint with that ID exists, or if that endpoint is not a producer. + +\sa FindConsumer +\sa FindEndpoint +*/ + +/*! +\fn BMidiConsumer* BMidiRoster::FindConsumer( + int32 id, bool localOnly = false) +\brief Finds the consumer with the specified ID + +Like FindEndpoint(), but only looks for consumer endpoints. Returns NULL if no +endpoint with that ID exists, or if that endpoint is not a consumer. + +\sa FindProducer +\sa FindEndpoint +*/ + +/*! +\fn void BMidiRoster::StartWatching(const BMessenger* msngr) +\brief Start receiving notifications from the Midi Roster + +When you start watching, BMidiRoster sends you notifications for all currently +published remote endpoints, and all the current connections +between them. (At this point, BMidiRoster does not let you know about +connections between unpublished endpoints, nor does it tell you anything about +your local endpoints, even though they may be published.) + +Thereafter, you'll receive notifications any time something important happens +to an object. The application that performs these operations is itself not +notified. The assumption here is that you already know about these changes, +because you are the one that is performing them. + +The notifications are BMessages with code B_MIDI_EVENT. You specify the +BMessenger that will be used to send these messages. Each message contains a +field called be:op that describes the type of notification. + +The "registered" and "unregistered" notifications are sent when a remote +endpoint Register()'s or Unregister()'s, respectively. You don't receive these +notifications when you register or unregister your local endpoints, but the +other apps will. + + + + + +
be:opint32B_MIDI_REGISTERED
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
+ + + + + +
be:opint32B_MIDI_UNREGISTERED
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
+ +The "connected" and "disconnected" notifications are sent when a consumer \link +BMidiProducer::Connect() Connect()\endlink's to a producer, or when they \link +BMidiProducer::Disconnect() Disconnect() \endlink. You will receive these +notifications when any two endpoints connect or disconnect, even if they +are not published. (The purpose of which is debatable.) You won't receive the +notifications if you are the one making the connection, even if both endpoints +are remote. You will be notified when another app connects one of your +published endpoints. + + + + + +
be:opint32B_MIDI_CONNECTED
be:producerint32ID of the connector
be:consumerint32ID of the connectee
+ + + + + +
be:opint32B_MIDI_DISCONNECTED
be:producerint32ID of the connector
be:consumerint32ID of the connectee
+ +The following notifications are sent when an endpoint's attributes are changed. +You receive these notifications only if another application is changing one of +its published endpoints. + + + + + + +
be:opint32B_MIDI_CHANGED_NAME
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:namestringthe endpoint's new name
+ + + + + + +
be:opint32B_MIDI_CHANGED_LATENCY
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:latencyint64the new latency (microseconds)
+ + + + + + +
be:opint32B_MIDI_CHANGED_PROPERTIES
be:idint32ID of the endpoint
be:typestring"producer" or "consumer"
be:propertiesBMessagethe new properties
+ +Typical usage example: + +\code +void MyView::AttachedToWindow() +{ + BMessenger msgr(this); + BMidiRoster::StartWatching(&msgr); +} + +void MyView::MessageReceived(BMessage* msg) +{ + switch (msg->what) + { + case B_MIDI_EVENT: + HandleMidiEvent(msg); + break; + default: + super::MessageReceived(msg); + break; + } +} +\endcode + +\sa \ref bmidiop "The possible be:op codes (BMidiOp)" +*/ + +/*! +\fn void BMidiRoster::StopWatching() +\brief Stop receiving notifications from the Midi Roster +\sa StartWatching() +*/ + +/*! +\fn status_t BMidiRoster::Register(BMidiEndpoint* object) +\brief Publishes an endpoint to other applications + +Calls BMidiEndpoint's \link BMidiEndpoint::Register() Register() \endlink +method to publish an endpoint, which makes it visible to other applications. + +*/ + +/*! +\fn status_t BMidiRoster::Unregister(BMidiEndpoint* object) +\brief Hides an endpoint from other applications + +Calls BMidiEndpoint's \link BMidiEndpoint::Unregister() Unregister() \endlink +method to hide a previously published endpoint from other applications. + +*/ + +/*! +\fn BMidiRoster* BMidiRoster::MidiRoster() +\brief Returns a pointer to the only instance of BMidiRoster + +There is no real reason use this function, since all BMidiRoster's public +function are static. + +*/