├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── PortMidi.cabal ├── README.txt ├── Setup.hs ├── Sound ├── PortMidi.hs └── PortMidi │ └── DeviceInfo.hsc ├── appveyor.yml ├── portmidi ├── license.txt ├── pm_common │ ├── pminternal.h │ ├── pmutil.c │ ├── pmutil.h │ ├── portmidi.c │ └── portmidi.h ├── pm_linux │ ├── pmlinux.c │ ├── pmlinux.h │ ├── pmlinuxalsa.c │ └── pmlinuxalsa.h ├── pm_mac │ ├── pmmac.c │ ├── pmmac.h │ ├── pmmacosxcm.c │ └── pmmacosxcm.h ├── pm_win │ ├── pmwin.c │ ├── pmwinmm.c │ └── pmwinmm.h └── porttime │ ├── porttime.c │ ├── porttime.h │ ├── ptlinux.c │ ├── ptmacosx_mach.c │ └── ptwinmm.c └── stack.yaml /.travis.yml: -------------------------------------------------------------------------------- 1 | # Based on https://docs.haskellstack.org/en/stable/travis_ci/ 2 | 3 | sudo: false 4 | 5 | language: generic 6 | 7 | os: 8 | - osx 9 | - linux 10 | 11 | # GHC versions prior to 7.10.3 are not supported. 12 | # 13 | # Based on "Latest (stack) LTS per GHC version" (https://www.stackage.org/) 14 | env: 15 | - RESOLVER="lts-6.35" # ghc 7.10.3 16 | - RESOLVER="lts-9.21" # ghc 8.0.2 17 | - RESOLVER="lts-11.20" # ghc 8.2.2 18 | - RESOLVER="lts-12.5" # ghc 8.4.3 19 | - RESOLVER="lts-16.2" . # ghc 8.8.3 20 | - RESOLVER="nightly" 21 | 22 | matrix: 23 | allow_failures: 24 | - env: RESOLVER="nightly" 25 | 26 | cache: 27 | directories: 28 | - $HOME/.stack 29 | timeout: 1800 30 | 31 | 32 | addons: 33 | apt: 34 | packages: 35 | - libasound2-dev 36 | 37 | before_install: 38 | 39 | - set -ex 40 | - mkdir -p ~/.local/bin 41 | - export PATH=$HOME/.local/bin:$PATH 42 | - | 43 | if [ `uname` = "Darwin" ] 44 | then 45 | travis_retry curl --insecure -L https://www.stackage.org/stack/osx-x86_64 | tar xz --strip-components=1 --include '*/stack' -C ~/.local/bin 46 | else 47 | travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' 48 | fi 49 | - set +ex 50 | 51 | script: 52 | - set -ex 53 | - stack --no-terminal --resolver $RESOLVER build --install-ghc --only-dependencies -j2 54 | - stack --no-terminal --resolver $RESOLVER test --pedantic --haddock --no-haddock-deps -j2 55 | - set +ex 56 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | This project adheres to [Haskell PVP](https://pvp.haskell.org/). 4 | 5 | The format of this changelog is based on 6 | [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 7 | 8 | 9 | ## 0.2.0.0 10 | ### Changed 11 | - Error reporting (Breaking change). 12 | All functions now adhere to the `Either` 13 | [conventions](http://hackage.haskell.org/package/base-4.11.1.0/docs/Data-Either.html): 14 | real errors are returned in a `Left`, 15 | successes are returned in a `Right`. 16 | The non-errors constructors of `PMError` 17 | (`NoError` and `GotData`) were moved to a new type `PMSuccess`. 18 | 19 | ### Added 20 | - The `poll` function which binds to 21 | [`Pm_Poll`](http://portmedia.sourceforge.net/portmidi/doxygen/group__grp__io.html#g344e6ba3edb14d560ccb074d66b56590). 22 | - The `readEventsToBuffer` function, to read events in a user-supplied buffer. 23 | - The `PMSuccess` type representing non-errors, with its associated 24 | functions `getText` and `getSuccessText`. 25 | 26 | 27 | ## 0.1.6.1 28 | 29 | commit 3b92597d84113969371612fb5c590b9f8c77d53c 30 | Author: Paul H. Liu 31 | Date: Wed Jul 6 09:34:40 2016 -0700 32 | 33 | Fix case sensitivity, and bump version to 0.1.6.1 to correct version number 34 | 35 | commit 2d5266dd325f2c248a9022679abfcebe9b317a95 36 | Author: Paul H. Liu 37 | Date: Tue Jul 5 21:37:24 2016 -0700 38 | 39 | Move to github, and bump version to 0.1.5.3 40 | 41 | commit d7946afcfd7c3204c1ea999ed7fdb9c4e9ff20a7 42 | Author: Paul Liu 43 | Date: Sun Apr 3 00:46:02 2016 -0700 44 | 45 | PMEvent uses CLong as message type to better handle SysEx. 46 | 47 | Ignore-this: 367f146403228813c914a23bfc2a1248 48 | - This is a breaking change. Use encodeMsg or decodeMsg to work with PMMsg. 49 | 50 | darcs-hash:20160403074602-da8e5-05c03013660b8fc91f843fa22143ec2b7ea5d7b5 51 | 52 | commit d5217416a5477f10a36727254425a572d18b7e4a 53 | Author: Paul Liu 54 | Date: Sun Apr 3 00:40:29 2016 -0700 55 | 56 | Use LANGUAGE pragma instead of OPTIONS_GHC 57 | 58 | Ignore-this: c65fb70875497b8e6c46f76ee9105dbe 59 | 60 | darcs-hash:20160403074029-da8e5-378ba491b94131265a94f115bf068971f869ea2b 61 | 62 | commit 358452adfcf91c2436752f9b95ebcc5d5124db97 63 | Author: Paul Liu 64 | Date: Sun Apr 3 00:40:09 2016 -0700 65 | 66 | Detect freebsd OS 67 | 68 | Ignore-this: 9f819a332d364679acc2e005488f9375 69 | 70 | darcs-hash:20160403074009-da8e5-16571867d53492c3fb7dc84df8fb8a500a71312a 71 | 72 | commit c64496ded49633100864e8e0e192a9333ef8caa5 73 | Author: Paul Liu 74 | Date: Mon Sep 21 22:17:09 2015 -0700 75 | 76 | Add CHANGELOG.txt to package, and bump version to 0.1.5.2 77 | 78 | Ignore-this: 465173236978f9557dbbc574b5d34a8f 79 | 80 | darcs-hash:20150922051709-da8e5-824c6e4c060e3c85ee3831cebde4a88fd3f8582b 81 | 82 | commit fda70ecb49f85fd4dccc5daaecbdd9f96fe1651f 83 | Author: Paul Liu 84 | Date: Mon Sep 21 11:29:09 2015 -0700 85 | 86 | Update README and bump version to 0.1.5.1 87 | 88 | Ignore-this: 98500cbf8d27a5068949cb458648500e 89 | 90 | darcs-hash:20150921182909-da8e5-fc100f8c68c20229a726f31ce8fd7051e8c13163 91 | 92 | commit 044b0f47f3fc3bc6ade39c2972fb4b40c16c4e05 93 | Author: Paul Liu 94 | Date: Mon Sep 21 11:17:37 2015 -0700 95 | 96 | Bump version to 0.1.5 97 | 98 | Ignore-this: 4fb2ae12ea9145bac0f0459ff3073169 99 | 100 | darcs-hash:20150921181737-da8e5-75d0be5001d877daf0c359687c66408afec0639b 101 | 102 | commit a158c93a9d50c8bc56a3be908effd4020f9e7cc0 103 | Author: Mark Lentczner 104 | Date: Mon Sep 21 11:16:04 2015 -0700 105 | 106 | On some systems, Int is not the same size as CInt. 107 | 108 | Ignore-this: 7bd6231264f14d66dd4c0156cc3266ba 109 | 110 | - Added hidden module Sound.PortMidi.DeviceInfo, moving DeviceInfo and peekDeviceInfo there 111 | - this allowed it to be a hsc2hs module, enabling peekDeviceInfo to use C generated structure offsets and sizes 112 | - FFI declarations for C calls that take and return PmError and PmDeviceID have been changed to take and return CInt rather than Int 113 | - Haskell functions interfacing with C calls have had fromIntegral calls added as needed 114 | - toPMError convenience function added, which replaced many calls to toEnum 115 | - FFI declaration for Pm_CountDevices added, countDevices became a Haskell function 116 | - PMError's toEnum function produces a more useful error message when the "impossible" happens 117 | 118 | darcs-hash:20150921181604-63162-de113c7c4da4f42d274b8b6c0641563c8670f606 119 | 120 | commit cfae744f937cf4dfafd9e03bce9e0088a5b375f5 121 | Author: Mark Lentczner 122 | Date: Mon Sep 21 11:15:00 2015 -0700 123 | 124 | Add strictness annotation when necessary 125 | 126 | Ignore-this: 9e5792f9ad04c797fcd51b244d7485fa 127 | 128 | darcs-hash:20150921181500-63162-dd522488d7f680cf536655d8cb1a878e489dcc98 129 | 130 | commit 4fb6f505b2f495bad6ddd1072ff41b51001d9b44 131 | Author: Paul Liu 132 | Date: Wed May 6 00:33:40 2015 -0700 133 | 134 | Update cabal file, and bump version to 0.1.4 135 | 136 | Ignore-this: 8b1d17f401ebd925a4bc6ff7778b453d 137 | 138 | darcs-hash:20150506073340-da8e5-0cf190250915842e35fc5f3177e576d84fb82026 139 | 140 | commit a164e464065c84f3f4e958c53271f930c097f2d3 141 | Author: Paul Liu 142 | Date: Wed May 6 00:33:10 2015 -0700 143 | 144 | Use withCAString for writeSysEx 145 | 146 | Ignore-this: 9499c201b5fb289cda27c090fdbf1541 147 | 148 | darcs-hash:20150506073310-da8e5-ecd2cf32c5f121cc95b97c1e9a495d189b0391cc 149 | 150 | commit c3ad563bbce9fbc25c8470865ded7b070d0d030e 151 | Author: Paul Liu 152 | Date: Wed Sep 16 11:06:11 2009 -0700 153 | 154 | remove conflicts in cabal file 155 | 156 | Ignore-this: 3ef1f4b655182e4cc5eb861e8f2427f1 157 | 158 | darcs-hash:20090916180611-da8e5-47cff7e32f38c7d54155784dbf2ddee6f1053faf 159 | 160 | commit 6b0963d50576c0572155e421a4e6d02c8c649d8c 161 | Author: Paul Liu 162 | Date: Wed Sep 16 11:01:59 2009 -0700 163 | 164 | fix compilation problem on OS X 165 | 166 | Ignore-this: a87d64d1c1a59bd161edecbde23932e3 167 | 168 | darcs-hash:20090916180159-da8e5-62407ea637a40e789f7e316a6fe181f56bc8fa1f 169 | 170 | commit 13c1d569f8566968bd0a214903d11c08a1101a2a 171 | Author: Paul Liu 172 | Date: Thu Jul 30 14:32:59 2009 -0700 173 | 174 | make cabal work for ghc 6.10.* on OS X 175 | 176 | darcs-hash:20090730213259-da8e5-f5a2558120fba824bb05440a8d7e3a2985fab2ed 177 | 178 | commit 8b7e2ea3c5ef6cb2b436e136964c2ee5814ffc02 179 | Author: Paul Liu 180 | Date: Thu Sep 4 18:59:02 2008 -0700 181 | 182 | PortMidi-0.1 initial release 183 | 184 | darcs-hash:20080905015902-da8e5-902a16abf5d28adda47ab45f6c0927a1042caf09 185 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008, Paul H. Liu 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 13 | -------------------------------------------------------------------------------- /PortMidi.cabal: -------------------------------------------------------------------------------- 1 | name: PortMidi 2 | version: 0.2.0.0 3 | Cabal-Version: >= 1.6 4 | build-type: Simple 5 | license: BSD3 6 | license-file: LICENSE 7 | maintainer: Paul H. Liu 8 | , Olivier Sohn 9 | homepage: http://github.com/PortMidi/PortMidi 10 | category: Sound 11 | stability: experimental 12 | synopsis: A binding for PortMedia/PortMidi 13 | description: A Haskell binding for PortMedia/PortMidi 14 | extra-source-files: 15 | README.txt 16 | CHANGELOG.md 17 | portmidi/pm_common/portmidi.h 18 | portmidi/pm_common/pmutil.h 19 | portmidi/pm_common/pminternal.h 20 | portmidi/pm_linux/pmlinux.h 21 | portmidi/pm_linux/pmlinuxalsa.h 22 | portmidi/pm_mac/pmmac.h 23 | portmidi/pm_mac/pmmacosxcm.h 24 | portmidi/pm_win/pmwinmm.h 25 | portmidi/porttime/porttime.h 26 | Library 27 | exposed-modules: Sound.PortMidi 28 | other-modules: Sound.PortMidi.DeviceInfo 29 | build-depends: base >= 4.8 && < 5 30 | extensions: CPP, ForeignFunctionInterface 31 | if os(linux) || os(freebsd) 32 | include-dirs: portmidi/pm_common portmidi/pm_linux portmidi/porttime 33 | cc-options: -DPMALSA 34 | c-sources: 35 | portmidi/pm_common/pmutil.c 36 | portmidi/pm_common/portmidi.c 37 | portmidi/pm_linux/pmlinux.c 38 | portmidi/pm_linux/pmlinuxalsa.c 39 | portmidi/porttime/porttime.c 40 | portmidi/porttime/ptlinux.c 41 | extra-libraries: asound 42 | else 43 | if os(darwin) 44 | include-dirs: portmidi/pm_common portmidi/pm_mac portmidi/porttime 45 | cc-options: -msse2 46 | c-sources: 47 | portmidi/pm_common/pmutil.c 48 | portmidi/pm_common/portmidi.c 49 | portmidi/pm_mac/pmmac.c 50 | portmidi/pm_mac/pmmacosxcm.c 51 | portmidi/porttime/porttime.c 52 | portmidi/porttime/ptmacosx_mach.c 53 | frameworks: CoreMIDI CoreFoundation CoreAudio 54 | else 55 | if os(mingw32) 56 | include-dirs: portmidi/pm_common portmidi/pm_win portmidi/porttime 57 | c-sources: 58 | portmidi/pm_common/pmutil.c 59 | portmidi/pm_common/portmidi.c 60 | portmidi/pm_win/pmwin.c 61 | portmidi/pm_win/pmwinmm.c 62 | portmidi/porttime/porttime.c 63 | portmidi/porttime/ptwinmm.c 64 | extra-libraries: winmm 65 | source-repository head 66 | type: git 67 | location: https://github.com/PortMidi/PortMidi 68 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | This is a Haskell module for PortMidi audio library, which supports 2 | real-time MIDI input and output. 3 | 4 | ========= 5 | ChangeLog 6 | ========= 7 | 8 | Please see CHANGELOG.md. 9 | 10 | ============ 11 | Installation 12 | ============ 13 | 14 | The usual cabal installation steps apply, either: 15 | 16 | runhaskell Setup.hs configure 17 | runhaskell Setup.hs build 18 | runhaskell Setup.hs install 19 | 20 | or simply: 21 | 22 | cabal install 23 | 24 | This will install a PortMidi package that contains a Sound.PortMidi module. 25 | 26 | ============================== 27 | Bug reports / Feature requests 28 | ============================== 29 | 30 | Bug reports and feature requests can be created here: 31 | https://github.com/PortMidi/PortMidi/issues. 32 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | module Main (main) where 2 | 3 | import Distribution.Simple 4 | 5 | main :: IO () 6 | main = defaultMain 7 | -------------------------------------------------------------------------------- /Sound/PortMidi.hs: -------------------------------------------------------------------------------- 1 | {- | 2 | Interface to PortMidi 3 | -} 4 | {-# LANGUAGE EmptyDataDecls #-} 5 | {-# LANGUAGE GeneralizedNewtypeDeriving #-} 6 | 7 | module Sound.PortMidi ( 8 | -- * Data Types 9 | PMError(..) 10 | , PMSuccess(..) 11 | , PMEventCount(..) 12 | , PMStream 13 | , DeviceInfo(..) 14 | , DeviceID 15 | , PMMsg(..) 16 | , PMEvent(..) 17 | -- * Constants 18 | , filterActive 19 | , filterSysex 20 | , filterClock 21 | , filterPlay 22 | , filterTick 23 | , filterFD 24 | , filterUndefined 25 | , filterReset 26 | , filterRealtime 27 | , filterNote 28 | , filterChannelAftertouch 29 | , filterPolyAftertouch 30 | , filterAftertouch 31 | , filterProgram 32 | , filterControl 33 | , filterPitchBend 34 | , filterMTC 35 | , filterSongPosition 36 | , filterSongSelect 37 | , filterTune 38 | , filterSystemCommon 39 | -- * PortMidi functions 40 | , initialize 41 | , terminate 42 | , hasHostError 43 | , getErrorText 44 | , getSuccessText 45 | , getText 46 | , countDevices 47 | , getDefaultInputDeviceID 48 | , getDefaultOutputDeviceID 49 | , getDeviceInfo 50 | , openInput 51 | , openOutput 52 | , setFilter 53 | , channel 54 | , setChannelMask 55 | , abort 56 | , close 57 | , poll 58 | , readEvents 59 | , readEventsToBuffer 60 | , writeEvents 61 | , writeShort 62 | , writeSysEx 63 | -- * Time function 64 | , time 65 | -- 66 | , encodeMsg 67 | , decodeMsg 68 | ) where 69 | 70 | 71 | import Foreign 72 | import Foreign.C 73 | 74 | import Sound.PortMidi.DeviceInfo 75 | 76 | -- | Represents non-errors of the C enum `PmError` 77 | data PMSuccess 78 | = NoError'NoData 79 | -- ^ Returned by 'poll' when there is no data, and returned by other functions 80 | -- when there is no error. 81 | | GotData 82 | -- ^ Only returned by 'poll' when data is available. 83 | deriving (Eq, Show) 84 | 85 | instance Enum PMSuccess where 86 | fromEnum NoError'NoData = 0 87 | fromEnum GotData = 1 88 | toEnum 0 = NoError'NoData 89 | toEnum 1 = GotData 90 | toEnum x = error $ "PortMidi: PMSuccess toEnum out of bound " ++ show x 91 | 92 | -- | Represents real errors of the C enum `PmError` 93 | data PMError 94 | = HostError 95 | | InvalidDeviceId 96 | | InsufficientMemory 97 | | BufferTooSmall 98 | | BufferOverflow 99 | | BadPtr 100 | | BadData 101 | | InternalError 102 | | BufferMaxSize 103 | deriving (Eq, Show) 104 | 105 | instance Enum PMError where 106 | fromEnum HostError = -10000 107 | fromEnum InvalidDeviceId = -9999 108 | fromEnum InsufficientMemory = -9998 109 | fromEnum BufferTooSmall = -9997 110 | fromEnum BufferOverflow = -9996 111 | fromEnum BadPtr = -9995 112 | fromEnum BadData = -9994 113 | fromEnum InternalError = -9993 114 | fromEnum BufferMaxSize = -9992 115 | toEnum (-10000) = HostError 116 | toEnum (-9999) = InvalidDeviceId 117 | toEnum (-9998) = InsufficientMemory 118 | toEnum (-9997) = BufferTooSmall 119 | toEnum (-9996) = BufferOverflow 120 | toEnum (-9995) = BadPtr 121 | toEnum (-9994) = BadData 122 | toEnum (-9993) = InternalError 123 | toEnum (-9992) = BufferMaxSize 124 | toEnum x = error $ "PortMidi: PMError toEnum out of bound " ++ show x 125 | 126 | eitherErrorOrSuccess :: CInt -> Either PMError PMSuccess 127 | eitherErrorOrSuccess n 128 | | isSuccess = Right $ toEnum $ fromIntegral n 129 | | otherwise = Left $ toEnum $ fromIntegral n 130 | where 131 | isSuccess = n == 0 || n == 1 132 | 133 | -- | Represents a count of 'PMEvent's 134 | newtype PMEventCount = PMEventCount CInt 135 | deriving(Num, Integral, Real, Enum, Show, Eq, Ord) 136 | 137 | -- | Interprets a 'CInt', as returned by 'pm_Read'. 138 | eitherErrorOrCount :: CInt -> Either PMError PMEventCount 139 | eitherErrorOrCount n 140 | | n >= 0 = Right $ fromIntegral n 141 | | otherwise = Left $ toEnum $ fromIntegral n 142 | 143 | data PortMidiStream 144 | type PMStreamPtr = Ptr PortMidiStream 145 | type PMStream = ForeignPtr PortMidiStream 146 | type DeviceID = Int 147 | 148 | (.<.) :: CLong -> Int -> CLong 149 | (.<.) = shiftL 150 | 151 | (.>.) :: CLong -> Int -> CLong 152 | (.>.) = shiftR 153 | 154 | filterActive, filterSysex, filterClock, filterPlay, filterTick, filterFD, filterUndefined, filterReset, filterRealtime, filterNote, filterChannelAftertouch, filterPolyAftertouch, filterAftertouch, filterProgram, filterControl, filterPitchBend, filterMTC, filterSongPosition, filterSongSelect, filterTune, filterSystemCommon :: CLong 155 | 156 | filterActive = 1 .<. 0x0e 157 | filterSysex = 1 .<. 0x00 158 | filterClock = 1 .<. 0x08 159 | filterPlay = (1 .<. 0x0A) .|. (1 .<. 0x0C) .|. (1 .<. 0x0B) 160 | filterTick = 1 .<. 0x09 161 | filterFD = 1 .<. 0x0D 162 | filterUndefined = filterFD 163 | filterReset = 1 .<. 0x0F 164 | filterRealtime = filterActive .|. filterSysex .|. filterClock .|. filterPlay .|. filterUndefined .|. filterReset .|. filterTick 165 | filterNote = (1 .<. 0x19) .|. (1 .<. 0x18) 166 | filterChannelAftertouch = 1 .<. 0x1D 167 | filterPolyAftertouch = 1 .<. 0x1A 168 | filterAftertouch = filterChannelAftertouch .|. filterPolyAftertouch 169 | filterProgram = 1 .<. 0x1C 170 | filterControl = 1 .<. 0x1B 171 | filterPitchBend = 1 .<. 0x1E 172 | filterMTC = 1 .<. 0x01 173 | filterSongPosition = 1 .<. 0x02 174 | filterSongSelect = 1 .<. 0x03 175 | filterTune = 1 .<. 0x06 176 | filterSystemCommon = filterMTC .|. filterSongPosition .|. filterSongSelect .|. filterTune 177 | 178 | data PMMsg 179 | = PMMsg 180 | { status :: {-# UNPACK #-} !CLong 181 | , data1 :: {-# UNPACK #-} !CLong 182 | , data2 :: {-# UNPACK #-} !CLong 183 | } deriving (Eq, Show) 184 | 185 | encodeMsg :: PMMsg -> CLong 186 | encodeMsg (PMMsg s d1 d2) = ((d2 .&. 0xFF) .<. 16) .|. ((d1 .&. 0xFF) .<. 8) .|. (s .&. 0xFF) 187 | decodeMsg :: CLong -> PMMsg 188 | decodeMsg i = PMMsg (i .&. 0xFF) ((i .>. 8) .&. 0xFF) ((i .>. 16) .&. 0xFF) 189 | 190 | -- | Time with millisecond precision. 191 | type Timestamp = CULong 192 | 193 | data PMEvent 194 | = PMEvent 195 | { message :: {-# UNPACK #-} !CLong 196 | , timestamp :: {-# UNPACK #-} !Timestamp 197 | } deriving (Eq, Show) 198 | 199 | instance Storable PMEvent where 200 | sizeOf _ = sizeOf (0::CLong) * 2 201 | alignment _ = alignment (0::CLong) 202 | peek ptr = do 203 | m <- peekByteOff ptr 0 204 | t <- peekByteOff ptr (sizeOf m) 205 | return $ PMEvent m t 206 | poke ptr (PMEvent m t) = do 207 | pokeByteOff ptr 0 m 208 | pokeByteOff ptr (sizeOf m) t 209 | 210 | 211 | foreign import ccall "portmidi.h Pm_Initialize" pm_Initialize :: IO CInt 212 | initialize :: IO (Either PMError PMSuccess) 213 | initialize = pm_Initialize >>= return . eitherErrorOrSuccess 214 | 215 | foreign import ccall "portmidi.h Pm_Terminate" pm_Terminate :: IO CInt 216 | terminate :: IO (Either PMError PMSuccess) 217 | terminate = pm_Terminate >>= return . eitherErrorOrSuccess 218 | 219 | foreign import ccall "portmidi.h Pm_HasHostError" pm_HasHostError :: PMStreamPtr -> IO CInt 220 | hasHostError :: PMStream -> IO Bool 221 | hasHostError = flip withForeignPtr (\stream -> pm_HasHostError stream >>= return . toEnum . fromIntegral) 222 | 223 | foreign import ccall "portmidi.h Pm_GetErrorText" pm_GetErrorText :: CInt -> IO CString 224 | getErrorText :: PMError -> IO String 225 | getErrorText err = pm_GetErrorText (fromIntegral $ fromEnum err) >>= peekCString 226 | 227 | getSuccessText :: PMSuccess -> IO String 228 | getSuccessText success = pm_GetErrorText (fromIntegral $ fromEnum success) >>= peekCString 229 | 230 | getText :: Either PMError PMSuccess -> IO String 231 | getText = either getErrorText getSuccessText 232 | 233 | foreign import ccall "portmidi.h Pm_CountDevices" pm_countDevices :: IO CInt 234 | countDevices :: IO DeviceID 235 | countDevices = pm_countDevices >>= return . fromIntegral 236 | 237 | foreign import ccall "portmidi.h Pm_GetDefaultInputDeviceID" pm_GetDefaultInputDeviceID :: IO CInt 238 | getDefaultInputDeviceID :: IO (Maybe DeviceID) 239 | getDefaultInputDeviceID = do 240 | i <- pm_GetDefaultInputDeviceID 241 | return $ if i == -1 then Nothing else Just (fromIntegral i) 242 | foreign import ccall "portmidi.h Pm_GetDefaultOutputDeviceID" pm_GetDefaultOutputDeviceID :: IO CInt 243 | getDefaultOutputDeviceID :: IO (Maybe DeviceID) 244 | getDefaultOutputDeviceID = do 245 | i <- pm_GetDefaultOutputDeviceID 246 | return $ if i == -1 then Nothing else Just (fromIntegral i) 247 | 248 | foreign import ccall "portmidi.h Pm_GetDeviceInfo" pm_GetDeviceInfo :: CInt -> IO (Ptr ()) 249 | getDeviceInfo :: DeviceID -> IO DeviceInfo 250 | getDeviceInfo deviceID = pm_GetDeviceInfo (fromIntegral deviceID) >>= peekDeviceInfo 251 | 252 | foreign import ccall "portmidi.h Pm_OpenInput" pm_OpenInput :: Ptr PMStreamPtr -> CInt -> Ptr () -> CLong -> Ptr () -> Ptr () -> IO CInt 253 | openInput :: DeviceID -> IO (Either PMError PMStream) 254 | openInput inputDevice = 255 | with nullPtr (\ptr -> 256 | eitherErrorOrSuccess <$> pm_OpenInput ptr (fromIntegral inputDevice) nullPtr 0 nullPtr nullPtr >>= either 257 | (return . Left) 258 | (\_ -> do 259 | stream <- peek ptr 260 | Right <$> newForeignPtr_ stream)) 261 | 262 | foreign import ccall "portmidi.h Pm_OpenOutput" pm_OpenOutput :: Ptr PMStreamPtr -> CInt -> Ptr () -> CLong -> Ptr () -> Ptr () -> CLong -> IO CInt 263 | openOutput :: DeviceID -> Int -> IO (Either PMError PMStream) 264 | openOutput outputDevice latency = 265 | with nullPtr (\ptr -> do 266 | eitherErrorOrSuccess <$> pm_OpenOutput ptr (fromIntegral outputDevice) nullPtr 0 nullPtr nullPtr (fromIntegral latency) >>= either 267 | (return . Left) 268 | (\_ -> do 269 | stream <- peek ptr 270 | Right <$> newForeignPtr_ stream)) 271 | 272 | foreign import ccall "portmidi.h Pm_SetFilter" pm_SetFilter :: PMStreamPtr -> CLong -> IO CInt 273 | setFilter :: PMStream -> CLong -> IO (Either PMError PMSuccess) 274 | setFilter stream theFilter = withForeignPtr stream (fmap eitherErrorOrSuccess . flip pm_SetFilter theFilter) 275 | 276 | channel :: Int -> CLong 277 | channel i = 1 .<. i 278 | 279 | foreign import ccall "portmidi.h Pm_SetChannelMask" pm_SetChannelMask :: PMStreamPtr -> CLong -> IO CInt 280 | setChannelMask :: PMStream -> CLong -> IO (Either PMError PMSuccess) 281 | setChannelMask stream mask = withForeignPtr stream (fmap eitherErrorOrSuccess . flip pm_SetChannelMask mask) 282 | 283 | foreign import ccall "portmidi.h Pm_Abort" pm_Abort :: PMStreamPtr -> IO CInt 284 | abort :: PMStream -> IO (Either PMError PMSuccess) 285 | abort = flip withForeignPtr (fmap eitherErrorOrSuccess . pm_Abort) 286 | 287 | foreign import ccall "portmidi.h Pm_Close" pm_Close :: PMStreamPtr -> IO CInt 288 | close :: PMStream -> IO (Either PMError PMSuccess) 289 | close = flip withForeignPtr (fmap eitherErrorOrSuccess . pm_Close) 290 | 291 | foreign import ccall "portmidi.h Pm_Poll" pm_Poll :: PMStreamPtr -> IO CInt 292 | -- | Returns wether or not a subsequent call to 'readEvents' would return 293 | -- some 'PMEvent's or not. 294 | poll :: PMStream -> IO (Either PMError PMSuccess) 295 | poll = flip withForeignPtr (fmap eitherErrorOrSuccess . pm_Poll) 296 | 297 | foreign import ccall "portmidi.h Pm_Read" pm_Read :: PMStreamPtr -> Ptr PMEvent -> CLong -> IO CInt 298 | -- | Reads at most 256 'PMEvent's, using a dynamically allocated buffer. 299 | readEvents :: PMStream -> IO (Either PMError [PMEvent]) 300 | readEvents stream = 301 | allocaArray (fromIntegral defaultBufferSize) $ \arr -> 302 | readEventsToBuffer stream arr defaultBufferSize >>= either 303 | (return . Left) 304 | (fmap Right . flip peekArray arr . fromIntegral) 305 | where 306 | defaultBufferSize = 256 307 | 308 | -- | Reads 'PMEvent's and writes them to the buffer passed as argument. 309 | readEventsToBuffer :: PMStream 310 | -> Ptr PMEvent 311 | -- ^ The 'PMEvent's buffer which will contain the results. 312 | -> CLong 313 | -- ^ The size of the 'PMEvent' buffer, in number of elements. 314 | -- No more that this number of 'PMEvent's can be read at once. 315 | -> IO (Either PMError PMEventCount) 316 | -- ^ When 'Right', returns the number of elements written 317 | -- to the 'PMEvent' buffer. 318 | readEventsToBuffer stream ptr sz = 319 | withForeignPtr stream $ \s -> 320 | eitherErrorOrCount <$> pm_Read s ptr sz 321 | 322 | foreign import ccall "portmidi.h Pm_Write" pm_Write :: PMStreamPtr -> Ptr PMEvent -> CLong -> IO CInt 323 | writeEvents :: PMStream -> [PMEvent] -> IO (Either PMError PMSuccess) 324 | writeEvents stream events = withForeignPtr stream (\s -> 325 | withArrayLen events (\len arr -> eitherErrorOrSuccess <$> pm_Write s arr (fromIntegral len))) 326 | 327 | foreign import ccall "portmidi.h Pm_WriteShort" pm_WriteShort :: PMStreamPtr -> CULong -> CLong -> IO CInt 328 | writeShort :: PMStream -> PMEvent -> IO (Either PMError PMSuccess) 329 | writeShort stream (PMEvent msg t) = withForeignPtr stream (\s -> 330 | eitherErrorOrSuccess <$> pm_WriteShort s t msg) 331 | 332 | foreign import ccall "portmidi.h Pm_WriteSysEx" pm_WriteSysEx :: PMStreamPtr -> CULong -> CString -> IO CInt 333 | writeSysEx :: PMStream -> Timestamp -> String -> IO (Either PMError PMSuccess) 334 | writeSysEx stream t str = withForeignPtr stream (\st -> 335 | withCAString str (\s -> eitherErrorOrSuccess <$> pm_WriteSysEx st t s)) 336 | 337 | foreign import ccall "porttime.h Pt_Time" time :: IO Timestamp 338 | -------------------------------------------------------------------------------- /Sound/PortMidi/DeviceInfo.hsc: -------------------------------------------------------------------------------- 1 | {-# OPTIONS_HADDOCK hide #-} 2 | 3 | module Sound.PortMidi.DeviceInfo ( 4 | DeviceInfo(..) 5 | , peekDeviceInfo 6 | ) where 7 | 8 | import Foreign 9 | import Foreign.C 10 | 11 | #include "portmidi.h" 12 | 13 | data DeviceInfo 14 | = DeviceInfo 15 | { interface :: String 16 | , name :: String 17 | , input :: Bool 18 | , output :: Bool 19 | , opened :: Bool 20 | } deriving (Eq, Show) 21 | 22 | peekDeviceInfo :: Ptr a -> IO DeviceInfo 23 | peekDeviceInfo ptr = do 24 | s <- #{peek PmDeviceInfo, interf} ptr >>= peekCString 25 | u <- #{peek PmDeviceInfo, name} ptr >>= peekCString 26 | i <- #{peek PmDeviceInfo, input} ptr 27 | o <- #{peek PmDeviceInfo, output} ptr 28 | d <- #{peek PmDeviceInfo, opened} ptr 29 | return $ DeviceInfo s u (asBool i) (asBool o) (asBool d) 30 | where 31 | asBool :: #{type int} -> Bool 32 | asBool = (/= 0) 33 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # based on 2 | # https://github.com/commercialhaskell/stack/blob/master/appveyor.yml 3 | 4 | # Disabled cache in hope of improving reliability of AppVeyor builds 5 | #cache: 6 | #- "c:\\sr" # stack root, short paths == fewer problems 7 | 8 | build: off 9 | 10 | environment: 11 | global: 12 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 13 | APPVEYOR_SAVE_CACHE_ON_ERROR: false 14 | STACK_ROOT: "c:\\sr" 15 | matrix: 16 | - RESOLVER: "lts-6.35" # ghc 7.10.3 17 | - RESOLVER: "lts-9.21" # ghc 8.0.2 18 | - RESOLVER: "lts-11.20" # ghc 8.2.2 19 | - RESOLVER: "lts-12.5" # ghc 8.4.3 20 | - RESOLVER: "lts-16.2" # ghc 8.8.3 21 | - RESOLVER: "nightly" 22 | 23 | before_test: 24 | # http://help.appveyor.com/discussions/problems/6312-curl-command-not-found 25 | - set PATH=C:\Program Files\Git\mingw64\bin;%PATH% 26 | - curl -sS -ostack.zip -L --insecure https://get.haskellstack.org/stable/windows-x86_64.zip 27 | - 7z x stack.zip stack.exe 28 | 29 | clone_folder: "c:\\stack" 30 | 31 | test_script: 32 | - echo "" | stack --resolver %RESOLVER% --no-terminal build --pedantic --jobs 1 33 | - echo "" | stack --resolver %RESOLVER% --no-terminal test --pedantic --jobs 1 34 | - echo "" | stack --resolver %RESOLVER% --no-terminal haddock --pedantic --jobs 1 35 | -------------------------------------------------------------------------------- /portmidi/license.txt: -------------------------------------------------------------------------------- 1 | /* 2 | * PortMidi Portable Real-Time MIDI Library 3 | * 4 | * license.txt -- a copy of the PortMidi copyright notice and license information 5 | * 6 | * Latest version available at: http://sourceforge.net/projects/portmedia 7 | * 8 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 9 | * Copyright (c) 2001-2009 Roger B. Dannenberg 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining 12 | * a copy of this software and associated documentation files 13 | * (the "Software"), to deal in the Software without restriction, 14 | * including without limitation the rights to use, copy, modify, merge, 15 | * publish, distribute, sublicense, and/or sell copies of the Software, 16 | * and to permit persons to whom the Software is furnished to do so, 17 | * subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be 20 | * included in all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 26 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 27 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 28 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | /* 32 | * The text above constitutes the entire PortMidi license; however, 33 | * the PortMusic community also makes the following non-binding requests: 34 | * 35 | * Any person wishing to distribute modifications to the Software is 36 | * requested to send the modifications to the original developer so that 37 | * they can be incorporated into the canonical version. It is also 38 | * requested that these non-binding requests be included along with the 39 | * license above. 40 | */ 41 | -------------------------------------------------------------------------------- /portmidi/pm_common/pminternal.h: -------------------------------------------------------------------------------- 1 | /* pminternal.h -- header for interface implementations */ 2 | 3 | /* this file is included by files that implement library internals */ 4 | /* Here is a guide to implementers: 5 | provide an initialization function similar to pm_winmm_init() 6 | add your initialization function to pm_init() 7 | Note that your init function should never require not-standard 8 | libraries or fail in any way. If the interface is not available, 9 | simply do not call pm_add_device. This means that non-standard 10 | libraries should try to do dynamic linking at runtime using a DLL 11 | and return without error if the DLL cannot be found or if there 12 | is any other failure. 13 | implement functions as indicated in pm_fns_type to open, read, write, 14 | close, etc. 15 | call pm_add_device() for each input and output device, passing it a 16 | pm_fns_type structure. 17 | assumptions about pm_fns_type functions are given below. 18 | */ 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | /* these are defined in system-specific file */ 25 | void *pm_alloc(size_t s); 26 | void pm_free(void *ptr); 27 | 28 | /* if an error occurs while opening or closing a midi stream, set these: */ 29 | extern int pm_hosterror; 30 | extern char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; 31 | 32 | struct pm_internal_struct; 33 | 34 | /* these do not use PmInternal because it is not defined yet... */ 35 | typedef PmError (*pm_write_short_fn)(struct pm_internal_struct *midi, 36 | PmEvent *buffer); 37 | typedef PmError (*pm_begin_sysex_fn)(struct pm_internal_struct *midi, 38 | PmTimestamp timestamp); 39 | typedef PmError (*pm_end_sysex_fn)(struct pm_internal_struct *midi, 40 | PmTimestamp timestamp); 41 | typedef PmError (*pm_write_byte_fn)(struct pm_internal_struct *midi, 42 | unsigned char byte, PmTimestamp timestamp); 43 | typedef PmError (*pm_write_realtime_fn)(struct pm_internal_struct *midi, 44 | PmEvent *buffer); 45 | typedef PmError (*pm_write_flush_fn)(struct pm_internal_struct *midi, 46 | PmTimestamp timestamp); 47 | typedef PmTimestamp (*pm_synchronize_fn)(struct pm_internal_struct *midi); 48 | /* pm_open_fn should clean up all memory and close the device if any part 49 | of the open fails */ 50 | typedef PmError (*pm_open_fn)(struct pm_internal_struct *midi, 51 | void *driverInfo); 52 | typedef PmError (*pm_abort_fn)(struct pm_internal_struct *midi); 53 | /* pm_close_fn should clean up all memory and close the device if any 54 | part of the close fails. */ 55 | typedef PmError (*pm_close_fn)(struct pm_internal_struct *midi); 56 | typedef PmError (*pm_poll_fn)(struct pm_internal_struct *midi); 57 | typedef void (*pm_host_error_fn)(struct pm_internal_struct *midi, char * msg, 58 | unsigned int len); 59 | typedef unsigned int (*pm_has_host_error_fn)(struct pm_internal_struct *midi); 60 | 61 | typedef struct { 62 | pm_write_short_fn write_short; /* output short MIDI msg */ 63 | pm_begin_sysex_fn begin_sysex; /* prepare to send a sysex message */ 64 | pm_end_sysex_fn end_sysex; /* marks end of sysex message */ 65 | pm_write_byte_fn write_byte; /* accumulate one more sysex byte */ 66 | pm_write_realtime_fn write_realtime; /* send real-time message within sysex */ 67 | pm_write_flush_fn write_flush; /* send any accumulated but unsent data */ 68 | pm_synchronize_fn synchronize; /* synchronize portmidi time to stream time */ 69 | pm_open_fn open; /* open MIDI device */ 70 | pm_abort_fn abort; /* abort */ 71 | pm_close_fn close; /* close device */ 72 | pm_poll_fn poll; /* read pending midi events into portmidi buffer */ 73 | pm_has_host_error_fn has_host_error; /* true when device has had host 74 | error message */ 75 | pm_host_error_fn host_error; /* provide text readable host error message 76 | for device (clears and resets) */ 77 | } pm_fns_node, *pm_fns_type; 78 | 79 | 80 | /* when open fails, the dictionary gets this set of functions: */ 81 | extern pm_fns_node pm_none_dictionary; 82 | 83 | typedef struct { 84 | PmDeviceInfo pub; /* some portmidi state also saved in here (for autmatic 85 | device closing (see PmDeviceInfo struct) */ 86 | void *descriptor; /* ID number passed to win32 multimedia API open */ 87 | void *internalDescriptor; /* points to PmInternal device, allows automatic 88 | device closing */ 89 | pm_fns_type dictionary; 90 | } descriptor_node, *descriptor_type; 91 | 92 | extern int pm_descriptor_max; 93 | extern descriptor_type descriptors; 94 | extern int pm_descriptor_index; 95 | 96 | typedef unsigned long (*time_get_proc_type)(void *time_info); 97 | 98 | typedef struct pm_internal_struct { 99 | int device_id; /* which device is open (index to descriptors) */ 100 | short write_flag; /* MIDI_IN, or MIDI_OUT */ 101 | 102 | PmTimeProcPtr time_proc; /* where to get the time */ 103 | void *time_info; /* pass this to get_time() */ 104 | long buffer_len; /* how big is the buffer or queue? */ 105 | PmQueue *queue; 106 | 107 | long latency; /* time delay in ms between timestamps and actual output */ 108 | /* set to zero to get immediate, simple blocking output */ 109 | /* if latency is zero, timestamps will be ignored; */ 110 | /* if midi input device, this field ignored */ 111 | 112 | int sysex_in_progress; /* when sysex status is seen, this flag becomes 113 | * true until EOX is seen. When true, new data is appended to the 114 | * stream of outgoing bytes. When overflow occurs, sysex data is 115 | * dropped (until an EOX or non-real-timei status byte is seen) so 116 | * that, if the overflow condition is cleared, we don't start 117 | * sending data from the middle of a sysex message. If a sysex 118 | * message is filtered, sysex_in_progress is false, causing the 119 | * message to be dropped. */ 120 | PmMessage sysex_message; /* buffer for 4 bytes of sysex data */ 121 | int sysex_message_count; /* how many bytes in sysex_message so far */ 122 | 123 | long filters; /* flags that filter incoming message classes */ 124 | int channel_mask; /* flter incoming messages based on channel */ 125 | PmTimestamp last_msg_time; /* timestamp of last message */ 126 | PmTimestamp sync_time; /* time of last synchronization */ 127 | PmTimestamp now; /* set by PmWrite to current time */ 128 | int first_message; /* initially true, used to run first synchronization */ 129 | pm_fns_type dictionary; /* implementation functions */ 130 | void *descriptor; /* system-dependent state */ 131 | /* the following are used to expedite sysex data */ 132 | /* on windows, in debug mode, based on some profiling, these optimizations 133 | * cut the time to process sysex bytes from about 7.5 to 0.26 usec/byte, 134 | * but this does not count time in the driver, so I don't know if it is 135 | * important 136 | */ 137 | unsigned char *fill_base; /* addr of ptr to sysex data */ 138 | unsigned long *fill_offset_ptr; /* offset of next sysex byte */ 139 | int fill_length; /* how many sysex bytes to write */ 140 | } PmInternal; 141 | 142 | 143 | /* defined by system specific implementation, e.g. pmwinmm, used by PortMidi */ 144 | void pm_init(void); 145 | void pm_term(void); 146 | 147 | /* defined by portMidi, used by pmwinmm */ 148 | PmError none_write_short(PmInternal *midi, PmEvent *buffer); 149 | PmError none_write_byte(PmInternal *midi, unsigned char byte, 150 | PmTimestamp timestamp); 151 | PmTimestamp none_synchronize(PmInternal *midi); 152 | 153 | PmError pm_fail_fn(PmInternal *midi); 154 | PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp); 155 | PmError pm_success_fn(PmInternal *midi); 156 | PmError pm_add_device(char *interf, char *name, int input, void *descriptor, 157 | pm_fns_type dictionary); 158 | unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data, int len, 159 | PmTimestamp timestamp); 160 | void pm_read_short(PmInternal *midi, PmEvent *event); 161 | 162 | #define none_write_flush pm_fail_timestamp_fn 163 | #define none_sysex pm_fail_timestamp_fn 164 | #define none_poll pm_fail_fn 165 | #define success_poll pm_success_fn 166 | 167 | #define MIDI_REALTIME_MASK 0xf8 168 | #define is_real_time(msg) \ 169 | ((Pm_MessageStatus(msg) & MIDI_REALTIME_MASK) == MIDI_REALTIME_MASK) 170 | 171 | #ifdef __cplusplus 172 | } 173 | #endif 174 | 175 | -------------------------------------------------------------------------------- /portmidi/pm_common/pmutil.c: -------------------------------------------------------------------------------- 1 | /* pmutil.c -- some helpful utilities for building midi 2 | applications that use PortMidi 3 | */ 4 | #include 5 | #include 6 | #include 7 | #include "portmidi.h" 8 | #include "pmutil.h" 9 | #include "pminternal.h" 10 | 11 | #ifdef WIN32 12 | #define bzero(addr, siz) memset(addr, 0, siz) 13 | #endif 14 | 15 | // #define QUEUE_DEBUG 1 16 | #ifdef QUEUE_DEBUG 17 | #include "stdio.h" 18 | #endif 19 | 20 | /* code is based on 4-byte words -- it should work on a 64-bit machine 21 | as long as a "long" has 4 bytes. This code could be generalized to 22 | be independent of the size of "long" */ 23 | 24 | typedef long int32; 25 | 26 | typedef struct { 27 | long head; 28 | long tail; 29 | long len; 30 | long msg_size; /* number of int32 in a message including extra word */ 31 | long overflow; 32 | long peek_overflow; 33 | int32 *buffer; 34 | int32 *peek; 35 | int peek_flag; 36 | } PmQueueRep; 37 | 38 | 39 | PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg) 40 | { 41 | int int32s_per_msg = ((bytes_per_msg + sizeof(int32) - 1) & 42 | ~(sizeof(int32) - 1)) / sizeof(int32); 43 | PmQueueRep *queue = (PmQueueRep *) pm_alloc(sizeof(PmQueueRep)); 44 | if (!queue) /* memory allocation failed */ 45 | return NULL; 46 | 47 | /* need extra word per message for non-zero encoding */ 48 | queue->len = num_msgs * (int32s_per_msg + 1); 49 | queue->buffer = (int32 *) pm_alloc(queue->len * sizeof(int32)); 50 | bzero(queue->buffer, queue->len * sizeof(int32)); 51 | if (!queue->buffer) { 52 | pm_free(queue); 53 | return NULL; 54 | } else { /* allocate the "peek" buffer */ 55 | queue->peek = (int32 *) pm_alloc(int32s_per_msg * sizeof(int32)); 56 | if (!queue->peek) { 57 | /* free everything allocated so far and return */ 58 | pm_free(queue->buffer); 59 | pm_free(queue); 60 | return NULL; 61 | } 62 | } 63 | bzero(queue->buffer, queue->len * sizeof(int32)); 64 | queue->head = 0; 65 | queue->tail = 0; 66 | /* msg_size is in words */ 67 | queue->msg_size = int32s_per_msg + 1; /* note extra word is counted */ 68 | queue->overflow = FALSE; 69 | queue->peek_overflow = FALSE; 70 | queue->peek_flag = FALSE; 71 | return queue; 72 | } 73 | 74 | 75 | PmError Pm_QueueDestroy(PmQueue *q) 76 | { 77 | PmQueueRep *queue = (PmQueueRep *) q; 78 | 79 | /* arg checking */ 80 | if (!queue || !queue->buffer || !queue->peek) 81 | return pmBadPtr; 82 | 83 | pm_free(queue->peek); 84 | pm_free(queue->buffer); 85 | pm_free(queue); 86 | return pmNoError; 87 | } 88 | 89 | 90 | PmError Pm_Dequeue(PmQueue *q, void *msg) 91 | { 92 | long head; 93 | PmQueueRep *queue = (PmQueueRep *) q; 94 | int i; 95 | int32 *msg_as_int32 = (int32 *) msg; 96 | 97 | /* arg checking */ 98 | if (!queue) 99 | return pmBadPtr; 100 | /* a previous peek operation encountered an overflow, but the overflow 101 | * has not yet been reported to client, so do it now. No message is 102 | * returned, but on the next call, we will return the peek buffer. 103 | */ 104 | if (queue->peek_overflow) { 105 | queue->peek_overflow = FALSE; 106 | return pmBufferOverflow; 107 | } 108 | if (queue->peek_flag) { 109 | memcpy(msg, queue->peek, (queue->msg_size - 1) * sizeof(int32)); 110 | queue->peek_flag = FALSE; 111 | return pmGotData; 112 | } 113 | 114 | head = queue->head; 115 | /* if writer overflows, it writes queue->overflow = tail+1 so that 116 | * when the reader gets to that position in the buffer, it can 117 | * return the overflow condition to the reader. The problem is that 118 | * at overflow, things have wrapped around, so tail == head, and the 119 | * reader will detect overflow immediately instead of waiting until 120 | * it reads everything in the buffer, wrapping around again to the 121 | * point where tail == head. So the condition also checks that 122 | * queue->buffer[head] is zero -- if so, then the buffer is now 123 | * empty, and we're at the point in the msg stream where overflow 124 | * occurred. It's time to signal overflow to the reader. If 125 | * queue->buffer[head] is non-zero, there's a message there and we 126 | * should read all the way around the buffer before signalling overflow. 127 | * There is a write-order dependency here, but to fail, the overflow 128 | * field would have to be written while an entire buffer full of 129 | * writes are still pending. I'm assuming out-of-order writes are 130 | * possible, but not that many. 131 | */ 132 | if (queue->overflow == head + 1 && !queue->buffer[head]) { 133 | queue->overflow = 0; /* non-overflow condition */ 134 | return pmBufferOverflow; 135 | } 136 | 137 | /* test to see if there is data in the queue -- test from back 138 | * to front so if writer is simultaneously writing, we don't 139 | * waste time discovering the write is not finished 140 | */ 141 | for (i = queue->msg_size - 1; i >= 0; i--) { 142 | if (!queue->buffer[head + i]) { 143 | return pmNoData; 144 | } 145 | } 146 | memcpy(msg, (char *) &queue->buffer[head + 1], 147 | sizeof(int32) * (queue->msg_size - 1)); 148 | /* fix up zeros */ 149 | i = queue->buffer[head]; 150 | while (i < queue->msg_size) { 151 | int32 j; 152 | i--; /* msg does not have extra word so shift down */ 153 | j = msg_as_int32[i]; 154 | msg_as_int32[i] = 0; 155 | i = j; 156 | } 157 | /* signal that data has been removed by zeroing: */ 158 | bzero((char *) &queue->buffer[head], sizeof(int32) * queue->msg_size); 159 | 160 | /* update head */ 161 | head += queue->msg_size; 162 | if (head == queue->len) head = 0; 163 | queue->head = head; 164 | return pmGotData; /* success */ 165 | } 166 | 167 | 168 | 169 | PmError Pm_SetOverflow(PmQueue *q) 170 | { 171 | PmQueueRep *queue = (PmQueueRep *) q; 172 | long tail; 173 | /* arg checking */ 174 | if (!queue) 175 | return pmBadPtr; 176 | /* no more enqueue until receiver acknowledges overflow */ 177 | if (queue->overflow) return pmBufferOverflow; 178 | tail = queue->tail; 179 | queue->overflow = tail + 1; 180 | return pmBufferOverflow; 181 | } 182 | 183 | 184 | PmError Pm_Enqueue(PmQueue *q, void *msg) 185 | { 186 | PmQueueRep *queue = (PmQueueRep *) q; 187 | long tail; 188 | int i; 189 | int32 *src = (int32 *) msg; 190 | int32 *ptr; 191 | int32 *dest; 192 | int rslt; 193 | if (!queue) 194 | return pmBadPtr; 195 | /* no more enqueue until receiver acknowledges overflow */ 196 | if (queue->overflow) return pmBufferOverflow; 197 | rslt = Pm_QueueFull(q); 198 | /* already checked above: if (rslt == pmBadPtr) return rslt; */ 199 | tail = queue->tail; 200 | if (rslt) { 201 | queue->overflow = tail + 1; 202 | return pmBufferOverflow; 203 | } 204 | 205 | /* queue is has room for message, and overflow flag is cleared */ 206 | ptr = &queue->buffer[tail]; 207 | dest = ptr + 1; 208 | for (i = 1; i < queue->msg_size; i++) { 209 | int32 j = src[i - 1]; 210 | if (!j) { 211 | *ptr = i; 212 | ptr = dest; 213 | } else { 214 | *dest = j; 215 | } 216 | dest++; 217 | } 218 | *ptr = i; 219 | tail += queue->msg_size; 220 | if (tail == queue->len) tail = 0; 221 | queue->tail = tail; 222 | return pmNoError; 223 | } 224 | 225 | 226 | int Pm_QueueEmpty(PmQueue *q) 227 | { 228 | PmQueueRep *queue = (PmQueueRep *) q; 229 | return (!queue) || /* null pointer -> return "empty" */ 230 | (queue->buffer[queue->head] == 0 && !queue->peek_flag); 231 | } 232 | 233 | 234 | int Pm_QueueFull(PmQueue *q) 235 | { 236 | int tail; 237 | int i; 238 | PmQueueRep *queue = (PmQueueRep *) q; 239 | /* arg checking */ 240 | if (!queue) 241 | return pmBadPtr; 242 | tail = queue->tail; 243 | /* test to see if there is space in the queue */ 244 | for (i = 0; i < queue->msg_size; i++) { 245 | if (queue->buffer[tail + i]) { 246 | return TRUE; 247 | } 248 | } 249 | return FALSE; 250 | } 251 | 252 | 253 | void *Pm_QueuePeek(PmQueue *q) 254 | { 255 | PmError rslt; 256 | long temp; 257 | PmQueueRep *queue = (PmQueueRep *) q; 258 | /* arg checking */ 259 | if (!queue) 260 | return NULL; 261 | 262 | if (queue->peek_flag) { 263 | return queue->peek; 264 | } 265 | /* this is ugly: if peek_overflow is set, then Pm_Dequeue() 266 | * returns immediately with pmBufferOverflow, but here, we 267 | * want Pm_Dequeue() to really check for data. If data is 268 | * there, we can return it 269 | */ 270 | temp = queue->peek_overflow; 271 | queue->peek_overflow = FALSE; 272 | rslt = Pm_Dequeue(q, queue->peek); 273 | queue->peek_overflow = temp; 274 | 275 | if (rslt == 1) { 276 | queue->peek_flag = TRUE; 277 | return queue->peek; 278 | } else if (rslt == pmBufferOverflow) { 279 | /* when overflow is indicated, the queue is empty and the 280 | * first message that was dropped by Enqueue (signalling 281 | * pmBufferOverflow to its caller) would have been the next 282 | * message in the queue. Pm_QueuePeek will return NULL, but 283 | * remember that an overflow occurred. (see Pm_Dequeue) 284 | */ 285 | queue->peek_overflow = TRUE; 286 | } 287 | return NULL; 288 | } 289 | 290 | -------------------------------------------------------------------------------- /portmidi/pm_common/pmutil.h: -------------------------------------------------------------------------------- 1 | /* pmutil.h -- some helpful utilities for building midi 2 | applications that use PortMidi 3 | */ 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif /* __cplusplus */ 8 | 9 | typedef void PmQueue; 10 | 11 | /* 12 | A single-reader, single-writer queue is created by 13 | Pm_QueueCreate(), which takes the number of messages and 14 | the message size as parameters. The queue only accepts 15 | fixed sized messages. Returns NULL if memory cannot be allocated. 16 | 17 | This queue implementation uses the "light pipe" algorithm which 18 | operates correctly even with multi-processors and out-of-order 19 | memory writes. (see Alexander Dokumentov, "Lock-free Interprocess 20 | Communication," Dr. Dobbs Portal, http://www.ddj.com/, 21 | articleID=189401457, June 15, 2006. This algorithm requires 22 | that messages be translated to a form where no words contain 23 | zeros. Each word becomes its own "data valid" tag. Because of 24 | this translation, we cannot return a pointer to data still in 25 | the queue when the "peek" method is called. Instead, a buffer 26 | is preallocated so that data can be copied there. Pm_QueuePeek() 27 | dequeues a message into this buffer and returns a pointer to 28 | it. A subsequent Pm_Dequeue() will copy from this buffer. 29 | 30 | This implementation does not try to keep reader/writer data in 31 | separate cache lines or prevent thrashing on cache lines. 32 | However, this algorithm differs by doing inserts/removals in 33 | units of messages rather than units of machine words. Some 34 | performance improvement might be obtained by not clearing data 35 | immediately after a read, but instead by waiting for the end 36 | of the cache line, especially if messages are smaller than 37 | cache lines. See the Dokumentov article for explanation. 38 | 39 | The algorithm is extended to handle "overflow" reporting. To report 40 | an overflow, the sender writes the current tail position to a field. 41 | The receiver must acknowlege receipt by zeroing the field. The sender 42 | will not send more until the field is zeroed. 43 | 44 | Pm_QueueDestroy() destroys the queue and frees its storage. 45 | */ 46 | 47 | PmQueue *Pm_QueueCreate(long num_msgs, long bytes_per_msg); 48 | PmError Pm_QueueDestroy(PmQueue *queue); 49 | 50 | /* 51 | Pm_Dequeue() removes one item from the queue, copying it into msg. 52 | Returns 1 if successful, and 0 if the queue is empty. 53 | Returns pmBufferOverflow if what would have been the next thing 54 | in the queue was dropped due to overflow. (So when overflow occurs, 55 | the receiver can receive a queue full of messages before getting the 56 | overflow report. This protocol ensures that the reader will be 57 | notified when data is lost due to overflow. 58 | */ 59 | PmError Pm_Dequeue(PmQueue *queue, void *msg); 60 | 61 | 62 | /* 63 | Pm_Enqueue() inserts one item into the queue, copying it from msg. 64 | Returns pmNoError if successful and pmBufferOverflow if the queue was 65 | already full. If pmBufferOverflow is returned, the overflow flag is set. 66 | */ 67 | PmError Pm_Enqueue(PmQueue *queue, void *msg); 68 | 69 | 70 | /* 71 | Pm_QueueFull() returns non-zero if the queue is full 72 | Pm_QueueEmpty() returns non-zero if the queue is empty 73 | 74 | Either condition may change immediately because a parallel 75 | enqueue or dequeue operation could be in progress. Furthermore, 76 | Pm_QueueEmpty() is optimistic: it may say false, when due to 77 | out-of-order writes, the full message has not arrived. Therefore, 78 | Pm_Dequeue() could still return 0 after Pm_QueueEmpty() returns 79 | false. On the other hand, Pm_QueueFull() is pessimistic: if it 80 | returns false, then Pm_Enqueue() is guaranteed to succeed. 81 | 82 | Error conditions: Pm_QueueFull() returns pmBadPtr if queue is NULL. 83 | Pm_QueueEmpty() returns FALSE if queue is NULL. 84 | */ 85 | int Pm_QueueFull(PmQueue *queue); 86 | int Pm_QueueEmpty(PmQueue *queue); 87 | 88 | 89 | /* 90 | Pm_QueuePeek() returns a pointer to the item at the head of the queue, 91 | or NULL if the queue is empty. The item is not removed from the queue. 92 | Pm_QueuePeek() will not indicate when an overflow occurs. If you want 93 | to get and check pmBufferOverflow messages, use the return value of 94 | Pm_QueuePeek() *only* as an indication that you should call 95 | Pm_Dequeue(). At the point where a direct call to Pm_Dequeue() would 96 | return pmBufferOverflow, Pm_QueuePeek() will return NULL but internally 97 | clear the pmBufferOverflow flag, enabling Pm_Enqueue() to resume 98 | enqueuing messages. A subsequent call to Pm_QueuePeek() 99 | will return a pointer to the first message *after* the overflow. 100 | Using this as an indication to call Pm_Dequeue(), the first call 101 | to Pm_Dequeue() will return pmBufferOverflow. The second call will 102 | return success, copying the same message pointed to by the previous 103 | Pm_QueuePeek(). 104 | 105 | When to use Pm_QueuePeek(): (1) when you need to look at the message 106 | data to decide who should be called to receive it. (2) when you need 107 | to know a message is ready but cannot accept the message. 108 | 109 | Note that Pm_QueuePeek() is not a fast check, so if possible, you 110 | might as well just call Pm_Dequeue() and accept the data if it is there. 111 | */ 112 | void *Pm_QueuePeek(PmQueue *queue); 113 | 114 | /* 115 | Pm_SetOverflow() allows the writer (enqueuer) to signal an overflow 116 | condition to the reader (dequeuer). E.g. when transfering data from 117 | the OS to an application, if the OS indicates a buffer overrun, 118 | Pm_SetOverflow() can be used to insure that the reader receives a 119 | pmBufferOverflow result from Pm_Dequeue(). Returns pmBadPtr if queue 120 | is NULL, returns pmBufferOverflow if buffer is already in an overflow 121 | state, returns pmNoError if successfully set overflow state. 122 | */ 123 | PmError Pm_SetOverflow(PmQueue *queue); 124 | 125 | #ifdef __cplusplus 126 | } 127 | #endif /* __cplusplus */ 128 | -------------------------------------------------------------------------------- /portmidi/pm_common/portmidi.c: -------------------------------------------------------------------------------- 1 | #include "stdlib.h" 2 | #include "string.h" 3 | #include "portmidi.h" 4 | #include "porttime.h" 5 | #include "pmutil.h" 6 | #include "pminternal.h" 7 | #include 8 | 9 | #define MIDI_CLOCK 0xf8 10 | #define MIDI_ACTIVE 0xfe 11 | #define MIDI_STATUS_MASK 0x80 12 | #define MIDI_SYSEX 0xf0 13 | #define MIDI_EOX 0xf7 14 | #define MIDI_START 0xFA 15 | #define MIDI_STOP 0xFC 16 | #define MIDI_CONTINUE 0xFB 17 | #define MIDI_F9 0xF9 18 | #define MIDI_FD 0xFD 19 | #define MIDI_RESET 0xFF 20 | #define MIDI_NOTE_ON 0x90 21 | #define MIDI_NOTE_OFF 0x80 22 | #define MIDI_CHANNEL_AT 0xD0 23 | #define MIDI_POLY_AT 0xA0 24 | #define MIDI_PROGRAM 0xC0 25 | #define MIDI_CONTROL 0xB0 26 | #define MIDI_PITCHBEND 0xE0 27 | #define MIDI_MTC 0xF1 28 | #define MIDI_SONGPOS 0xF2 29 | #define MIDI_SONGSEL 0xF3 30 | #define MIDI_TUNE 0xF6 31 | 32 | #define is_empty(midi) ((midi)->tail == (midi)->head) 33 | 34 | static int pm_initialized = FALSE; 35 | int pm_hosterror; 36 | char pm_hosterror_text[PM_HOST_ERROR_MSG_LEN]; 37 | 38 | #ifdef PM_CHECK_ERRORS 39 | 40 | #include 41 | 42 | #define STRING_MAX 80 43 | 44 | static void prompt_and_exit(void) 45 | { 46 | char line[STRING_MAX]; 47 | printf("type ENTER..."); 48 | fgets(line, STRING_MAX, stdin); 49 | /* this will clean up open ports: */ 50 | exit(-1); 51 | } 52 | 53 | 54 | static PmError pm_errmsg(PmError err) 55 | { 56 | if (err == pmHostError) { 57 | /* it seems pointless to allocate memory and copy the string, 58 | * so I will do the work of Pm_GetHostErrorText directly 59 | */ 60 | printf("PortMidi found host error...\n %s\n", pm_hosterror_text); 61 | pm_hosterror = FALSE; 62 | pm_hosterror_text[0] = 0; /* clear the message */ 63 | prompt_and_exit(); 64 | } else if (err < 0) { 65 | printf("PortMidi call failed...\n %s\n", Pm_GetErrorText(err)); 66 | prompt_and_exit(); 67 | } 68 | return err; 69 | } 70 | #else 71 | #define pm_errmsg(err) err 72 | #endif 73 | 74 | /* 75 | ==================================================================== 76 | system implementation of portmidi interface 77 | ==================================================================== 78 | */ 79 | 80 | int pm_descriptor_max = 0; 81 | int pm_descriptor_index = 0; 82 | descriptor_type descriptors = NULL; 83 | 84 | /* pm_add_device -- describe interface/device pair to library 85 | * 86 | * This is called at intialization time, once for each 87 | * interface (e.g. DirectSound) and device (e.g. SoundBlaster 1) 88 | * The strings are retained but NOT COPIED, so do not destroy them! 89 | * 90 | * returns pmInvalidDeviceId if device memory is exceeded 91 | * otherwise returns pmNoError 92 | */ 93 | PmError pm_add_device(char *interf, char *name, int input, 94 | void *descriptor, pm_fns_type dictionary) { 95 | if (pm_descriptor_index >= pm_descriptor_max) { 96 | // expand descriptors 97 | descriptor_type new_descriptors = (descriptor_type) 98 | pm_alloc(sizeof(descriptor_node) * (pm_descriptor_max + 32)); 99 | if (!new_descriptors) return pmInsufficientMemory; 100 | if (descriptors) { 101 | memcpy(new_descriptors, descriptors, 102 | sizeof(descriptor_node) * pm_descriptor_max); 103 | free(descriptors); 104 | } 105 | pm_descriptor_max += 32; 106 | descriptors = new_descriptors; 107 | } 108 | descriptors[pm_descriptor_index].pub.interf = interf; 109 | descriptors[pm_descriptor_index].pub.name = name; 110 | descriptors[pm_descriptor_index].pub.input = input; 111 | descriptors[pm_descriptor_index].pub.output = !input; 112 | 113 | /* default state: nothing to close (for automatic device closing) */ 114 | descriptors[pm_descriptor_index].pub.opened = FALSE; 115 | 116 | /* ID number passed to win32 multimedia API open */ 117 | descriptors[pm_descriptor_index].descriptor = descriptor; 118 | 119 | /* points to PmInternal, allows automatic device closing */ 120 | descriptors[pm_descriptor_index].internalDescriptor = NULL; 121 | 122 | descriptors[pm_descriptor_index].dictionary = dictionary; 123 | 124 | pm_descriptor_index++; 125 | 126 | return pmNoError; 127 | } 128 | 129 | 130 | /* 131 | ==================================================================== 132 | portmidi implementation 133 | ==================================================================== 134 | */ 135 | 136 | int Pm_CountDevices( void ) { 137 | Pm_Initialize(); 138 | /* no error checking -- Pm_Initialize() does not fail */ 139 | return pm_descriptor_index; 140 | } 141 | 142 | 143 | const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ) { 144 | Pm_Initialize(); /* no error check needed */ 145 | if (id >= 0 && id < pm_descriptor_index) { 146 | return &descriptors[id].pub; 147 | } 148 | return NULL; 149 | } 150 | 151 | /* pm_success_fn -- "noop" function pointer */ 152 | PmError pm_success_fn(PmInternal *midi) { 153 | return pmNoError; 154 | } 155 | 156 | /* none_write -- returns an error if called */ 157 | PmError none_write_short(PmInternal *midi, PmEvent *buffer) { 158 | return pmBadPtr; 159 | } 160 | 161 | /* pm_fail_timestamp_fn -- placeholder for begin_sysex and flush */ 162 | PmError pm_fail_timestamp_fn(PmInternal *midi, PmTimestamp timestamp) { 163 | return pmBadPtr; 164 | } 165 | 166 | PmError none_write_byte(PmInternal *midi, unsigned char byte, 167 | PmTimestamp timestamp) { 168 | return pmBadPtr; 169 | } 170 | 171 | /* pm_fail_fn -- generic function, returns error if called */ 172 | PmError pm_fail_fn(PmInternal *midi) { 173 | return pmBadPtr; 174 | } 175 | 176 | static PmError none_open(PmInternal *midi, void *driverInfo) { 177 | return pmBadPtr; 178 | } 179 | static void none_get_host_error(PmInternal * midi, char * msg, unsigned int len) { 180 | strcpy(msg, ""); 181 | } 182 | static unsigned int none_has_host_error(PmInternal * midi) { 183 | return FALSE; 184 | } 185 | PmTimestamp none_synchronize(PmInternal *midi) { 186 | return 0; 187 | } 188 | 189 | #define none_abort pm_fail_fn 190 | #define none_close pm_fail_fn 191 | 192 | pm_fns_node pm_none_dictionary = { 193 | none_write_short, 194 | none_sysex, 195 | none_sysex, 196 | none_write_byte, 197 | none_write_short, 198 | none_write_flush, 199 | none_synchronize, 200 | none_open, 201 | none_abort, 202 | none_close, 203 | none_poll, 204 | none_has_host_error, 205 | none_get_host_error 206 | }; 207 | 208 | 209 | const char *Pm_GetErrorText( PmError errnum ) { 210 | const char *msg; 211 | 212 | switch(errnum) 213 | { 214 | case pmNoError: 215 | msg = ""; 216 | break; 217 | case pmHostError: 218 | msg = "PortMidi: `Host error'"; 219 | break; 220 | case pmInvalidDeviceId: 221 | msg = "PortMidi: `Invalid device ID'"; 222 | break; 223 | case pmInsufficientMemory: 224 | msg = "PortMidi: `Insufficient memory'"; 225 | break; 226 | case pmBufferTooSmall: 227 | msg = "PortMidi: `Buffer too small'"; 228 | break; 229 | case pmBadPtr: 230 | msg = "PortMidi: `Bad pointer'"; 231 | break; 232 | case pmInternalError: 233 | msg = "PortMidi: `Internal PortMidi Error'"; 234 | break; 235 | case pmBufferOverflow: 236 | msg = "PortMidi: `Buffer overflow'"; 237 | break; 238 | case pmBadData: 239 | msg = "PortMidi: `Invalid MIDI message Data'"; 240 | break; 241 | case pmBufferMaxSize: 242 | msg = "PortMidi: `Buffer cannot be made larger'"; 243 | break; 244 | default: 245 | msg = "PortMidi: `Illegal error number'"; 246 | break; 247 | } 248 | return msg; 249 | } 250 | 251 | 252 | /* This can be called whenever you get a pmHostError return value. 253 | * The error will always be in the global pm_hosterror_text. 254 | */ 255 | void Pm_GetHostErrorText(char * msg, unsigned int len) { 256 | assert(msg); 257 | assert(len > 0); 258 | if (pm_hosterror) { 259 | strncpy(msg, (char *) pm_hosterror_text, len); 260 | pm_hosterror = FALSE; 261 | pm_hosterror_text[0] = 0; /* clear the message; not necessary, but it 262 | might help with debugging */ 263 | msg[len - 1] = 0; /* make sure string is terminated */ 264 | } else { 265 | msg[0] = 0; /* no string to return */ 266 | } 267 | } 268 | 269 | 270 | int Pm_HasHostError(PortMidiStream * stream) { 271 | if (pm_hosterror) return TRUE; 272 | if (stream) { 273 | PmInternal * midi = (PmInternal *) stream; 274 | pm_hosterror = (*midi->dictionary->has_host_error)(midi); 275 | if (pm_hosterror) { 276 | midi->dictionary->host_error(midi, pm_hosterror_text, 277 | PM_HOST_ERROR_MSG_LEN); 278 | /* now error message is global */ 279 | return TRUE; 280 | } 281 | } 282 | return FALSE; 283 | } 284 | 285 | 286 | PmError Pm_Initialize( void ) { 287 | if (!pm_initialized) { 288 | pm_hosterror = FALSE; 289 | pm_hosterror_text[0] = 0; /* the null string */ 290 | pm_init(); 291 | pm_initialized = TRUE; 292 | } 293 | return pmNoError; 294 | } 295 | 296 | 297 | PmError Pm_Terminate( void ) { 298 | if (pm_initialized) { 299 | pm_term(); 300 | // if there are no devices, descriptors might still be NULL 301 | if (descriptors != NULL) { 302 | free(descriptors); 303 | descriptors = NULL; 304 | } 305 | pm_descriptor_index = 0; 306 | pm_descriptor_max = 0; 307 | pm_initialized = FALSE; 308 | } 309 | return pmNoError; 310 | } 311 | 312 | 313 | /* Pm_Read -- read up to length longs from source into buffer */ 314 | /* 315 | * returns number of longs actually read, or error code 316 | */ 317 | int Pm_Read(PortMidiStream *stream, PmEvent *buffer, long length) { 318 | PmInternal *midi = (PmInternal *) stream; 319 | int n = 0; 320 | PmError err = pmNoError; 321 | pm_hosterror = FALSE; 322 | /* arg checking */ 323 | if(midi == NULL) 324 | err = pmBadPtr; 325 | else if(!descriptors[midi->device_id].pub.opened) 326 | err = pmBadPtr; 327 | else if(!descriptors[midi->device_id].pub.input) 328 | err = pmBadPtr; 329 | /* First poll for data in the buffer... 330 | * This either simply checks for data, or attempts first to fill the buffer 331 | * with data from the MIDI hardware; this depends on the implementation. 332 | * We could call Pm_Poll here, but that would redo a lot of redundant 333 | * parameter checking, so I copied some code from Pm_Poll to here: */ 334 | else err = (*(midi->dictionary->poll))(midi); 335 | 336 | if (err != pmNoError) { 337 | if (err == pmHostError) { 338 | midi->dictionary->host_error(midi, pm_hosterror_text, 339 | PM_HOST_ERROR_MSG_LEN); 340 | pm_hosterror = TRUE; 341 | } 342 | return pm_errmsg(err); 343 | } 344 | 345 | while (n < length) { 346 | PmError err = Pm_Dequeue(midi->queue, buffer++); 347 | if (err == pmBufferOverflow) { 348 | /* ignore the data we have retreived so far */ 349 | return pm_errmsg(pmBufferOverflow); 350 | } else if (err == 0) { /* empty queue */ 351 | break; 352 | } 353 | n++; 354 | } 355 | return n; 356 | } 357 | 358 | PmError Pm_Poll( PortMidiStream *stream ) 359 | { 360 | PmInternal *midi = (PmInternal *) stream; 361 | PmError err; 362 | 363 | pm_hosterror = FALSE; 364 | /* arg checking */ 365 | if(midi == NULL) 366 | err = pmBadPtr; 367 | else if (!descriptors[midi->device_id].pub.opened) 368 | err = pmBadPtr; 369 | else if (!descriptors[midi->device_id].pub.input) 370 | err = pmBadPtr; 371 | else 372 | err = (*(midi->dictionary->poll))(midi); 373 | 374 | if (err != pmNoError) { 375 | if (err == pmHostError) { 376 | midi->dictionary->host_error(midi, pm_hosterror_text, 377 | PM_HOST_ERROR_MSG_LEN); 378 | pm_hosterror = TRUE; 379 | } 380 | return pm_errmsg(err); 381 | } 382 | 383 | return !Pm_QueueEmpty(midi->queue); 384 | } 385 | 386 | 387 | /* this is called from Pm_Write and Pm_WriteSysEx to issue a 388 | * call to the system-dependent end_sysex function and handle 389 | * the error return 390 | */ 391 | static PmError pm_end_sysex(PmInternal *midi) 392 | { 393 | PmError err = (*midi->dictionary->end_sysex)(midi, 0); 394 | midi->sysex_in_progress = FALSE; 395 | if (err == pmHostError) { 396 | midi->dictionary->host_error(midi, pm_hosterror_text, 397 | PM_HOST_ERROR_MSG_LEN); 398 | pm_hosterror = TRUE; 399 | } 400 | return err; 401 | } 402 | 403 | 404 | /* to facilitate correct error-handling, Pm_Write, Pm_WriteShort, and 405 | Pm_WriteSysEx all operate a state machine that "outputs" calls to 406 | write_short, begin_sysex, write_byte, end_sysex, and write_realtime */ 407 | 408 | PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length) 409 | { 410 | PmInternal *midi = (PmInternal *) stream; 411 | PmError err = pmNoError; 412 | int i; 413 | int bits; 414 | 415 | pm_hosterror = FALSE; 416 | /* arg checking */ 417 | if(midi == NULL) 418 | err = pmBadPtr; 419 | else if(!descriptors[midi->device_id].pub.opened) 420 | err = pmBadPtr; 421 | else if(!descriptors[midi->device_id].pub.output) 422 | err = pmBadPtr; 423 | else 424 | err = pmNoError; 425 | 426 | if (err != pmNoError) goto pm_write_error; 427 | 428 | if (midi->latency == 0) { 429 | midi->now = 0; 430 | } else { 431 | midi->now = (*(midi->time_proc))(midi->time_info); 432 | if (midi->first_message || midi->sync_time + 100 /*ms*/ < midi->now) { 433 | /* time to resync */ 434 | midi->now = (*midi->dictionary->synchronize)(midi); 435 | midi->first_message = FALSE; 436 | } 437 | } 438 | /* error recovery: when a sysex is detected, we call 439 | * dictionary->begin_sysex() followed by calls to 440 | * dictionary->write_byte() and dictionary->write_realtime() 441 | * until an end-of-sysex is detected, when we call 442 | * dictionary->end_sysex(). After an error occurs, 443 | * Pm_Write() continues to call functions. For example, 444 | * it will continue to call write_byte() even after 445 | * an error sending a sysex message, and end_sysex() will be 446 | * called when an EOX or non-real-time status is found. 447 | * When errors are detected, Pm_Write() returns immediately, 448 | * so it is possible that this will drop data and leave 449 | * sysex messages in a partially transmitted state. 450 | */ 451 | for (i = 0; i < length; i++) { 452 | unsigned long msg = buffer[i].message; 453 | bits = 0; 454 | /* is this a sysex message? */ 455 | if (Pm_MessageStatus(msg) == MIDI_SYSEX) { 456 | if (midi->sysex_in_progress) { 457 | /* error: previous sysex was not terminated by EOX */ 458 | midi->sysex_in_progress = FALSE; 459 | err = pmBadData; 460 | goto pm_write_error; 461 | } 462 | midi->sysex_in_progress = TRUE; 463 | if ((err = (*midi->dictionary->begin_sysex)(midi, 464 | buffer[i].timestamp)) != pmNoError) 465 | goto pm_write_error; 466 | if ((err = (*midi->dictionary->write_byte)(midi, MIDI_SYSEX, 467 | buffer[i].timestamp)) != pmNoError) 468 | goto pm_write_error; 469 | bits = 8; 470 | /* fall through to continue sysex processing */ 471 | } else if ((msg & MIDI_STATUS_MASK) && 472 | (Pm_MessageStatus(msg) != MIDI_EOX)) { 473 | /* a non-sysex message */ 474 | if (midi->sysex_in_progress) { 475 | /* this should be a realtime message */ 476 | if (is_real_time(msg)) { 477 | if ((err = (*midi->dictionary->write_realtime)(midi, 478 | &(buffer[i]))) != pmNoError) 479 | goto pm_write_error; 480 | } else { 481 | midi->sysex_in_progress = FALSE; 482 | err = pmBadData; 483 | /* ignore any error from this, because we already have one */ 484 | /* pass 0 as timestamp -- it's ignored */ 485 | (*midi->dictionary->end_sysex)(midi, 0); 486 | goto pm_write_error; 487 | } 488 | } else { /* regular short midi message */ 489 | if ((err = (*midi->dictionary->write_short)(midi, 490 | &(buffer[i]))) != pmNoError) 491 | goto pm_write_error; 492 | continue; 493 | } 494 | } 495 | if (midi->sysex_in_progress) { /* send sysex bytes until EOX */ 496 | /* see if we can accelerate data transfer */ 497 | if (bits == 0 && midi->fill_base && /* 4 bytes to copy */ 498 | (*midi->fill_offset_ptr) + 4 <= midi->fill_length && 499 | (msg & 0x80808080) == 0) { /* all data */ 500 | /* copy 4 bytes from msg to fill_base + fill_offset */ 501 | unsigned char *ptr = midi->fill_base + 502 | *(midi->fill_offset_ptr); 503 | ptr[0] = msg; ptr[1] = msg >> 8; 504 | ptr[2] = msg >> 18; ptr[3] = msg >> 24; 505 | (*midi->fill_offset_ptr) += 4; 506 | continue; 507 | } 508 | /* no acceleration, so do byte-by-byte copying */ 509 | while (bits < 32) { 510 | unsigned char midi_byte = (unsigned char) (msg >> bits); 511 | if ((err = (*midi->dictionary->write_byte)(midi, midi_byte, 512 | buffer[i].timestamp)) != pmNoError) 513 | goto pm_write_error; 514 | if (midi_byte == MIDI_EOX) { 515 | err = pm_end_sysex(midi); 516 | if (err != pmNoError) goto error_exit; 517 | break; /* from while loop */ 518 | } 519 | bits += 8; 520 | } 521 | } else { 522 | /* not in sysex mode, but message did not start with status */ 523 | err = pmBadData; 524 | goto pm_write_error; 525 | } 526 | } 527 | /* after all messages are processed, send the data */ 528 | if (!midi->sysex_in_progress) 529 | err = (*midi->dictionary->write_flush)(midi, 0); 530 | pm_write_error: 531 | if (err == pmHostError) { 532 | midi->dictionary->host_error(midi, pm_hosterror_text, 533 | PM_HOST_ERROR_MSG_LEN); 534 | pm_hosterror = TRUE; 535 | } 536 | error_exit: 537 | return pm_errmsg(err); 538 | } 539 | 540 | 541 | PmError Pm_WriteShort(PortMidiStream *stream, long when, long msg) 542 | { 543 | PmEvent event; 544 | 545 | event.timestamp = when; 546 | event.message = msg; 547 | return Pm_Write(stream, &event, 1); 548 | } 549 | 550 | 551 | PmError Pm_WriteSysEx(PortMidiStream *stream, PmTimestamp when, 552 | unsigned char *msg) 553 | { 554 | /* allocate buffer space for PM_DEFAULT_SYSEX_BUFFER_SIZE bytes */ 555 | /* each PmEvent holds sizeof(PmMessage) bytes of sysex data */ 556 | #define BUFLEN (PM_DEFAULT_SYSEX_BUFFER_SIZE / sizeof(PmMessage)) 557 | PmEvent buffer[BUFLEN]; 558 | int buffer_size = 1; /* first time, send 1. After that, it's BUFLEN */ 559 | PmInternal *midi = (PmInternal *) stream; 560 | /* the next byte in the buffer is represented by an index, bufx, and 561 | a shift in bits */ 562 | int shift = 0; 563 | int bufx = 0; 564 | buffer[0].message = 0; 565 | buffer[0].timestamp = when; 566 | 567 | while (1) { 568 | /* insert next byte into buffer */ 569 | buffer[bufx].message |= ((*msg) << shift); 570 | shift += 8; 571 | if (*msg++ == MIDI_EOX) break; 572 | if (shift == 32) { 573 | shift = 0; 574 | bufx++; 575 | if (bufx == buffer_size) { 576 | PmError err = Pm_Write(stream, buffer, buffer_size); 577 | /* note: Pm_Write has already called errmsg() */ 578 | if (err) return err; 579 | /* prepare to fill another buffer */ 580 | bufx = 0; 581 | buffer_size = BUFLEN; 582 | /* optimization: maybe we can just copy bytes */ 583 | if (midi->fill_base) { 584 | PmError err; 585 | while (*(midi->fill_offset_ptr) < midi->fill_length) { 586 | midi->fill_base[(*midi->fill_offset_ptr)++] = *msg; 587 | if (*msg++ == MIDI_EOX) { 588 | err = pm_end_sysex(midi); 589 | if (err != pmNoError) return pm_errmsg(err); 590 | goto end_of_sysex; 591 | } 592 | } 593 | /* I thought that I could do a pm_Write here and 594 | * change this if to a loop, avoiding calls in Pm_Write 595 | * to the slower write_byte, but since 596 | * sysex_in_progress is true, this will not flush 597 | * the buffer, and we'll infinite loop: */ 598 | /* err = Pm_Write(stream, buffer, 0); 599 | if (err) return err; */ 600 | /* instead, the way this works is that Pm_Write calls 601 | * write_byte on 4 bytes. The first, since the buffer 602 | * is full, will flush the buffer and allocate a new 603 | * one. This primes the buffer so 604 | * that we can return to the loop above and fill it 605 | * efficiently without a lot of function calls. 606 | */ 607 | buffer_size = 1; /* get another message started */ 608 | } 609 | } 610 | buffer[bufx].message = 0; 611 | buffer[bufx].timestamp = when; 612 | } 613 | /* keep inserting bytes until you find MIDI_EOX */ 614 | } 615 | end_of_sysex: 616 | /* we're finished sending full buffers, but there may 617 | * be a partial one left. 618 | */ 619 | if (shift != 0) bufx++; /* add partial message to buffer len */ 620 | if (bufx) { /* bufx is number of PmEvents to send from buffer */ 621 | PmError err = Pm_Write(stream, buffer, bufx); 622 | if (err) return err; 623 | } 624 | return pmNoError; 625 | } 626 | 627 | 628 | 629 | PmError Pm_OpenInput(PortMidiStream** stream, 630 | PmDeviceID inputDevice, 631 | void *inputDriverInfo, 632 | long bufferSize, 633 | PmTimeProcPtr time_proc, 634 | void *time_info) 635 | { 636 | PmInternal *midi; 637 | PmError err = pmNoError; 638 | pm_hosterror = FALSE; 639 | *stream = NULL; 640 | 641 | /* arg checking */ 642 | if (inputDevice < 0 || inputDevice >= pm_descriptor_index) 643 | err = pmInvalidDeviceId; 644 | else if (!descriptors[inputDevice].pub.input) 645 | err = pmBadPtr; 646 | else if(descriptors[inputDevice].pub.opened) 647 | err = pmBadPtr; 648 | 649 | if (err != pmNoError) 650 | goto error_return; 651 | 652 | /* create portMidi internal data */ 653 | midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); 654 | *stream = midi; 655 | if (!midi) { 656 | err = pmInsufficientMemory; 657 | goto error_return; 658 | } 659 | midi->device_id = inputDevice; 660 | midi->write_flag = FALSE; 661 | midi->time_proc = time_proc; 662 | midi->time_info = time_info; 663 | /* windows adds timestamps in the driver and these are more accurate than 664 | using a time_proc, so do not automatically provide a time proc. Non-win 665 | implementations may want to provide a default time_proc in their 666 | system-specific midi_out_open() method. 667 | */ 668 | if (bufferSize <= 0) bufferSize = 256; /* default buffer size */ 669 | midi->queue = Pm_QueueCreate(bufferSize, sizeof(PmEvent)); 670 | if (!midi->queue) { 671 | /* free portMidi data */ 672 | *stream = NULL; 673 | pm_free(midi); 674 | err = pmInsufficientMemory; 675 | goto error_return; 676 | } 677 | midi->buffer_len = bufferSize; /* portMidi input storage */ 678 | midi->latency = 0; /* not used */ 679 | midi->sysex_in_progress = FALSE; 680 | midi->sysex_message = 0; 681 | midi->sysex_message_count = 0; 682 | midi->filters = PM_FILT_ACTIVE; 683 | midi->channel_mask = 0xFFFF; 684 | midi->sync_time = 0; 685 | midi->first_message = TRUE; 686 | midi->dictionary = descriptors[inputDevice].dictionary; 687 | midi->fill_base = NULL; 688 | midi->fill_offset_ptr = NULL; 689 | midi->fill_length = 0; 690 | descriptors[inputDevice].internalDescriptor = midi; 691 | /* open system dependent input device */ 692 | err = (*midi->dictionary->open)(midi, inputDriverInfo); 693 | if (err) { 694 | *stream = NULL; 695 | descriptors[inputDevice].internalDescriptor = NULL; 696 | /* free portMidi data */ 697 | Pm_QueueDestroy(midi->queue); 698 | pm_free(midi); 699 | } else { 700 | /* portMidi input open successful */ 701 | descriptors[inputDevice].pub.opened = TRUE; 702 | } 703 | error_return: 704 | /* note: if there is a pmHostError, it is the responsibility 705 | * of the system-dependent code (*midi->dictionary->open)() 706 | * to set pm_hosterror and pm_hosterror_text 707 | */ 708 | return pm_errmsg(err); 709 | } 710 | 711 | 712 | PmError Pm_OpenOutput(PortMidiStream** stream, 713 | PmDeviceID outputDevice, 714 | void *outputDriverInfo, 715 | long bufferSize, 716 | PmTimeProcPtr time_proc, 717 | void *time_info, 718 | long latency ) 719 | { 720 | PmInternal *midi; 721 | PmError err = pmNoError; 722 | pm_hosterror = FALSE; 723 | *stream = NULL; 724 | 725 | /* arg checking */ 726 | if (outputDevice < 0 || outputDevice >= pm_descriptor_index) 727 | err = pmInvalidDeviceId; 728 | else if (!descriptors[outputDevice].pub.output) 729 | err = pmInvalidDeviceId; 730 | else if (descriptors[outputDevice].pub.opened) 731 | err = pmInvalidDeviceId; 732 | if (err != pmNoError) 733 | goto error_return; 734 | 735 | /* create portMidi internal data */ 736 | midi = (PmInternal *) pm_alloc(sizeof(PmInternal)); 737 | *stream = midi; 738 | if (!midi) { 739 | err = pmInsufficientMemory; 740 | goto error_return; 741 | } 742 | midi->device_id = outputDevice; 743 | midi->write_flag = TRUE; 744 | midi->time_proc = time_proc; 745 | /* if latency > 0, we need a time reference. If none is provided, 746 | use PortTime library */ 747 | if (time_proc == NULL && latency != 0) { 748 | if (!Pt_Started()) 749 | Pt_Start(1, 0, 0); 750 | /* time_get does not take a parameter, so coerce */ 751 | midi->time_proc = (PmTimeProcPtr) Pt_Time; 752 | } 753 | midi->time_info = time_info; 754 | midi->buffer_len = bufferSize; 755 | midi->queue = NULL; /* unused by output */ 756 | /* if latency zero, output immediate (timestamps ignored) */ 757 | /* if latency < 0, use 0 but don't return an error */ 758 | if (latency < 0) latency = 0; 759 | midi->latency = latency; 760 | midi->sysex_in_progress = FALSE; 761 | midi->sysex_message = 0; /* unused by output */ 762 | midi->sysex_message_count = 0; /* unused by output */ 763 | midi->filters = 0; /* not used for output */ 764 | midi->channel_mask = 0xFFFF; 765 | midi->sync_time = 0; 766 | midi->first_message = TRUE; 767 | midi->dictionary = descriptors[outputDevice].dictionary; 768 | midi->fill_base = NULL; 769 | midi->fill_offset_ptr = NULL; 770 | midi->fill_length = 0; 771 | descriptors[outputDevice].internalDescriptor = midi; 772 | /* open system dependent output device */ 773 | err = (*midi->dictionary->open)(midi, outputDriverInfo); 774 | if (err) { 775 | *stream = NULL; 776 | descriptors[outputDevice].internalDescriptor = NULL; 777 | /* free portMidi data */ 778 | pm_free(midi); 779 | } else { 780 | /* portMidi input open successful */ 781 | descriptors[outputDevice].pub.opened = TRUE; 782 | } 783 | error_return: 784 | /* note: system-dependent code must set pm_hosterror and 785 | * pm_hosterror_text if a pmHostError occurs 786 | */ 787 | return pm_errmsg(err); 788 | } 789 | 790 | 791 | PmError Pm_SetChannelMask(PortMidiStream *stream, int mask) 792 | { 793 | PmInternal *midi = (PmInternal *) stream; 794 | PmError err = pmNoError; 795 | 796 | if (midi == NULL) 797 | err = pmBadPtr; 798 | else 799 | midi->channel_mask = mask; 800 | 801 | return pm_errmsg(err); 802 | } 803 | 804 | 805 | PmError Pm_SetFilter(PortMidiStream *stream, long filters) { 806 | PmInternal *midi = (PmInternal *) stream; 807 | PmError err = pmNoError; 808 | 809 | /* arg checking */ 810 | if (midi == NULL) 811 | err = pmBadPtr; 812 | else if (!descriptors[midi->device_id].pub.opened) 813 | err = pmBadPtr; 814 | else 815 | midi->filters = filters; 816 | return pm_errmsg(err); 817 | } 818 | 819 | 820 | PmError Pm_Close( PortMidiStream *stream ) { 821 | PmInternal *midi = (PmInternal *) stream; 822 | PmError err = pmNoError; 823 | 824 | pm_hosterror = FALSE; 825 | /* arg checking */ 826 | if (midi == NULL) /* midi must point to something */ 827 | err = pmBadPtr; 828 | /* if it is an open device, the device_id will be valid */ 829 | else if (midi->device_id < 0 || midi->device_id >= pm_descriptor_index) 830 | err = pmBadPtr; 831 | /* and the device should be in the opened state */ 832 | else if (!descriptors[midi->device_id].pub.opened) 833 | err = pmBadPtr; 834 | 835 | if (err != pmNoError) 836 | goto error_return; 837 | 838 | /* close the device */ 839 | err = (*midi->dictionary->close)(midi); 840 | /* even if an error occurred, continue with cleanup */ 841 | descriptors[midi->device_id].internalDescriptor = NULL; 842 | descriptors[midi->device_id].pub.opened = FALSE; 843 | if (midi->queue) Pm_QueueDestroy(midi->queue); 844 | pm_free(midi); 845 | error_return: 846 | /* system dependent code must set pm_hosterror and 847 | * pm_hosterror_text if a pmHostError occurs. 848 | */ 849 | return pm_errmsg(err); 850 | } 851 | 852 | 853 | PmError Pm_Abort( PortMidiStream* stream ) { 854 | PmInternal *midi = (PmInternal *) stream; 855 | PmError err; 856 | /* arg checking */ 857 | if (midi == NULL) 858 | err = pmBadPtr; 859 | if (!descriptors[midi->device_id].pub.output) 860 | err = pmBadPtr; 861 | if (!descriptors[midi->device_id].pub.opened) 862 | err = pmBadPtr; 863 | else 864 | err = (*midi->dictionary->abort)(midi); 865 | 866 | if (err == pmHostError) { 867 | midi->dictionary->host_error(midi, pm_hosterror_text, 868 | PM_HOST_ERROR_MSG_LEN); 869 | pm_hosterror = TRUE; 870 | } 871 | return pm_errmsg(err); 872 | } 873 | 874 | 875 | 876 | /* pm_channel_filtered returns non-zero if the channel mask is blocking the current channel */ 877 | #define pm_channel_filtered(status, mask) \ 878 | ((((status) & 0xF0) != 0xF0) && (!(Pm_Channel((status) & 0x0F) & (mask)))) 879 | 880 | 881 | /* The following two functions will checks to see if a MIDI message matches 882 | the filtering criteria. Since the sysex routines only want to filter realtime messages, 883 | we need to have separate routines. 884 | */ 885 | 886 | 887 | /* pm_realtime_filtered returns non-zero if the filter will kill the current message. 888 | Note that only realtime messages are checked here. 889 | */ 890 | #define pm_realtime_filtered(status, filters) \ 891 | ((((status) & 0xF0) == 0xF0) && ((1 << ((status) & 0xF)) & (filters))) 892 | 893 | /* 894 | return ((status == MIDI_ACTIVE) && (filters & PM_FILT_ACTIVE)) 895 | || ((status == MIDI_CLOCK) && (filters & PM_FILT_CLOCK)) 896 | || ((status == MIDI_START) && (filters & PM_FILT_PLAY)) 897 | || ((status == MIDI_STOP) && (filters & PM_FILT_PLAY)) 898 | || ((status == MIDI_CONTINUE) && (filters & PM_FILT_PLAY)) 899 | || ((status == MIDI_F9) && (filters & PM_FILT_F9)) 900 | || ((status == MIDI_FD) && (filters & PM_FILT_FD)) 901 | || ((status == MIDI_RESET) && (filters & PM_FILT_RESET)) 902 | || ((status == MIDI_MTC) && (filters & PM_FILT_MTC)) 903 | || ((status == MIDI_SONGPOS) && (filters & PM_FILT_SONG_POSITION)) 904 | || ((status == MIDI_SONGSEL) && (filters & PM_FILT_SONG_SELECT)) 905 | || ((status == MIDI_TUNE) && (filters & PM_FILT_TUNE)); 906 | }*/ 907 | 908 | 909 | /* pm_status_filtered returns non-zero if a filter will kill the current message, based on status. 910 | Note that sysex and real time are not checked. It is up to the subsystem (winmm, core midi, alsa) 911 | to filter sysex, as it is handled more easily and efficiently at that level. 912 | Realtime message are filtered in pm_realtime_filtered. 913 | */ 914 | #define pm_status_filtered(status, filters) ((1 << (16 + ((status) >> 4))) & (filters)) 915 | 916 | 917 | /* 918 | return ((status == MIDI_NOTE_ON) && (filters & PM_FILT_NOTE)) 919 | || ((status == MIDI_NOTE_OFF) && (filters & PM_FILT_NOTE)) 920 | || ((status == MIDI_CHANNEL_AT) && (filters & PM_FILT_CHANNEL_AFTERTOUCH)) 921 | || ((status == MIDI_POLY_AT) && (filters & PM_FILT_POLY_AFTERTOUCH)) 922 | || ((status == MIDI_PROGRAM) && (filters & PM_FILT_PROGRAM)) 923 | || ((status == MIDI_CONTROL) && (filters & PM_FILT_CONTROL)) 924 | || ((status == MIDI_PITCHBEND) && (filters & PM_FILT_PITCHBEND)); 925 | 926 | } 927 | */ 928 | 929 | static void pm_flush_sysex(PmInternal *midi, PmTimestamp timestamp) 930 | { 931 | PmEvent event; 932 | 933 | /* there may be nothing in the buffer */ 934 | if (midi->sysex_message_count == 0) return; /* nothing to flush */ 935 | 936 | event.message = midi->sysex_message; 937 | event.timestamp = timestamp; 938 | /* copied from pm_read_short, avoids filtering */ 939 | if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { 940 | midi->sysex_in_progress = FALSE; 941 | } 942 | midi->sysex_message_count = 0; 943 | midi->sysex_message = 0; 944 | } 945 | 946 | 947 | /* pm_read_short and pm_read_bytes 948 | are the interface between system-dependent MIDI input handlers 949 | and the system-independent PortMIDI code. 950 | The input handler MUST obey these rules: 951 | 1) all short input messages must be sent to pm_read_short, which 952 | enqueues them to a FIFO for the application. 953 | 2) eash buffer of sysex bytes should be reported by calling pm_read_bytes 954 | (which sets midi->sysex_in_progress). After the eox byte, 955 | pm_read_bytes will clear sysex_in_progress 956 | */ 957 | 958 | /* pm_read_short is the place where all input messages arrive from 959 | system-dependent code such as pmwinmm.c. Here, the messages 960 | are entered into the PortMidi input buffer. 961 | */ 962 | void pm_read_short(PmInternal *midi, PmEvent *event) 963 | { 964 | int status; 965 | /* arg checking */ 966 | assert(midi != NULL); 967 | /* midi filtering is applied here */ 968 | status = Pm_MessageStatus(event->message); 969 | if (!pm_status_filtered(status, midi->filters) 970 | && (!is_real_time(status) || 971 | !pm_realtime_filtered(status, midi->filters)) 972 | && !pm_channel_filtered(status, midi->channel_mask)) { 973 | /* if sysex is in progress and we get a status byte, it had 974 | better be a realtime message or the starting SYSEX byte; 975 | otherwise, we exit the sysex_in_progress state 976 | */ 977 | if (midi->sysex_in_progress && (status & MIDI_STATUS_MASK)) { 978 | /* two choices: real-time or not. If it's real-time, then 979 | * this should be delivered as a sysex byte because it is 980 | * embedded in a sysex message 981 | */ 982 | if (is_real_time(status)) { 983 | midi->sysex_message |= 984 | (status << (8 * midi->sysex_message_count++)); 985 | if (midi->sysex_message_count == 4) { 986 | pm_flush_sysex(midi, event->timestamp); 987 | } 988 | } else { /* otherwise, it's not real-time. This interrupts 989 | * a sysex message in progress */ 990 | midi->sysex_in_progress = FALSE; 991 | } 992 | } else if (Pm_Enqueue(midi->queue, event) == pmBufferOverflow) { 993 | midi->sysex_in_progress = FALSE; 994 | } 995 | } 996 | } 997 | 998 | /* pm_read_bytes -- read one (partial) sysex msg from MIDI data */ 999 | /* 1000 | * returns how many bytes processed 1001 | */ 1002 | unsigned int pm_read_bytes(PmInternal *midi, const unsigned char *data, 1003 | int len, PmTimestamp timestamp) 1004 | { 1005 | unsigned int i = 0; /* index into data */ 1006 | PmEvent event; 1007 | event.timestamp = timestamp; 1008 | assert(midi); 1009 | /* note that since buffers may not have multiples of 4 bytes, 1010 | * pm_read_bytes may be called in the middle of an outgoing 1011 | * 4-byte PortMidi message. sysex_in_progress indicates that 1012 | * a sysex has been sent but no eox. 1013 | */ 1014 | if (len == 0) return 0; /* sanity check */ 1015 | if (!midi->sysex_in_progress) { 1016 | while (i < len) { /* process all data */ 1017 | unsigned char byte = data[i++]; 1018 | if (byte == MIDI_SYSEX && 1019 | !pm_realtime_filtered(byte, midi->filters)) { 1020 | midi->sysex_in_progress = TRUE; 1021 | i--; /* back up so code below will get SYSEX byte */ 1022 | break; /* continue looping below to process msg */ 1023 | } else if (byte == MIDI_EOX) { 1024 | midi->sysex_in_progress = FALSE; 1025 | return i; /* done with one message */ 1026 | } else if (byte & MIDI_STATUS_MASK) { 1027 | /* We're getting MIDI but no sysex in progress. 1028 | * Either the SYSEX status byte was dropped or 1029 | * the message was filtered. Drop the data, but 1030 | * send any embedded realtime bytes. 1031 | */ 1032 | /* assume that this is a real-time message: 1033 | * it is an error to pass non-real-time messages 1034 | * to pm_read_bytes 1035 | */ 1036 | event.message = byte; 1037 | pm_read_short(midi, &event); 1038 | } 1039 | } /* all bytes in the buffer are processed */ 1040 | } 1041 | /* Now, isysex_in_progress) { 1046 | if (midi->sysex_message_count == 0 && i <= len - 4 && 1047 | ((event.message = (((long) data[i]) | 1048 | (((long) data[i+1]) << 8) | 1049 | (((long) data[i+2]) << 16) | 1050 | (((long) data[i+3]) << 24))) & 1051 | 0x80808080) == 0) { /* all data, no status */ 1052 | if (Pm_Enqueue(midi->queue, &event) == pmBufferOverflow) { 1053 | midi->sysex_in_progress = FALSE; 1054 | } 1055 | i += 4; 1056 | } else { 1057 | while (i < len) { 1058 | /* send one byte at a time */ 1059 | unsigned char byte = data[i++]; 1060 | if (is_real_time(byte) && 1061 | pm_realtime_filtered(byte, midi->filters)) { 1062 | continue; /* real-time data is filtered, so omit */ 1063 | } 1064 | midi->sysex_message |= 1065 | (byte << (8 * midi->sysex_message_count++)); 1066 | if (byte == MIDI_EOX) { 1067 | midi->sysex_in_progress = FALSE; 1068 | pm_flush_sysex(midi, event.timestamp); 1069 | return i; 1070 | } else if (midi->sysex_message_count == 4) { 1071 | pm_flush_sysex(midi, event.timestamp); 1072 | /* after handling at least one non-data byte 1073 | * and reaching a 4-byte message boundary, 1074 | * resume trying to send 4 at a time in outer loop 1075 | */ 1076 | break; 1077 | } 1078 | } 1079 | } 1080 | } 1081 | return i; 1082 | } 1083 | 1084 | 1085 | -------------------------------------------------------------------------------- /portmidi/pm_common/portmidi.h: -------------------------------------------------------------------------------- 1 | #ifndef PORT_MIDI_H 2 | #define PORT_MIDI_H 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif /* __cplusplus */ 6 | 7 | /* 8 | * PortMidi Portable Real-Time MIDI Library 9 | * PortMidi API Header File 10 | * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ 11 | * 12 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 13 | * Copyright (c) 2001-2006 Roger B. Dannenberg 14 | * 15 | * Latest version available at: http://www.cs.cmu.edu/~music/portmidi/ 16 | * 17 | * Copyright (c) 1999-2000 Ross Bencina and Phil Burk 18 | * Copyright (c) 2001-2006 Roger B. Dannenberg 19 | * 20 | * Permission is hereby granted, free of charge, to any person obtaining 21 | * a copy of this software and associated documentation files 22 | * (the "Software"), to deal in the Software without restriction, 23 | * including without limitation the rights to use, copy, modify, merge, 24 | * publish, distribute, sublicense, and/or sell copies of the Software, 25 | * and to permit persons to whom the Software is furnished to do so, 26 | * subject to the following conditions: 27 | * 28 | * The above copyright notice and this permission notice shall be 29 | * included in all copies or substantial portions of the Software. 30 | * 31 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR 35 | * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF 36 | * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | */ 39 | 40 | /* 41 | * The text above constitutes the entire PortMidi license; however, 42 | * the PortMusic community also makes the following non-binding requests: 43 | * 44 | * Any person wishing to distribute modifications to the Software is 45 | * requested to send the modifications to the original developer so that 46 | * they can be incorporated into the canonical version. It is also 47 | * requested that these non-binding requests be included along with the 48 | * license above. 49 | */ 50 | 51 | /* CHANGELOG FOR PORTMIDI 52 | * (see ../CHANGELOG.txt) 53 | * 54 | * IMPORTANT INFORMATION ABOUT A WIN32 BUG: 55 | * 56 | * Windows apparently has a serious midi bug -- if you do not close ports, Windows 57 | * may crash. PortMidi tries to protect against this by using a DLL to clean up. 58 | * 59 | * If client exits for example with: 60 | * i) assert 61 | * ii) Ctrl^c, 62 | * then DLL clean-up routine called. However, when client does something 63 | * really bad (e.g. assigns value to NULL pointer) then DLL CLEANUP ROUTINE 64 | * NEVER RUNS! In this state, if you wait around long enough, you will 65 | * probably get the blue screen of death. Can also go into Pview and there will 66 | * exist zombie process that you can't kill. 67 | * 68 | * You can enable the DLL cleanup routine by defining USE_DLL_FOR_CLEANUP. 69 | * Do not define this preprocessor symbol if you do not want to use this 70 | * feature. 71 | * 72 | * NOTES ON HOST ERROR REPORTING: 73 | * 74 | * PortMidi errors (of type PmError) are generic, system-independent errors. 75 | * When an error does not map to one of the more specific PmErrors, the 76 | * catch-all code pmHostError is returned. This means that PortMidi has 77 | * retained a more specific system-dependent error code. The caller can 78 | * get more information by calling Pm_HasHostError() to test if there is 79 | * a pending host error, and Pm_GetHostErrorText() to get a text string 80 | * describing the error. Host errors are reported on a per-device basis 81 | * because only after you open a device does PortMidi have a place to 82 | * record the host error code. I.e. only 83 | * those routines that receive a (PortMidiStream *) argument check and 84 | * report errors. One exception to this is that Pm_OpenInput() and 85 | * Pm_OpenOutput() can report errors even though when an error occurs, 86 | * there is no PortMidiStream* to hold the error. Fortunately, both 87 | * of these functions return any error immediately, so we do not really 88 | * need per-device error memory. Instead, any host error code is stored 89 | * in a global, pmHostError is returned, and the user can call 90 | * Pm_GetHostErrorText() to get the error message (and the invalid stream 91 | * parameter will be ignored.) The functions 92 | * pm_init and pm_term do not fail or raise 93 | * errors. The job of pm_init is to locate all available devices so that 94 | * the caller can get information via PmDeviceInfo(). If an error occurs, 95 | * the device is simply not listed as available. 96 | * 97 | * Host errors come in two flavors: 98 | * a) host error 99 | * b) host error during callback 100 | * These can occur w/midi input or output devices. (b) can only happen 101 | * asynchronously (during callback routines), whereas (a) only occurs while 102 | * synchronously running PortMidi and any resulting system dependent calls. 103 | * Both (a) and (b) are reported by the next read or write call. You can 104 | * also query for asynchronous errors (b) at any time by calling 105 | * Pm_HasHostError(). 106 | * 107 | * NOTES ON COMPILE-TIME SWITCHES 108 | * 109 | * DEBUG assumes stdio and a console. Use this if you want automatic, simple 110 | * error reporting, e.g. for prototyping. If you are using MFC or some 111 | * other graphical interface with no console, DEBUG probably should be 112 | * undefined. 113 | * PM_CHECK_ERRORS more-or-less takes over error checking for return values, 114 | * stopping your program and printing error messages when an error 115 | * occurs. This also uses stdio for console text I/O. 116 | * USE_DLL_FOR_CLEANUP is described above. (Windows only.) 117 | * 118 | */ 119 | 120 | #ifndef FALSE 121 | #define FALSE 0 122 | #endif 123 | #ifndef TRUE 124 | #define TRUE 1 125 | #endif 126 | 127 | /* default size of buffers for sysex transmission: */ 128 | #define PM_DEFAULT_SYSEX_BUFFER_SIZE 1024 129 | 130 | 131 | typedef enum { 132 | pmNoError = 0, 133 | pmNoData = 0, /* A "no error" return that also indicates no data avail. */ 134 | pmGotData = 1, /* A "no error" return that also indicates data available */ 135 | pmHostError = -10000, 136 | pmInvalidDeviceId, /* out of range or 137 | * output device when input is requested or 138 | * input device when output is requested or 139 | * device is already opened 140 | */ 141 | pmInsufficientMemory, 142 | pmBufferTooSmall, 143 | pmBufferOverflow, 144 | pmBadPtr, 145 | pmBadData, /* illegal midi data, e.g. missing EOX */ 146 | pmInternalError, 147 | pmBufferMaxSize /* buffer is already as large as it can be */ 148 | /* NOTE: If you add a new error type, be sure to update Pm_GetErrorText() */ 149 | } PmError; 150 | 151 | /* 152 | Pm_Initialize() is the library initialisation function - call this before 153 | using the library. 154 | */ 155 | 156 | PmError Pm_Initialize( void ); 157 | 158 | /* 159 | Pm_Terminate() is the library termination function - call this after 160 | using the library. 161 | */ 162 | 163 | PmError Pm_Terminate( void ); 164 | 165 | /* A single PortMidiStream is a descriptor for an open MIDI device. 166 | */ 167 | typedef void PortMidiStream; 168 | #define PmStream PortMidiStream 169 | 170 | /* 171 | Test whether stream has a pending host error. Normally, the client finds 172 | out about errors through returned error codes, but some errors can occur 173 | asynchronously where the client does not 174 | explicitly call a function, and therefore cannot receive an error code. 175 | The client can test for a pending error using Pm_HasHostError(). If true, 176 | the error can be accessed and cleared by calling Pm_GetErrorText(). 177 | Errors are also cleared by calling other functions that can return 178 | errors, e.g. Pm_OpenInput(), Pm_OpenOutput(), Pm_Read(), Pm_Write(). The 179 | client does not need to call Pm_HasHostError(). Any pending error will be 180 | reported the next time the client performs an explicit function call on 181 | the stream, e.g. an input or output operation. Until the error is cleared, 182 | no new error codes will be obtained, even for a different stream. 183 | */ 184 | int Pm_HasHostError( PortMidiStream * stream ); 185 | 186 | 187 | /* Translate portmidi error number into human readable message. 188 | These strings are constants (set at compile time) so client has 189 | no need to allocate storage 190 | */ 191 | const char *Pm_GetErrorText( PmError errnum ); 192 | 193 | /* Translate portmidi host error into human readable message. 194 | These strings are computed at run time, so client has to allocate storage. 195 | After this routine executes, the host error is cleared. 196 | */ 197 | void Pm_GetHostErrorText(char * msg, unsigned int len); 198 | 199 | #define HDRLENGTH 50 200 | #define PM_HOST_ERROR_MSG_LEN 256u /* any host error msg will occupy less 201 | than this number of characters */ 202 | 203 | /* 204 | Device enumeration mechanism. 205 | 206 | Device ids range from 0 to Pm_CountDevices()-1. 207 | 208 | */ 209 | typedef int PmDeviceID; 210 | #define pmNoDevice -1 211 | typedef struct { 212 | int structVersion; 213 | const char *interf; /* underlying MIDI API, e.g. MMSystem or DirectX */ 214 | const char *name; /* device name, e.g. USB MidiSport 1x1 */ 215 | int input; /* true iff input is available */ 216 | int output; /* true iff output is available */ 217 | int opened; /* used by generic PortMidi code to do error checking on arguments */ 218 | 219 | } PmDeviceInfo; 220 | 221 | 222 | int Pm_CountDevices( void ); 223 | /* 224 | Pm_GetDefaultInputDeviceID(), Pm_GetDefaultOutputDeviceID() 225 | 226 | Return the default device ID or pmNoDevice if there are no devices. 227 | The result can be passed to Pm_OpenMidi(). 228 | 229 | On the PC, the user can specify a default device by 230 | setting an environment variable. For example, to use device #1. 231 | 232 | set PM_RECOMMENDED_OUTPUT_DEVICE=1 233 | 234 | The user should first determine the available device ID by using 235 | the supplied application "testin" or "testout". 236 | 237 | In general, the registry is a better place for this kind of info, 238 | and with USB devices that can come and go, using integers is not 239 | very reliable for device identification. Under Windows, if 240 | PM_RECOMMENDED_OUTPUT_DEVICE (or PM_RECOMMENDED_INPUT_DEVICE) is 241 | *NOT* found in the environment, then the default device is obtained 242 | by looking for a string in the registry under: 243 | HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Input_Device 244 | and HKEY_LOCAL_MACHINE/SOFTWARE/PortMidi/Recommended_Output_Device 245 | for a string. The number of the first device with a substring that 246 | matches the string exactly is returned. For example, if the string 247 | in the registry is "USB", and device 1 is named 248 | "In USB MidiSport 1x1", then that will be the default 249 | input because it contains the string "USB". 250 | 251 | In addition to the name, PmDeviceInfo has the member "interf", which 252 | is the interface name. (The "interface" is the underlying software 253 | system or API used by PortMidi to access devices. Examples are 254 | MMSystem, DirectX (not implemented), ALSA, OSS (not implemented), etc.) 255 | At present, the only Win32 interface is "MMSystem", the only Linux 256 | interface is "ALSA", and the only Max OS X interface is "CoreMIDI". 257 | To specify both the interface and the device name in the registry, 258 | separate the two with a comma and a space, e.g.: 259 | MMSystem, In USB MidiSport 1x1 260 | In this case, the string before the comma must be a substring of 261 | the "interf" string, and the string after the space must be a 262 | substring of the "name" name string in order to match the device. 263 | 264 | Note: in the current release, the default is simply the first device 265 | (the input or output device with the lowest PmDeviceID). 266 | */ 267 | PmDeviceID Pm_GetDefaultInputDeviceID( void ); 268 | PmDeviceID Pm_GetDefaultOutputDeviceID( void ); 269 | 270 | /* 271 | PmTimestamp is used to represent a millisecond clock with arbitrary 272 | start time. The type is used for all MIDI timestampes and clocks. 273 | */ 274 | typedef long PmTimestamp; 275 | typedef PmTimestamp (*PmTimeProcPtr)(void *time_info); 276 | 277 | /* TRUE if t1 before t2 */ 278 | #define PmBefore(t1,t2) ((t1-t2) < 0) 279 | 280 | /* 281 | Pm_GetDeviceInfo() returns a pointer to a PmDeviceInfo structure 282 | referring to the device specified by id. 283 | If id is out of range the function returns NULL. 284 | 285 | The returned structure is owned by the PortMidi implementation and must 286 | not be manipulated or freed. The pointer is guaranteed to be valid 287 | between calls to Pm_Initialize() and Pm_Terminate(). 288 | */ 289 | const PmDeviceInfo* Pm_GetDeviceInfo( PmDeviceID id ); 290 | 291 | /* 292 | Pm_OpenInput() and Pm_OpenOutput() open devices. 293 | 294 | stream is the address of a PortMidiStream pointer which will receive 295 | a pointer to the newly opened stream. 296 | 297 | inputDevice is the id of the device used for input (see PmDeviceID above). 298 | 299 | inputDriverInfo is a pointer to an optional driver specific data structure 300 | containing additional information for device setup or handle processing. 301 | inputDriverInfo is never required for correct operation. If not used 302 | inputDriverInfo should be NULL. 303 | 304 | outputDevice is the id of the device used for output (see PmDeviceID above.) 305 | 306 | outputDriverInfo is a pointer to an optional driver specific data structure 307 | containing additional information for device setup or handle processing. 308 | outputDriverInfo is never required for correct operation. If not used 309 | outputDriverInfo should be NULL. 310 | 311 | For input, the buffersize specifies the number of input events to be 312 | buffered waiting to be read using Pm_Read(). For output, buffersize 313 | specifies the number of output events to be buffered waiting for output. 314 | (In some cases -- see below -- PortMidi does not buffer output at all 315 | and merely passes data to a lower-level API, in which case buffersize 316 | is ignored.) 317 | 318 | latency is the delay in milliseconds applied to timestamps to determine 319 | when the output should actually occur. (If latency is < 0, 0 is assumed.) 320 | If latency is zero, timestamps are ignored and all output is delivered 321 | immediately. If latency is greater than zero, output is delayed until the 322 | message timestamp plus the latency. (NOTE: the time is measured relative 323 | to the time source indicated by time_proc. Timestamps are absolute, 324 | not relative delays or offsets.) In some cases, PortMidi can obtain 325 | better timing than your application by passing timestamps along to the 326 | device driver or hardware. Latency may also help you to synchronize midi 327 | data to audio data by matching midi latency to the audio buffer latency. 328 | 329 | time_proc is a pointer to a procedure that returns time in milliseconds. It 330 | may be NULL, in which case a default millisecond timebase (PortTime) is 331 | used. If the application wants to use PortTime, it should start the timer 332 | (call Pt_Start) before calling Pm_OpenInput or Pm_OpenOutput. If the 333 | application tries to start the timer *after* Pm_OpenInput or Pm_OpenOutput, 334 | it may get a ptAlreadyStarted error from Pt_Start, and the application's 335 | preferred time resolution and callback function will be ignored. 336 | time_proc result values are appended to incoming MIDI data, and time_proc 337 | times are used to schedule outgoing MIDI data (when latency is non-zero). 338 | 339 | time_info is a pointer passed to time_proc. 340 | 341 | Example: If I provide a timestamp of 5000, latency is 1, and time_proc 342 | returns 4990, then the desired output time will be when time_proc returns 343 | timestamp+latency = 5001. This will be 5001-4990 = 11ms from now. 344 | 345 | return value: 346 | Upon success Pm_Open() returns PmNoError and places a pointer to a 347 | valid PortMidiStream in the stream argument. 348 | If a call to Pm_Open() fails a nonzero error code is returned (see 349 | PMError above) and the value of port is invalid. 350 | 351 | Any stream that is successfully opened should eventually be closed 352 | by calling Pm_Close(). 353 | 354 | */ 355 | PmError Pm_OpenInput( PortMidiStream** stream, 356 | PmDeviceID inputDevice, 357 | void *inputDriverInfo, 358 | long bufferSize, 359 | PmTimeProcPtr time_proc, 360 | void *time_info ); 361 | 362 | PmError Pm_OpenOutput( PortMidiStream** stream, 363 | PmDeviceID outputDevice, 364 | void *outputDriverInfo, 365 | long bufferSize, 366 | PmTimeProcPtr time_proc, 367 | void *time_info, 368 | long latency ); 369 | 370 | /* 371 | Pm_SetFilter() sets filters on an open input stream to drop selected 372 | input types. By default, only active sensing messages are filtered. 373 | To prohibit, say, active sensing and sysex messages, call 374 | Pm_SetFilter(stream, PM_FILT_ACTIVE | PM_FILT_SYSEX); 375 | 376 | Filtering is useful when midi routing or midi thru functionality is being 377 | provided by the user application. 378 | For example, you may want to exclude timing messages (clock, MTC, start/stop/continue), 379 | while allowing note-related messages to pass. 380 | Or you may be using a sequencer or drum-machine for MIDI clock information but want to 381 | exclude any notes it may play. 382 | */ 383 | 384 | /* filter active sensing messages (0xFE): */ 385 | #define PM_FILT_ACTIVE (1 << 0x0E) 386 | /* filter system exclusive messages (0xF0): */ 387 | #define PM_FILT_SYSEX (1 << 0x00) 388 | /* filter MIDI clock message (0xF8) */ 389 | #define PM_FILT_CLOCK (1 << 0x08) 390 | /* filter play messages (start 0xFA, stop 0xFC, continue 0xFB) */ 391 | #define PM_FILT_PLAY ((1 << 0x0A) | (1 << 0x0C) | (1 << 0x0B)) 392 | /* filter tick messages (0xF9) */ 393 | #define PM_FILT_TICK (1 << 0x09) 394 | /* filter undefined FD messages */ 395 | #define PM_FILT_FD (1 << 0x0D) 396 | /* filter undefined real-time messages */ 397 | #define PM_FILT_UNDEFINED PM_FILT_FD 398 | /* filter reset messages (0xFF) */ 399 | #define PM_FILT_RESET (1 << 0x0F) 400 | /* filter all real-time messages */ 401 | #define PM_FILT_REALTIME (PM_FILT_ACTIVE | PM_FILT_SYSEX | PM_FILT_CLOCK | \ 402 | PM_FILT_PLAY | PM_FILT_UNDEFINED | PM_FILT_RESET | PM_FILT_TICK) 403 | /* filter note-on and note-off (0x90-0x9F and 0x80-0x8F */ 404 | #define PM_FILT_NOTE ((1 << 0x19) | (1 << 0x18)) 405 | /* filter channel aftertouch (most midi controllers use this) (0xD0-0xDF)*/ 406 | #define PM_FILT_CHANNEL_AFTERTOUCH (1 << 0x1D) 407 | /* per-note aftertouch (0xA0-0xAF) */ 408 | #define PM_FILT_POLY_AFTERTOUCH (1 << 0x1A) 409 | /* filter both channel and poly aftertouch */ 410 | #define PM_FILT_AFTERTOUCH (PM_FILT_CHANNEL_AFTERTOUCH | PM_FILT_POLY_AFTERTOUCH) 411 | /* Program changes (0xC0-0xCF) */ 412 | #define PM_FILT_PROGRAM (1 << 0x1C) 413 | /* Control Changes (CC's) (0xB0-0xBF)*/ 414 | #define PM_FILT_CONTROL (1 << 0x1B) 415 | /* Pitch Bender (0xE0-0xEF*/ 416 | #define PM_FILT_PITCHBEND (1 << 0x1E) 417 | /* MIDI Time Code (0xF1)*/ 418 | #define PM_FILT_MTC (1 << 0x01) 419 | /* Song Position (0xF2) */ 420 | #define PM_FILT_SONG_POSITION (1 << 0x02) 421 | /* Song Select (0xF3)*/ 422 | #define PM_FILT_SONG_SELECT (1 << 0x03) 423 | /* Tuning request (0xF6)*/ 424 | #define PM_FILT_TUNE (1 << 0x06) 425 | /* All System Common messages (mtc, song position, song select, tune request) */ 426 | #define PM_FILT_SYSTEMCOMMON (PM_FILT_MTC | PM_FILT_SONG_POSITION | PM_FILT_SONG_SELECT | PM_FILT_TUNE) 427 | 428 | 429 | PmError Pm_SetFilter( PortMidiStream* stream, long filters ); 430 | 431 | /* 432 | Pm_SetChannelMask() filters incoming messages based on channel. 433 | The mask is a 16-bit bitfield corresponding to appropriate channels 434 | The Pm_Channel macro can assist in calling this function. 435 | i.e. to set receive only input on channel 1, call with 436 | Pm_SetChannelMask(Pm_Channel(1)); 437 | Multiple channels should be OR'd together, like 438 | Pm_SetChannelMask(Pm_Channel(10) | Pm_Channel(11)) 439 | 440 | All channels are allowed by default 441 | */ 442 | #define Pm_Channel(channel) (1<<(channel)) 443 | 444 | PmError Pm_SetChannelMask(PortMidiStream *stream, int mask); 445 | 446 | /* 447 | Pm_Abort() terminates outgoing messages immediately 448 | The caller should immediately close the output port; 449 | this call may result in transmission of a partial midi message. 450 | There is no abort for Midi input because the user can simply 451 | ignore messages in the buffer and close an input device at 452 | any time. 453 | */ 454 | PmError Pm_Abort( PortMidiStream* stream ); 455 | 456 | /* 457 | Pm_Close() closes a midi stream, flushing any pending buffers. 458 | (PortMidi attempts to close open streams when the application 459 | exits -- this is particularly difficult under Windows.) 460 | */ 461 | PmError Pm_Close( PortMidiStream* stream ); 462 | 463 | /* 464 | Pm_Message() encodes a short Midi message into a long word. If data1 465 | and/or data2 are not present, use zero. 466 | 467 | Pm_MessageStatus(), Pm_MessageData1(), and 468 | Pm_MessageData2() extract fields from a long-encoded midi message. 469 | */ 470 | #define Pm_Message(status, data1, data2) \ 471 | ((((data2) << 16) & 0xFF0000) | \ 472 | (((data1) << 8) & 0xFF00) | \ 473 | ((status) & 0xFF)) 474 | #define Pm_MessageStatus(msg) ((msg) & 0xFF) 475 | #define Pm_MessageData1(msg) (((msg) >> 8) & 0xFF) 476 | #define Pm_MessageData2(msg) (((msg) >> 16) & 0xFF) 477 | 478 | /* All midi data comes in the form of PmEvent structures. A sysex 479 | message is encoded as a sequence of PmEvent structures, with each 480 | structure carrying 4 bytes of the message, i.e. only the first 481 | PmEvent carries the status byte. 482 | 483 | Note that MIDI allows nested messages: the so-called "real-time" MIDI 484 | messages can be inserted into the MIDI byte stream at any location, 485 | including within a sysex message. MIDI real-time messages are one-byte 486 | messages used mainly for timing (see the MIDI spec). PortMidi retains 487 | the order of non-real-time MIDI messages on both input and output, but 488 | it does not specify exactly how real-time messages are processed. This 489 | is particulary problematic for MIDI input, because the input parser 490 | must either prepare to buffer an unlimited number of sysex message 491 | bytes or to buffer an unlimited number of real-time messages that 492 | arrive embedded in a long sysex message. To simplify things, the input 493 | parser is allowed to pass real-time MIDI messages embedded within a 494 | sysex message, and it is up to the client to detect, process, and 495 | remove these messages as they arrive. 496 | 497 | When receiving sysex messages, the sysex message is terminated 498 | by either an EOX status byte (anywhere in the 4 byte messages) or 499 | by a non-real-time status byte in the low order byte of the message. 500 | If you get a non-real-time status byte but there was no EOX byte, it 501 | means the sysex message was somehow truncated. This is not 502 | considered an error; e.g., a missing EOX can result from the user 503 | disconnecting a MIDI cable during sysex transmission. 504 | 505 | A real-time message can occur within a sysex message. A real-time 506 | message will always occupy a full PmEvent with the status byte in 507 | the low-order byte of the PmEvent message field. (This implies that 508 | the byte-order of sysex bytes and real-time message bytes may not 509 | be preserved -- for example, if a real-time message arrives after 510 | 3 bytes of a sysex message, the real-time message will be delivered 511 | first. The first word of the sysex message will be delivered only 512 | after the 4th byte arrives, filling the 4-byte PmEvent message field. 513 | 514 | The timestamp field is observed when the output port is opened with 515 | a non-zero latency. A timestamp of zero means "use the current time", 516 | which in turn means to deliver the message with a delay of 517 | latency (the latency parameter used when opening the output port.) 518 | Do not expect PortMidi to sort data according to timestamps -- 519 | messages should be sent in the correct order, and timestamps MUST 520 | be non-decreasing. See also "Example" for Pm_OpenOutput() above. 521 | 522 | A sysex message will generally fill many PmEvent structures. On 523 | output to a PortMidiStream with non-zero latency, the first timestamp 524 | on sysex message data will determine the time to begin sending the 525 | message. PortMidi implementations may ignore timestamps for the 526 | remainder of the sysex message. 527 | 528 | On input, the timestamp ideally denotes the arrival time of the 529 | status byte of the message. The first timestamp on sysex message 530 | data will be valid. Subsequent timestamps may denote 531 | when message bytes were actually received, or they may be simply 532 | copies of the first timestamp. 533 | 534 | Timestamps for nested messages: If a real-time message arrives in 535 | the middle of some other message, it is enqueued immediately with 536 | the timestamp corresponding to its arrival time. The interrupted 537 | non-real-time message or 4-byte packet of sysex data will be enqueued 538 | later. The timestamp of interrupted data will be equal to that of 539 | the interrupting real-time message to insure that timestamps are 540 | non-decreasing. 541 | */ 542 | typedef long PmMessage; 543 | typedef struct { 544 | PmMessage message; 545 | PmTimestamp timestamp; 546 | } PmEvent; 547 | 548 | /* 549 | Pm_Read() retrieves midi data into a buffer, and returns the number 550 | of events read. Result is a non-negative number unless an error occurs, 551 | in which case a PmError value will be returned. 552 | 553 | Buffer Overflow 554 | 555 | The problem: if an input overflow occurs, data will be lost, ultimately 556 | because there is no flow control all the way back to the data source. 557 | When data is lost, the receiver should be notified and some sort of 558 | graceful recovery should take place, e.g. you shouldn't resume receiving 559 | in the middle of a long sysex message. 560 | 561 | With a lock-free fifo, which is pretty much what we're stuck with to 562 | enable portability to the Mac, it's tricky for the producer and consumer 563 | to synchronously reset the buffer and resume normal operation. 564 | 565 | Solution: the buffer managed by PortMidi will be flushed when an overflow 566 | occurs. The consumer (Pm_Read()) gets an error message (pmBufferOverflow) 567 | and ordinary processing resumes as soon as a new message arrives. The 568 | remainder of a partial sysex message is not considered to be a "new 569 | message" and will be flushed as well. 570 | 571 | */ 572 | int Pm_Read( PortMidiStream *stream, PmEvent *buffer, long length ); 573 | 574 | /* 575 | Pm_Poll() tests whether input is available, 576 | returning TRUE, FALSE, or an error value. 577 | */ 578 | PmError Pm_Poll( PortMidiStream *stream); 579 | 580 | /* 581 | Pm_Write() writes midi data from a buffer. This may contain: 582 | - short messages 583 | or 584 | - sysex messages that are converted into a sequence of PmEvent 585 | structures, e.g. sending data from a file or forwarding them 586 | from midi input. 587 | 588 | Use Pm_WriteSysEx() to write a sysex message stored as a contiguous 589 | array of bytes. 590 | 591 | Sysex data may contain embedded real-time messages. 592 | */ 593 | PmError Pm_Write( PortMidiStream *stream, PmEvent *buffer, long length ); 594 | 595 | /* 596 | Pm_WriteShort() writes a timestamped non-system-exclusive midi message. 597 | Messages are delivered in order as received, and timestamps must be 598 | non-decreasing. (But timestamps are ignored if the stream was opened 599 | with latency = 0.) 600 | */ 601 | PmError Pm_WriteShort( PortMidiStream *stream, PmTimestamp when, long msg); 602 | 603 | /* 604 | Pm_WriteSysEx() writes a timestamped system-exclusive midi message. 605 | */ 606 | PmError Pm_WriteSysEx( PortMidiStream *stream, PmTimestamp when, unsigned char *msg); 607 | 608 | 609 | #ifdef __cplusplus 610 | } 611 | #endif /* __cplusplus */ 612 | #endif /* PORT_MIDI_H */ 613 | -------------------------------------------------------------------------------- /portmidi/pm_linux/pmlinux.c: -------------------------------------------------------------------------------- 1 | /* pmlinux.c -- PortMidi os-dependent code */ 2 | 3 | /* This file only needs to implement pm_init(), which calls various 4 | routines to register the available midi devices. This file must 5 | be separate from the main portmidi.c file because it is system 6 | dependent, and it is separate from, pmlinuxalsa.c, because it 7 | might need to register non-alsa devices as well. 8 | 9 | NOTE: if you add non-ALSA support, you need to fix :alsa_poll() 10 | in pmlinuxalsa.c, which assumes all input devices are ALSA. 11 | */ 12 | 13 | #include "stdlib.h" 14 | #include "portmidi.h" 15 | #ifdef PMALSA 16 | #include "pmlinuxalsa.h" 17 | #endif 18 | 19 | #ifdef PMNULL 20 | #include "pmlinuxnull.h" 21 | #endif 22 | 23 | PmError pm_init() 24 | { 25 | /* Note: it is not an error for PMALSA to fail to initialize. 26 | * It may be a design error that the client cannot query what subsystems 27 | * are working properly other than by looking at the list of available 28 | * devices. 29 | */ 30 | #ifdef PMALSA 31 | pm_linuxalsa_init(); 32 | #endif 33 | #ifdef PMNULL 34 | pm_linuxnull_init(); 35 | #endif 36 | return pmNoError; 37 | } 38 | 39 | void pm_term(void) 40 | { 41 | #ifdef PMALSA 42 | pm_linuxalsa_term(); 43 | #endif 44 | } 45 | 46 | PmDeviceID pm_default_input_device_id = -1; 47 | PmDeviceID pm_default_output_device_id = -1; 48 | 49 | PmDeviceID Pm_GetDefaultInputDeviceID() { 50 | return pm_default_input_device_id; 51 | } 52 | 53 | PmDeviceID Pm_GetDefaultOutputDeviceID() { 54 | return pm_default_output_device_id; 55 | } 56 | 57 | void *pm_alloc(size_t s) { return malloc(s); } 58 | 59 | void pm_free(void *ptr) { free(ptr); } 60 | 61 | -------------------------------------------------------------------------------- /portmidi/pm_linux/pmlinux.h: -------------------------------------------------------------------------------- 1 | /* pmlinux.h */ 2 | 3 | extern PmDeviceID pm_default_input_device_id; 4 | extern PmDeviceID pm_default_output_device_id; 5 | 6 | -------------------------------------------------------------------------------- /portmidi/pm_linux/pmlinuxalsa.c: -------------------------------------------------------------------------------- 1 | /* 2 | * pmlinuxalsa.c -- system specific definitions 3 | * 4 | * written by: 5 | * Roger Dannenberg (port to Alsa 0.9.x) 6 | * Clemens Ladisch (provided code examples and invaluable consulting) 7 | * Jason Cohen, Rico Colon, Matt Filippone (Alsa 0.5.x implementation) 8 | */ 9 | 10 | #include "stdlib.h" 11 | #include "portmidi.h" 12 | #include "pmutil.h" 13 | #include "pminternal.h" 14 | #include "pmlinuxalsa.h" 15 | #include "string.h" 16 | #include "porttime.h" 17 | #include "pmlinux.h" 18 | 19 | #include 20 | 21 | /* I used many print statements to debug this code. I left them in the 22 | * source, and you can turn them on by changing false to true below: 23 | */ 24 | #define VERBOSE_ON 0 25 | #define VERBOSE if (VERBOSE_ON) 26 | 27 | #define MIDI_SYSEX 0xf0 28 | #define MIDI_EOX 0xf7 29 | 30 | #if SND_LIB_MAJOR == 0 && SND_LIB_MINOR < 9 31 | #error needs ALSA 0.9.0 or later 32 | #endif 33 | 34 | /* to store client/port in the device descriptor */ 35 | #define MAKE_DESCRIPTOR(client, port) ((void*)(size_t)(((client) << 8) | (port))) 36 | #define GET_DESCRIPTOR_CLIENT(info) ((((size_t)(info)) >> 8) & 0xff) 37 | #define GET_DESCRIPTOR_PORT(info) (((size_t)(info)) & 0xff) 38 | 39 | #define BYTE unsigned char 40 | #define UINT unsigned long 41 | 42 | extern pm_fns_node pm_linuxalsa_in_dictionary; 43 | extern pm_fns_node pm_linuxalsa_out_dictionary; 44 | 45 | static snd_seq_t *seq = NULL; // all input comes here, 46 | // output queue allocated on seq 47 | static int queue, queue_used; /* one for all ports, reference counted */ 48 | 49 | typedef struct alsa_descriptor_struct { 50 | int client; 51 | int port; 52 | int this_port; 53 | int in_sysex; 54 | snd_midi_event_t *parser; 55 | int error; /* host error code */ 56 | } alsa_descriptor_node, *alsa_descriptor_type; 57 | 58 | 59 | /* get_alsa_error_text -- copy error text to potentially short string */ 60 | /**/ 61 | static void get_alsa_error_text(char *msg, int len, int err) 62 | { 63 | int errlen = strlen(snd_strerror(err)); 64 | if (errlen < len) { 65 | strcpy(msg, snd_strerror(err)); 66 | } else if (len > 20) { 67 | sprintf(msg, "Alsa error %d", err); 68 | } else if (len > 4) { 69 | strcpy(msg, "Alsa"); 70 | } else { 71 | msg[0] = 0; 72 | } 73 | } 74 | 75 | 76 | /* queue is shared by both input and output, reference counted */ 77 | static PmError alsa_use_queue(void) 78 | { 79 | if (queue_used == 0) { 80 | snd_seq_queue_tempo_t *tempo; 81 | 82 | queue = snd_seq_alloc_queue(seq); 83 | if (queue < 0) { 84 | pm_hosterror = queue; 85 | return pmHostError; 86 | } 87 | snd_seq_queue_tempo_alloca(&tempo); 88 | snd_seq_queue_tempo_set_tempo(tempo, 480000); 89 | snd_seq_queue_tempo_set_ppq(tempo, 480); 90 | pm_hosterror = snd_seq_set_queue_tempo(seq, queue, tempo); 91 | if (pm_hosterror < 0) 92 | return pmHostError; 93 | 94 | snd_seq_start_queue(seq, queue, NULL); 95 | snd_seq_drain_output(seq); 96 | } 97 | ++queue_used; 98 | return pmNoError; 99 | } 100 | 101 | 102 | static void alsa_unuse_queue(void) 103 | { 104 | if (--queue_used == 0) { 105 | snd_seq_stop_queue(seq, queue, NULL); 106 | snd_seq_drain_output(seq); 107 | snd_seq_free_queue(seq, queue); 108 | VERBOSE printf("queue freed\n"); 109 | } 110 | } 111 | 112 | 113 | /* midi_message_length -- how many bytes in a message? */ 114 | static int midi_message_length(PmMessage message) 115 | { 116 | message &= 0xff; 117 | if (message < 0x80) { 118 | return 0; 119 | } else if (message < 0xf0) { 120 | static const int length[] = {3, 3, 3, 3, 2, 2, 3}; 121 | return length[(message - 0x80) >> 4]; 122 | } else { 123 | static const int length[] = { 124 | -1, 2, 3, 2, 0, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1, 1}; 125 | return length[message - 0xf0]; 126 | } 127 | } 128 | 129 | 130 | static PmError alsa_out_open(PmInternal *midi, void *driverInfo) 131 | { 132 | void *client_port = descriptors[midi->device_id].descriptor; 133 | alsa_descriptor_type desc = (alsa_descriptor_type) 134 | pm_alloc(sizeof(alsa_descriptor_node)); 135 | snd_seq_port_info_t *info; 136 | int err; 137 | 138 | if (!desc) return pmInsufficientMemory; 139 | 140 | snd_seq_port_info_alloca(&info); 141 | snd_seq_port_info_set_port(info, midi->device_id); 142 | snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | 143 | SND_SEQ_PORT_CAP_READ); 144 | snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | 145 | SND_SEQ_PORT_TYPE_APPLICATION); 146 | snd_seq_port_info_set_port_specified(info, 1); 147 | err = snd_seq_create_port(seq, info); 148 | if (err < 0) goto free_desc; 149 | 150 | /* fill in fields of desc, which is passed to pm_write routines */ 151 | midi->descriptor = desc; 152 | desc->client = GET_DESCRIPTOR_CLIENT(client_port); 153 | desc->port = GET_DESCRIPTOR_PORT(client_port); 154 | desc->this_port = midi->device_id; 155 | desc->in_sysex = 0; 156 | 157 | desc->error = 0; 158 | 159 | err = snd_midi_event_new(PM_DEFAULT_SYSEX_BUFFER_SIZE, &desc->parser); 160 | if (err < 0) goto free_this_port; 161 | 162 | if (midi->latency > 0) { /* must delay output using a queue */ 163 | err = alsa_use_queue(); 164 | if (err < 0) goto free_parser; 165 | 166 | err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); 167 | if (err < 0) goto unuse_queue; /* clean up and return on error */ 168 | } else { 169 | err = snd_seq_connect_to(seq, desc->this_port, desc->client, desc->port); 170 | if (err < 0) goto free_parser; /* clean up and return on error */ 171 | } 172 | return pmNoError; 173 | 174 | unuse_queue: 175 | alsa_unuse_queue(); 176 | free_parser: 177 | snd_midi_event_free(desc->parser); 178 | free_this_port: 179 | snd_seq_delete_port(seq, desc->this_port); 180 | free_desc: 181 | pm_free(desc); 182 | pm_hosterror = err; 183 | if (err < 0) { 184 | get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); 185 | } 186 | return pmHostError; 187 | } 188 | 189 | 190 | static PmError alsa_write_byte(PmInternal *midi, unsigned char byte, 191 | PmTimestamp timestamp) 192 | { 193 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 194 | snd_seq_event_t ev; 195 | int err; 196 | 197 | snd_seq_ev_clear(&ev); 198 | if (snd_midi_event_encode_byte(desc->parser, byte, &ev) == 1) { 199 | snd_seq_ev_set_dest(&ev, desc->client, desc->port); 200 | snd_seq_ev_set_source(&ev, desc->this_port); 201 | if (midi->latency > 0) { 202 | /* compute relative time of event = timestamp - now + latency */ 203 | PmTimestamp now = (midi->time_proc ? 204 | midi->time_proc(midi->time_info) : 205 | Pt_Time(NULL)); 206 | int when = timestamp; 207 | /* if timestamp is zero, send immediately */ 208 | /* otherwise compute time delay and use delay if positive */ 209 | if (when == 0) when = now; 210 | when = (when - now) + midi->latency; 211 | if (when < 0) when = 0; 212 | VERBOSE printf("timestamp %d now %d latency %ld, ", 213 | (int) timestamp, (int) now, midi->latency); 214 | VERBOSE printf("scheduling event after %d\n", when); 215 | /* message is sent in relative ticks, where 1 tick = 1 ms */ 216 | snd_seq_ev_schedule_tick(&ev, queue, 1, when); 217 | /* NOTE: for cases where the user does not supply a time function, 218 | we could optimize the code by not starting Pt_Time and using 219 | the alsa tick time instead. I didn't do this because it would 220 | entail changing the queue management to start the queue tick 221 | count when PortMidi is initialized and keep it running until 222 | PortMidi is terminated. (This should be simple, but it's not 223 | how the code works now.) -RBD */ 224 | } else { /* send event out without queueing */ 225 | VERBOSE printf("direct\n"); 226 | /* ev.queue = SND_SEQ_QUEUE_DIRECT; 227 | ev.dest.client = SND_SEQ_ADDRESS_SUBSCRIBERS; */ 228 | snd_seq_ev_set_direct(&ev); 229 | } 230 | VERBOSE printf("sending event\n"); 231 | err = snd_seq_event_output(seq, &ev); 232 | if (err < 0) { 233 | desc->error = err; 234 | return pmHostError; 235 | } 236 | } 237 | return pmNoError; 238 | } 239 | 240 | 241 | static PmError alsa_out_close(PmInternal *midi) 242 | { 243 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 244 | if (!desc) return pmBadPtr; 245 | 246 | if (pm_hosterror = snd_seq_disconnect_to(seq, desc->this_port, 247 | desc->client, desc->port)) { 248 | // if there's an error, try to delete the port anyway, but don't 249 | // change the pm_hosterror value so we retain the first error 250 | snd_seq_delete_port(seq, desc->this_port); 251 | } else { // if there's no error, delete the port and retain any error 252 | pm_hosterror = snd_seq_delete_port(seq, desc->this_port); 253 | } 254 | if (midi->latency > 0) alsa_unuse_queue(); 255 | snd_midi_event_free(desc->parser); 256 | midi->descriptor = NULL; /* destroy the pointer to signify "closed" */ 257 | pm_free(desc); 258 | if (pm_hosterror) { 259 | get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, 260 | pm_hosterror); 261 | return pmHostError; 262 | } 263 | return pmNoError; 264 | } 265 | 266 | 267 | static PmError alsa_in_open(PmInternal *midi, void *driverInfo) 268 | { 269 | void *client_port = descriptors[midi->device_id].descriptor; 270 | alsa_descriptor_type desc = (alsa_descriptor_type) 271 | pm_alloc(sizeof(alsa_descriptor_node)); 272 | snd_seq_port_info_t *info; 273 | snd_seq_port_subscribe_t *sub; 274 | snd_seq_addr_t addr; 275 | int err; 276 | 277 | if (!desc) return pmInsufficientMemory; 278 | 279 | err = alsa_use_queue(); 280 | if (err < 0) goto free_desc; 281 | 282 | snd_seq_port_info_alloca(&info); 283 | snd_seq_port_info_set_port(info, midi->device_id); 284 | snd_seq_port_info_set_capability(info, SND_SEQ_PORT_CAP_WRITE | 285 | SND_SEQ_PORT_CAP_READ); 286 | snd_seq_port_info_set_type(info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | 287 | SND_SEQ_PORT_TYPE_APPLICATION); 288 | snd_seq_port_info_set_port_specified(info, 1); 289 | /* bugfix: without the following, it won't generate timestamp - PaulLiu */ 290 | snd_seq_port_info_set_timestamping(info, 1); 291 | snd_seq_port_info_set_timestamp_real(info, 0); 292 | snd_seq_port_info_set_timestamp_queue(info, queue); 293 | err = snd_seq_create_port(seq, info); 294 | if (err < 0) goto free_queue; 295 | 296 | /* fill in fields of desc, which is passed to pm_write routines */ 297 | midi->descriptor = desc; 298 | desc->client = GET_DESCRIPTOR_CLIENT(client_port); 299 | desc->port = GET_DESCRIPTOR_PORT(client_port); 300 | desc->this_port = midi->device_id; 301 | desc->in_sysex = 0; 302 | 303 | desc->error = 0; 304 | 305 | VERBOSE printf("snd_seq_connect_from: %d %d %d\n", 306 | desc->this_port, desc->client, desc->port); 307 | snd_seq_port_subscribe_alloca(&sub); 308 | addr.client = snd_seq_client_id(seq); 309 | addr.port = desc->this_port; 310 | snd_seq_port_subscribe_set_dest(sub, &addr); 311 | addr.client = desc->client; 312 | addr.port = desc->port; 313 | snd_seq_port_subscribe_set_sender(sub, &addr); 314 | snd_seq_port_subscribe_set_time_update(sub, 1); 315 | /* this doesn't seem to work: messages come in with real timestamps */ 316 | snd_seq_port_subscribe_set_time_real(sub, 0); 317 | err = snd_seq_subscribe_port(seq, sub); 318 | /* err = 319 | snd_seq_connect_from(seq, desc->this_port, desc->client, desc->port); */ 320 | if (err < 0) goto free_this_port; /* clean up and return on error */ 321 | return pmNoError; 322 | 323 | free_this_port: 324 | snd_seq_delete_port(seq, desc->this_port); 325 | free_queue: 326 | alsa_unuse_queue(); 327 | free_desc: 328 | pm_free(desc); 329 | pm_hosterror = err; 330 | if (err < 0) { 331 | get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, err); 332 | } 333 | return pmHostError; 334 | } 335 | 336 | static PmError alsa_in_close(PmInternal *midi) 337 | { 338 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 339 | if (!desc) return pmBadPtr; 340 | if (pm_hosterror = snd_seq_disconnect_from(seq, desc->this_port, 341 | desc->client, desc->port)) { 342 | snd_seq_delete_port(seq, desc->this_port); /* try to close port */ 343 | } else { 344 | pm_hosterror = snd_seq_delete_port(seq, desc->this_port); 345 | } 346 | alsa_unuse_queue(); 347 | pm_free(desc); 348 | if (pm_hosterror) { 349 | get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, 350 | pm_hosterror); 351 | return pmHostError; 352 | } 353 | return pmNoError; 354 | } 355 | 356 | 357 | static PmError alsa_abort(PmInternal *midi) 358 | { 359 | /* NOTE: ALSA documentation is vague. This is supposed to 360 | * remove any pending output messages. If you can test and 361 | * confirm this code is correct, please update this comment. -RBD 362 | */ 363 | /* Unfortunately, I can't even compile it -- my ALSA version 364 | * does not implement snd_seq_remove_events_t, so this does 365 | * not compile. I'll try again, but it looks like I'll need to 366 | * upgrade my entire Linux OS -RBD 367 | */ 368 | /* 369 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 370 | snd_seq_remove_events_t info; 371 | snd_seq_addr_t addr; 372 | addr.client = desc->client; 373 | addr.port = desc->port; 374 | snd_seq_remove_events_set_dest(&info, &addr); 375 | snd_seq_remove_events_set_condition(&info, SND_SEQ_REMOVE_DEST); 376 | pm_hosterror = snd_seq_remove_events(seq, &info); 377 | if (pm_hosterror) { 378 | get_alsa_error_text(pm_hosterror_text, PM_HOST_ERROR_MSG_LEN, 379 | pm_hosterror); 380 | return pmHostError; 381 | } 382 | */ 383 | printf("WARNING: alsa_abort not implemented\n"); 384 | return pmNoError; 385 | } 386 | 387 | 388 | #ifdef GARBAGE 389 | This is old code here temporarily for reference 390 | static PmError alsa_write(PmInternal *midi, PmEvent *buffer, long length) 391 | { 392 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 393 | int i, bytes; 394 | unsigned char byte; 395 | long msg; 396 | 397 | desc->error = 0; 398 | for (; length > 0; length--, buffer++) { 399 | VERBOSE printf("message 0x%x\n", buffer->message); 400 | if (Pm_MessageStatus(buffer->message) == MIDI_SYSEX) 401 | desc->in_sysex = TRUE; 402 | if (desc->in_sysex) { 403 | msg = buffer->message; 404 | for (i = 0; i < 4; i++) { 405 | byte = msg; /* extract next byte to send */ 406 | alsa_write_byte(midi, byte, buffer->timestamp); 407 | if (byte == MIDI_EOX) { 408 | desc->in_sysex = FALSE; 409 | break; 410 | } 411 | if (desc->error < 0) break; 412 | msg >>= 8; /* shift next byte into position */ 413 | } 414 | } else { 415 | bytes = midi_message_length(buffer->message); 416 | msg = buffer->message; 417 | for (i = 0; i < bytes; i++) { 418 | byte = msg; /* extract next byte to send */ 419 | VERBOSE printf("sending 0x%x\n", byte); 420 | alsa_write_byte(midi, byte, buffer->timestamp); 421 | if (desc->error < 0) break; 422 | msg >>= 8; /* shift next byte into position */ 423 | } 424 | } 425 | } 426 | if (desc->error < 0) return pmHostError; 427 | 428 | VERBOSE printf("snd_seq_drain_output: 0x%x\n", seq); 429 | desc->error = snd_seq_drain_output(seq); 430 | if (desc->error < 0) return pmHostError; 431 | 432 | desc->error = pmNoError; 433 | return pmNoError; 434 | } 435 | #endif 436 | 437 | 438 | static PmError alsa_write_flush(PmInternal *midi, PmTimestamp timestamp) 439 | { 440 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 441 | VERBOSE printf("snd_seq_drain_output: 0x%p\n", seq); 442 | desc->error = snd_seq_drain_output(seq); 443 | if (desc->error < 0) return pmHostError; 444 | 445 | desc->error = pmNoError; 446 | return pmNoError; 447 | } 448 | 449 | 450 | static PmError alsa_write_short(PmInternal *midi, PmEvent *event) 451 | { 452 | int bytes = midi_message_length(event->message); 453 | long msg = event->message; 454 | int i; 455 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 456 | for (i = 0; i < bytes; i++) { 457 | unsigned char byte = msg; 458 | VERBOSE printf("sending 0x%x\n", byte); 459 | alsa_write_byte(midi, byte, event->timestamp); 460 | if (desc->error < 0) break; 461 | msg >>= 8; /* shift next byte into position */ 462 | } 463 | if (desc->error < 0) return pmHostError; 464 | desc->error = pmNoError; 465 | return pmNoError; 466 | } 467 | 468 | 469 | /* alsa_sysex -- implements begin_sysex and end_sysex */ 470 | PmError alsa_sysex(PmInternal *midi, PmTimestamp timestamp) { 471 | return pmNoError; 472 | } 473 | 474 | 475 | static PmTimestamp alsa_synchronize(PmInternal *midi) 476 | { 477 | return 0; /* linux implementation does not use this synchronize function */ 478 | /* Apparently, Alsa data is relative to the time you send it, and there 479 | is no reference. If this is true, this is a serious shortcoming of 480 | Alsa. If not true, then PortMidi has a serious shortcoming -- it 481 | should be scheduling relative to Alsa's time reference. */ 482 | } 483 | 484 | 485 | static void handle_event(snd_seq_event_t *ev) 486 | { 487 | int device_id = ev->dest.port; 488 | PmInternal *midi = descriptors[device_id].internalDescriptor; 489 | PmEvent pm_ev; 490 | PmTimeProcPtr time_proc = midi->time_proc; 491 | PmTimestamp timestamp; 492 | 493 | /* time stamp should be in ticks, using our queue where 1 tick = 1ms */ 494 | assert((ev->flags & SND_SEQ_TIME_STAMP_MASK) == SND_SEQ_TIME_STAMP_TICK); 495 | 496 | /* if no time_proc, just return "native" ticks (ms) */ 497 | if (time_proc == NULL) { 498 | timestamp = ev->time.tick; 499 | } else { /* translate time to time_proc basis */ 500 | snd_seq_queue_status_t *queue_status; 501 | snd_seq_queue_status_alloca(&queue_status); 502 | snd_seq_get_queue_status(seq, queue, queue_status); 503 | /* return (now - alsa_now) + alsa_timestamp */ 504 | timestamp = (*time_proc)(midi->time_info) + ev->time.tick - 505 | snd_seq_queue_status_get_tick_time(queue_status); 506 | } 507 | pm_ev.timestamp = timestamp; 508 | switch (ev->type) { 509 | case SND_SEQ_EVENT_NOTEON: 510 | pm_ev.message = Pm_Message(0x90 | ev->data.note.channel, 511 | ev->data.note.note & 0x7f, 512 | ev->data.note.velocity & 0x7f); 513 | pm_read_short(midi, &pm_ev); 514 | break; 515 | case SND_SEQ_EVENT_NOTEOFF: 516 | pm_ev.message = Pm_Message(0x80 | ev->data.note.channel, 517 | ev->data.note.note & 0x7f, 518 | ev->data.note.velocity & 0x7f); 519 | pm_read_short(midi, &pm_ev); 520 | break; 521 | case SND_SEQ_EVENT_KEYPRESS: 522 | pm_ev.message = Pm_Message(0xa0 | ev->data.note.channel, 523 | ev->data.note.note & 0x7f, 524 | ev->data.note.velocity & 0x7f); 525 | pm_read_short(midi, &pm_ev); 526 | break; 527 | case SND_SEQ_EVENT_CONTROLLER: 528 | pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, 529 | ev->data.control.param & 0x7f, 530 | ev->data.control.value & 0x7f); 531 | pm_read_short(midi, &pm_ev); 532 | break; 533 | case SND_SEQ_EVENT_PGMCHANGE: 534 | pm_ev.message = Pm_Message(0xc0 | ev->data.note.channel, 535 | ev->data.control.value & 0x7f, 0); 536 | pm_read_short(midi, &pm_ev); 537 | break; 538 | case SND_SEQ_EVENT_CHANPRESS: 539 | pm_ev.message = Pm_Message(0xd0 | ev->data.note.channel, 540 | ev->data.control.value & 0x7f, 0); 541 | pm_read_short(midi, &pm_ev); 542 | break; 543 | case SND_SEQ_EVENT_PITCHBEND: 544 | pm_ev.message = Pm_Message(0xe0 | ev->data.note.channel, 545 | (ev->data.control.value + 0x2000) & 0x7f, 546 | ((ev->data.control.value + 0x2000) >> 7) & 0x7f); 547 | pm_read_short(midi, &pm_ev); 548 | break; 549 | case SND_SEQ_EVENT_CONTROL14: 550 | if (ev->data.control.param < 0x20) { 551 | pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, 552 | ev->data.control.param, 553 | (ev->data.control.value >> 7) & 0x7f); 554 | pm_read_short(midi, &pm_ev); 555 | pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, 556 | ev->data.control.param + 0x20, 557 | ev->data.control.value & 0x7f); 558 | pm_read_short(midi, &pm_ev); 559 | } else { 560 | pm_ev.message = Pm_Message(0xb0 | ev->data.note.channel, 561 | ev->data.control.param & 0x7f, 562 | ev->data.control.value & 0x7f); 563 | 564 | pm_read_short(midi, &pm_ev); 565 | } 566 | break; 567 | case SND_SEQ_EVENT_SONGPOS: 568 | pm_ev.message = Pm_Message(0xf2, 569 | ev->data.control.value & 0x7f, 570 | (ev->data.control.value >> 7) & 0x7f); 571 | pm_read_short(midi, &pm_ev); 572 | break; 573 | case SND_SEQ_EVENT_SONGSEL: 574 | pm_ev.message = Pm_Message(0xf3, 575 | ev->data.control.value & 0x7f, 0); 576 | pm_read_short(midi, &pm_ev); 577 | break; 578 | case SND_SEQ_EVENT_QFRAME: 579 | pm_ev.message = Pm_Message(0xf1, 580 | ev->data.control.value & 0x7f, 0); 581 | pm_read_short(midi, &pm_ev); 582 | break; 583 | case SND_SEQ_EVENT_START: 584 | pm_ev.message = Pm_Message(0xfa, 0, 0); 585 | pm_read_short(midi, &pm_ev); 586 | break; 587 | case SND_SEQ_EVENT_CONTINUE: 588 | pm_ev.message = Pm_Message(0xfb, 0, 0); 589 | pm_read_short(midi, &pm_ev); 590 | break; 591 | case SND_SEQ_EVENT_STOP: 592 | pm_ev.message = Pm_Message(0xfc, 0, 0); 593 | pm_read_short(midi, &pm_ev); 594 | break; 595 | case SND_SEQ_EVENT_CLOCK: 596 | pm_ev.message = Pm_Message(0xf8, 0, 0); 597 | pm_read_short(midi, &pm_ev); 598 | break; 599 | case SND_SEQ_EVENT_TUNE_REQUEST: 600 | pm_ev.message = Pm_Message(0xf6, 0, 0); 601 | pm_read_short(midi, &pm_ev); 602 | break; 603 | case SND_SEQ_EVENT_RESET: 604 | pm_ev.message = Pm_Message(0xff, 0, 0); 605 | pm_read_short(midi, &pm_ev); 606 | break; 607 | case SND_SEQ_EVENT_SENSING: 608 | pm_ev.message = Pm_Message(0xfe, 0, 0); 609 | pm_read_short(midi, &pm_ev); 610 | break; 611 | case SND_SEQ_EVENT_SYSEX: { 612 | const BYTE *ptr = (const BYTE *) ev->data.ext.ptr; 613 | /* assume there is one sysex byte to process */ 614 | pm_read_bytes(midi, ptr, ev->data.ext.len, timestamp); 615 | break; 616 | } 617 | /* TODO: must handle port close messsage - PaulLiu */ 618 | //case SND_SEQ_EVENT_UNSUBSCRIBE: { 619 | //} 620 | default: 621 | printf("not handled type %x\n", ev->type); 622 | } 623 | } 624 | 625 | 626 | static PmError alsa_poll(PmInternal *midi) 627 | { 628 | snd_seq_event_t *ev; 629 | /* expensive check for input data, gets data from device: */ 630 | while (snd_seq_event_input_pending(seq, TRUE) > 0) { 631 | /* cheap check on local input buffer */ 632 | while (snd_seq_event_input_pending(seq, FALSE) > 0) { 633 | /* check for and ignore errors, e.g. input overflow */ 634 | /* note: if there's overflow, this should be reported 635 | * all the way through to client. Since input from all 636 | * devices is merged, we need to find all input devices 637 | * and set all to the overflow state. 638 | * NOTE: this assumes every input is ALSA based. 639 | */ 640 | int rslt = snd_seq_event_input(seq, &ev); 641 | if (rslt >= 0) { 642 | handle_event(ev); 643 | } else if (rslt == -ENOSPC) { 644 | int i; 645 | for (i = 0; i < pm_descriptor_index; i++) { 646 | if (descriptors[i].pub.input) { 647 | PmInternal *midi = (PmInternal *) 648 | descriptors[i].internalDescriptor; 649 | /* careful, device may not be open! */ 650 | if (midi) Pm_SetOverflow(midi->queue); 651 | } 652 | } 653 | } 654 | } 655 | } 656 | return pmNoError; 657 | } 658 | 659 | 660 | static unsigned int alsa_has_host_error(PmInternal *midi) 661 | { 662 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 663 | return desc->error; 664 | } 665 | 666 | 667 | static void alsa_get_host_error(PmInternal *midi, char *msg, unsigned int len) 668 | { 669 | alsa_descriptor_type desc = (alsa_descriptor_type) midi->descriptor; 670 | int err = (pm_hosterror || desc->error); 671 | get_alsa_error_text(msg, len, err); 672 | } 673 | 674 | 675 | pm_fns_node pm_linuxalsa_in_dictionary = { 676 | none_write_short, 677 | none_sysex, 678 | none_sysex, 679 | none_write_byte, 680 | none_write_short, 681 | none_write_flush, 682 | alsa_synchronize, 683 | alsa_in_open, 684 | alsa_abort, 685 | alsa_in_close, 686 | alsa_poll, 687 | alsa_has_host_error, 688 | alsa_get_host_error 689 | }; 690 | 691 | pm_fns_node pm_linuxalsa_out_dictionary = { 692 | alsa_write_short, 693 | alsa_sysex, 694 | alsa_sysex, 695 | alsa_write_byte, 696 | alsa_write_short, /* short realtime message */ 697 | alsa_write_flush, 698 | alsa_synchronize, 699 | alsa_out_open, 700 | alsa_abort, 701 | alsa_out_close, 702 | none_poll, 703 | alsa_has_host_error, 704 | alsa_get_host_error 705 | }; 706 | 707 | 708 | /* pm_strdup -- copy a string to the heap. Use this rather than strdup so 709 | * that we call pm_alloc, not malloc. This allows portmidi to avoid 710 | * malloc which might cause priority inversion. Probably ALSA is going 711 | * to call malloc anyway, so this extra work here may be pointless. 712 | */ 713 | char *pm_strdup(const char *s) 714 | { 715 | int len = strlen(s); 716 | char *dup = (char *) pm_alloc(len + 1); 717 | strcpy(dup, s); 718 | return dup; 719 | } 720 | 721 | 722 | PmError pm_linuxalsa_init( void ) 723 | { 724 | int err; 725 | snd_seq_client_info_t *cinfo; 726 | snd_seq_port_info_t *pinfo; 727 | unsigned int caps; 728 | 729 | /* Previously, the last parameter was SND_SEQ_NONBLOCK, but this 730 | * would cause messages to be dropped if the ALSA buffer fills up. 731 | * The correct behavior is for writes to block until there is 732 | * room to send all the data. The client should normally allocate 733 | * a large enough buffer to avoid blocking on output. 734 | * Now that blocking is enabled, the seq_event_input() will block 735 | * if there is no input data. This is not what we want, so must 736 | * call seq_event_input_pending() to avoid blocking. 737 | */ 738 | err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); 739 | if (err < 0) return err; 740 | 741 | snd_seq_client_info_alloca(&cinfo); 742 | snd_seq_port_info_alloca(&pinfo); 743 | 744 | snd_seq_client_info_set_client(cinfo, -1); 745 | while (snd_seq_query_next_client(seq, cinfo) == 0) { 746 | snd_seq_port_info_set_client(pinfo, snd_seq_client_info_get_client(cinfo)); 747 | snd_seq_port_info_set_port(pinfo, -1); 748 | while (snd_seq_query_next_port(seq, pinfo) == 0) { 749 | if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM) 750 | continue; /* ignore Timer and Announce ports on client 0 */ 751 | caps = snd_seq_port_info_get_capability(pinfo); 752 | if (!(caps & (SND_SEQ_PORT_CAP_SUBS_READ | SND_SEQ_PORT_CAP_SUBS_WRITE))) 753 | continue; /* ignore if you cannot read or write port */ 754 | if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE) { 755 | if (pm_default_output_device_id == -1) 756 | pm_default_output_device_id = pm_descriptor_index; 757 | pm_add_device("ALSA", 758 | pm_strdup(snd_seq_port_info_get_name(pinfo)), 759 | FALSE, 760 | MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), 761 | snd_seq_port_info_get_port(pinfo)), 762 | &pm_linuxalsa_out_dictionary); 763 | } 764 | if (caps & SND_SEQ_PORT_CAP_SUBS_READ) { 765 | if (pm_default_input_device_id == -1) 766 | pm_default_input_device_id = pm_descriptor_index; 767 | pm_add_device("ALSA", 768 | pm_strdup(snd_seq_port_info_get_name(pinfo)), 769 | TRUE, 770 | MAKE_DESCRIPTOR(snd_seq_port_info_get_client(pinfo), 771 | snd_seq_port_info_get_port(pinfo)), 772 | &pm_linuxalsa_in_dictionary); 773 | } 774 | } 775 | } 776 | return pmNoError; 777 | } 778 | 779 | 780 | void pm_linuxalsa_term(void) 781 | { 782 | if (seq) { 783 | snd_seq_close(seq); 784 | pm_free(descriptors); 785 | descriptors = NULL; 786 | pm_descriptor_index = 0; 787 | pm_descriptor_max = 0; 788 | } 789 | } 790 | -------------------------------------------------------------------------------- /portmidi/pm_linux/pmlinuxalsa.h: -------------------------------------------------------------------------------- 1 | /* pmlinuxalsa.h -- system-specific definitions */ 2 | 3 | PmError pm_linuxalsa_init(void); 4 | void pm_linuxalsa_term(void); 5 | 6 | 7 | -------------------------------------------------------------------------------- /portmidi/pm_mac/pmmac.c: -------------------------------------------------------------------------------- 1 | /* pmmac.c -- PortMidi os-dependent code */ 2 | 3 | /* This file only needs to implement: 4 | pm_init(), which calls various routines to register the 5 | available midi devices, 6 | Pm_GetDefaultInputDeviceID(), and 7 | Pm_GetDefaultOutputDeviceID(). 8 | It is seperate from pmmacosxcm because we might want to register 9 | non-CoreMIDI devices. 10 | */ 11 | 12 | #include "stdlib.h" 13 | #include "portmidi.h" 14 | #include "pmmacosxcm.h" 15 | 16 | PmError pm_init() 17 | { 18 | return pm_macosxcm_init(); 19 | } 20 | 21 | void pm_term(void) 22 | { 23 | pm_macosxcm_term(); 24 | } 25 | 26 | PmDeviceID pm_default_input_device_id = -1; 27 | PmDeviceID pm_default_output_device_id = -1; 28 | 29 | PmDeviceID Pm_GetDefaultInputDeviceID() 30 | { 31 | return pm_default_input_device_id; 32 | } 33 | 34 | PmDeviceID Pm_GetDefaultOutputDeviceID() { 35 | return pm_default_output_device_id; 36 | } 37 | 38 | void *pm_alloc(size_t s) { return malloc(s); } 39 | 40 | void pm_free(void *ptr) { free(ptr); } 41 | 42 | 43 | -------------------------------------------------------------------------------- /portmidi/pm_mac/pmmac.h: -------------------------------------------------------------------------------- 1 | /* pmmac.h */ 2 | 3 | extern PmDeviceID pm_default_input_device_id; 4 | extern PmDeviceID pm_default_output_device_id; -------------------------------------------------------------------------------- /portmidi/pm_mac/pmmacosxcm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Platform interface to the MacOS X CoreMIDI framework 3 | * 4 | * Jon Parise 5 | * and subsequent work by Andrew Zeldis and Zico Kolter 6 | * and Roger B. Dannenberg 7 | * 8 | * $Id: pmmacosx.c,v 1.17 2002/01/27 02:40:40 jon Exp $ 9 | */ 10 | 11 | /* Notes: 12 | since the input and output streams are represented by MIDIEndpointRef 13 | values and almost no other state, we store the MIDIEndpointRef on 14 | descriptors[midi->device_id].descriptor. The only other state we need 15 | is for errors: we need to know if there is an error and if so, what is 16 | the error text. We use a structure with two kinds of 17 | host error: "error" and "callback_error". That way, asynchronous callbacks 18 | do not interfere with other error information. 19 | 20 | OS X does not seem to have an error-code-to-text function, so we will 21 | just use text messages instead of error codes. 22 | */ 23 | 24 | #include 25 | 26 | //#define CM_DEBUG 1 27 | 28 | #include "portmidi.h" 29 | #include "pmutil.h" 30 | #include "pminternal.h" 31 | #include "porttime.h" 32 | #include "pmmac.h" 33 | #include "pmmacosxcm.h" 34 | 35 | #include 36 | #include 37 | 38 | #include 39 | #include 40 | #include 41 | 42 | #define PACKET_BUFFER_SIZE 1024 43 | 44 | /* this is very strange: if I put in a reasonable 45 | number here, e.g. 128, which would allow sysex data 46 | to be sent 128 bytes at a time, then I lose sysex 47 | data in my loopback test. With a buffer size of 4, 48 | we put at most 4 bytes in a packet (but maybe many 49 | packets in a packetList), and everything works fine. 50 | */ 51 | #define SYSEX_BUFFER_SIZE 4 52 | 53 | #define VERBOSE_ON 1 54 | #define VERBOSE if (VERBOSE_ON) 55 | 56 | #define MIDI_SYSEX 0xf0 57 | #define MIDI_EOX 0xf7 58 | #define MIDI_STATUS_MASK 0x80 59 | 60 | static MIDIClientRef client = NULL; /* Client handle to the MIDI server */ 61 | static MIDIPortRef portIn = NULL; /* Input port handle */ 62 | static MIDIPortRef portOut = NULL; /* Output port handle */ 63 | 64 | extern pm_fns_node pm_macosx_in_dictionary; 65 | extern pm_fns_node pm_macosx_out_dictionary; 66 | 67 | typedef struct midi_macosxcm_struct { 68 | unsigned long sync_time; /* when did we last determine delta? */ 69 | UInt64 delta; /* difference between stream time and real time in ns */ 70 | UInt64 last_time; /* last output time */ 71 | int first_message; /* tells midi_write to sychronize timestamps */ 72 | int sysex_mode; /* middle of sending sysex */ 73 | unsigned long sysex_word; /* accumulate data when receiving sysex */ 74 | unsigned int sysex_byte_count; /* count how many received */ 75 | char error[PM_HOST_ERROR_MSG_LEN]; 76 | char callback_error[PM_HOST_ERROR_MSG_LEN]; 77 | Byte packetBuffer[PACKET_BUFFER_SIZE]; 78 | MIDIPacketList *packetList; /* a pointer to packetBuffer */ 79 | MIDIPacket *packet; 80 | Byte sysex_buffer[SYSEX_BUFFER_SIZE]; /* temp storage for sysex data */ 81 | MIDITimeStamp sysex_timestamp; /* timestamp to use with sysex data */ 82 | /* allow for running status (is running status possible here? -rbd): -cpr */ 83 | unsigned char last_command; 84 | long last_msg_length; 85 | } midi_macosxcm_node, *midi_macosxcm_type; 86 | 87 | /* private function declarations */ 88 | MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp); 89 | PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp); 90 | 91 | char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint); 92 | 93 | 94 | static int 95 | midi_length(long msg) 96 | { 97 | int status, high, low; 98 | static int high_lengths[] = { 99 | 1, 1, 1, 1, 1, 1, 1, 1, /* 0x00 through 0x70 */ 100 | 3, 3, 3, 3, 2, 2, 3, 1 /* 0x80 through 0xf0 */ 101 | }; 102 | static int low_lengths[] = { 103 | 1, 2, 3, 2, 1, 1, 1, 1, /* 0xf0 through 0xf8 */ 104 | 1, 1, 1, 1, 1, 1, 1, 1 /* 0xf9 through 0xff */ 105 | }; 106 | 107 | status = msg & 0xFF; 108 | high = status >> 4; 109 | low = status & 15; 110 | 111 | return (high != 0xF) ? high_lengths[high] : low_lengths[low]; 112 | } 113 | 114 | static PmTimestamp midi_synchronize(PmInternal *midi) 115 | { 116 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 117 | UInt64 pm_stream_time_2 = 118 | AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); 119 | PmTimestamp real_time; 120 | UInt64 pm_stream_time; 121 | /* if latency is zero and this is an output, there is no 122 | time reference and midi_synchronize should never be called */ 123 | assert(midi->time_proc); 124 | assert(!(midi->write_flag && midi->latency == 0)); 125 | do { 126 | /* read real_time between two reads of stream time */ 127 | pm_stream_time = pm_stream_time_2; 128 | real_time = (*midi->time_proc)(midi->time_info); 129 | pm_stream_time_2 = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime()); 130 | /* repeat if more than 0.5 ms has elapsed */ 131 | } while (pm_stream_time_2 > pm_stream_time + 500000); 132 | m->delta = pm_stream_time - ((UInt64) real_time * (UInt64) 1000000); 133 | m->sync_time = real_time; 134 | return real_time; 135 | } 136 | 137 | 138 | static void 139 | process_packet(MIDIPacket *packet, PmEvent *event, 140 | PmInternal *midi, midi_macosxcm_type m) 141 | { 142 | /* handle a packet of MIDI messages from CoreMIDI */ 143 | /* there may be multiple short messages in one packet (!) */ 144 | unsigned int remaining_length = packet->length; 145 | unsigned char *cur_packet_data = packet->data; 146 | while (remaining_length > 0) { 147 | if (cur_packet_data[0] == MIDI_SYSEX || 148 | /* are we in the middle of a sysex message? */ 149 | (m->last_command == 0 && 150 | !(cur_packet_data[0] & MIDI_STATUS_MASK))) { 151 | m->last_command = 0; /* no running status */ 152 | unsigned int amt = pm_read_bytes(midi, cur_packet_data, 153 | remaining_length, 154 | event->timestamp); 155 | remaining_length -= amt; 156 | cur_packet_data += amt; 157 | } else if (cur_packet_data[0] == MIDI_EOX) { 158 | /* this should never happen, because pm_read_bytes should 159 | * get and read all EOX bytes*/ 160 | midi->sysex_in_progress = FALSE; 161 | m->last_command = 0; 162 | } else if (cur_packet_data[0] & MIDI_STATUS_MASK) { 163 | /* compute the length of the next (short) msg in packet */ 164 | unsigned int cur_message_length = midi_length(cur_packet_data[0]); 165 | if (cur_message_length > remaining_length) { 166 | #ifdef DEBUG 167 | printf("PortMidi debug msg: not enough data"); 168 | #endif 169 | /* since there's no more data, we're done */ 170 | return; 171 | } 172 | m->last_msg_length = cur_message_length; 173 | m->last_command = cur_packet_data[0]; 174 | switch (cur_message_length) { 175 | case 1: 176 | event->message = Pm_Message(cur_packet_data[0], 0, 0); 177 | break; 178 | case 2: 179 | event->message = Pm_Message(cur_packet_data[0], 180 | cur_packet_data[1], 0); 181 | break; 182 | case 3: 183 | event->message = Pm_Message(cur_packet_data[0], 184 | cur_packet_data[1], 185 | cur_packet_data[2]); 186 | break; 187 | default: 188 | /* PortMIDI internal error; should never happen */ 189 | assert(cur_message_length == 1); 190 | return; /* give up on packet if continued after assert */ 191 | } 192 | pm_read_short(midi, event); 193 | remaining_length -= m->last_msg_length; 194 | cur_packet_data += m->last_msg_length; 195 | } else if (m->last_msg_length > remaining_length + 1) { 196 | /* we have running status, but not enough data */ 197 | #ifdef DEBUG 198 | printf("PortMidi debug msg: not enough data in CoreMIDI packet"); 199 | #endif 200 | /* since there's no more data, we're done */ 201 | return; 202 | } else { /* output message using running status */ 203 | switch (m->last_msg_length) { 204 | case 1: 205 | event->message = Pm_Message(m->last_command, 0, 0); 206 | break; 207 | case 2: 208 | event->message = Pm_Message(m->last_command, 209 | cur_packet_data[0], 0); 210 | break; 211 | case 3: 212 | event->message = Pm_Message(m->last_command, 213 | cur_packet_data[0], 214 | cur_packet_data[1]); 215 | break; 216 | default: 217 | /* last_msg_length is invalid -- internal PortMIDI error */ 218 | assert(m->last_msg_length == 1); 219 | } 220 | pm_read_short(midi, event); 221 | remaining_length -= (m->last_msg_length - 1); 222 | cur_packet_data += (m->last_msg_length - 1); 223 | } 224 | } 225 | } 226 | 227 | 228 | 229 | /* called when MIDI packets are received */ 230 | static void 231 | readProc(const MIDIPacketList *newPackets, void *refCon, void *connRefCon) 232 | { 233 | PmInternal *midi; 234 | midi_macosxcm_type m; 235 | PmEvent event; 236 | MIDIPacket *packet; 237 | unsigned int packetIndex; 238 | unsigned long now; 239 | unsigned int status; 240 | 241 | #ifdef CM_DEBUG 242 | printf("readProc: numPackets %d: ", newPackets->numPackets); 243 | #endif 244 | 245 | /* Retrieve the context for this connection */ 246 | midi = (PmInternal *) connRefCon; 247 | m = (midi_macosxcm_type) midi->descriptor; 248 | assert(m); 249 | 250 | /* synchronize time references every 100ms */ 251 | now = (*midi->time_proc)(midi->time_info); 252 | if (m->first_message || m->sync_time + 100 /*ms*/ < now) { 253 | /* time to resync */ 254 | now = midi_synchronize(midi); 255 | m->first_message = FALSE; 256 | } 257 | 258 | packet = (MIDIPacket *) &newPackets->packet[0]; 259 | /* printf("readproc packet status %x length %d\n", packet->data[0], 260 | packet->length); */ 261 | for (packetIndex = 0; packetIndex < newPackets->numPackets; packetIndex++) { 262 | /* Set the timestamp and dispatch this message */ 263 | event.timestamp = 264 | (AudioConvertHostTimeToNanos(packet->timeStamp) - m->delta) / 265 | (UInt64) 1000000; 266 | status = packet->data[0]; 267 | /* process packet as sysex data if it begins with MIDI_SYSEX, or 268 | MIDI_EOX or non-status byte with no running status */ 269 | #ifdef CM_DEBUG 270 | printf(" %d", packet->length); 271 | #endif 272 | if (status == MIDI_SYSEX || status == MIDI_EOX || 273 | ((!(status & MIDI_STATUS_MASK)) && !m->last_command)) { 274 | /* previously was: !(status & MIDI_STATUS_MASK)) { 275 | * but this could mistake running status for sysex data 276 | */ 277 | /* reset running status data -cpr */ 278 | m->last_command = 0; 279 | m->last_msg_length = 0; 280 | /* printf("sysex packet length: %d\n", packet->length); */ 281 | pm_read_bytes(midi, packet->data, packet->length, event.timestamp); 282 | } else { 283 | process_packet(packet, &event, midi, m); 284 | } 285 | packet = MIDIPacketNext(packet); 286 | } 287 | #ifdef CM_DEBUG 288 | printf("\n"); 289 | #endif 290 | } 291 | 292 | static PmError 293 | midi_in_open(PmInternal *midi, void *driverInfo) 294 | { 295 | MIDIEndpointRef endpoint; 296 | midi_macosxcm_type m; 297 | OSStatus macHostError; 298 | 299 | /* insure that we have a time_proc for timing */ 300 | if (midi->time_proc == NULL) { 301 | if (!Pt_Started()) 302 | Pt_Start(1, 0, 0); 303 | /* time_get does not take a parameter, so coerce */ 304 | midi->time_proc = (PmTimeProcPtr) Pt_Time; 305 | } 306 | 307 | endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; 308 | if (endpoint == NULL) { 309 | return pmInvalidDeviceId; 310 | } 311 | 312 | m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ 313 | midi->descriptor = m; 314 | if (!m) { 315 | return pmInsufficientMemory; 316 | } 317 | m->error[0] = 0; 318 | m->callback_error[0] = 0; 319 | m->sync_time = 0; 320 | m->delta = 0; 321 | m->last_time = 0; 322 | m->first_message = TRUE; 323 | m->sysex_mode = FALSE; 324 | m->sysex_word = 0; 325 | m->sysex_byte_count = 0; 326 | m->packetList = NULL; 327 | m->packet = NULL; 328 | m->last_command = 0; 329 | m->last_msg_length = 0; 330 | 331 | macHostError = MIDIPortConnectSource(portIn, endpoint, midi); 332 | if (macHostError != noErr) { 333 | pm_hosterror = macHostError; 334 | sprintf(pm_hosterror_text, 335 | "Host error %ld: MIDIPortConnectSource() in midi_in_open()", 336 | macHostError); 337 | midi->descriptor = NULL; 338 | pm_free(m); 339 | return pmHostError; 340 | } 341 | 342 | return pmNoError; 343 | } 344 | 345 | static PmError 346 | midi_in_close(PmInternal *midi) 347 | { 348 | MIDIEndpointRef endpoint; 349 | OSStatus macHostError; 350 | PmError err = pmNoError; 351 | 352 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 353 | 354 | if (!m) return pmBadPtr; 355 | 356 | endpoint = (MIDIEndpointRef) descriptors[midi->device_id].descriptor; 357 | if (endpoint == NULL) { 358 | pm_hosterror = pmBadPtr; 359 | } 360 | 361 | /* shut off the incoming messages before freeing data structures */ 362 | macHostError = MIDIPortDisconnectSource(portIn, endpoint); 363 | if (macHostError != noErr) { 364 | pm_hosterror = macHostError; 365 | sprintf(pm_hosterror_text, 366 | "Host error %ld: MIDIPortDisconnectSource() in midi_in_close()", 367 | macHostError); 368 | err = pmHostError; 369 | } 370 | 371 | midi->descriptor = NULL; 372 | pm_free(midi->descriptor); 373 | 374 | return err; 375 | } 376 | 377 | 378 | static PmError 379 | midi_out_open(PmInternal *midi, void *driverInfo) 380 | { 381 | midi_macosxcm_type m; 382 | 383 | m = (midi_macosxcm_type) pm_alloc(sizeof(midi_macosxcm_node)); /* create */ 384 | midi->descriptor = m; 385 | if (!m) { 386 | return pmInsufficientMemory; 387 | } 388 | m->error[0] = 0; 389 | m->callback_error[0] = 0; 390 | m->sync_time = 0; 391 | m->delta = 0; 392 | m->last_time = 0; 393 | m->first_message = TRUE; 394 | m->sysex_mode = FALSE; 395 | m->sysex_word = 0; 396 | m->sysex_byte_count = 0; 397 | m->packetList = (MIDIPacketList *) m->packetBuffer; 398 | m->packet = NULL; 399 | m->last_command = 0; 400 | m->last_msg_length = 0; 401 | 402 | return pmNoError; 403 | } 404 | 405 | 406 | static PmError 407 | midi_out_close(PmInternal *midi) 408 | { 409 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 410 | if (!m) return pmBadPtr; 411 | 412 | midi->descriptor = NULL; 413 | pm_free(midi->descriptor); 414 | 415 | return pmNoError; 416 | } 417 | 418 | static PmError 419 | midi_abort(PmInternal *midi) 420 | { 421 | return pmNoError; 422 | } 423 | 424 | 425 | static PmError 426 | midi_write_flush(PmInternal *midi, PmTimestamp timestamp) 427 | { 428 | OSStatus macHostError; 429 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 430 | MIDIEndpointRef endpoint = 431 | (MIDIEndpointRef) descriptors[midi->device_id].descriptor; 432 | assert(m); 433 | assert(endpoint); 434 | if (m->packet != NULL) { 435 | /* out of space, send the buffer and start refilling it */ 436 | macHostError = MIDISend(portOut, endpoint, m->packetList); 437 | m->packet = NULL; /* indicate no data in packetList now */ 438 | if (macHostError != noErr) goto send_packet_error; 439 | } 440 | return pmNoError; 441 | 442 | send_packet_error: 443 | pm_hosterror = macHostError; 444 | sprintf(pm_hosterror_text, 445 | "Host error %ld: MIDISend() in midi_write()", 446 | macHostError); 447 | return pmHostError; 448 | 449 | } 450 | 451 | 452 | static PmError 453 | send_packet(PmInternal *midi, Byte *message, unsigned int messageLength, 454 | MIDITimeStamp timestamp) 455 | { 456 | PmError err; 457 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 458 | assert(m); 459 | 460 | /* printf("add %d to packet %lx len %d\n", message[0], m->packet, messageLength); */ 461 | m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), 462 | m->packet, timestamp, messageLength, 463 | message); 464 | if (m->packet == NULL) { 465 | /* out of space, send the buffer and start refilling it */ 466 | /* make midi->packet non-null to fool midi_write_flush into sending */ 467 | m->packet = (MIDIPacket *) 4; 468 | if ((err = midi_write_flush(midi, timestamp)) != pmNoError) return err; 469 | m->packet = MIDIPacketListInit(m->packetList); 470 | assert(m->packet); /* if this fails, it's a programming error */ 471 | m->packet = MIDIPacketListAdd(m->packetList, sizeof(m->packetBuffer), 472 | m->packet, timestamp, messageLength, 473 | message); 474 | assert(m->packet); /* can't run out of space on first message */ 475 | } 476 | return pmNoError; 477 | } 478 | 479 | 480 | static PmError 481 | midi_write_short(PmInternal *midi, PmEvent *event) 482 | { 483 | long when = event->timestamp; 484 | long what = event->message; 485 | MIDITimeStamp timestamp; 486 | UInt64 when_ns; 487 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 488 | Byte message[4]; 489 | unsigned int messageLength; 490 | 491 | if (m->packet == NULL) { 492 | m->packet = MIDIPacketListInit(m->packetList); 493 | /* this can never fail, right? failure would indicate something 494 | unrecoverable */ 495 | assert(m->packet); 496 | } 497 | 498 | /* compute timestamp */ 499 | if (when == 0) when = midi->now; 500 | /* if latency == 0, midi->now is not valid. We will just set it to zero */ 501 | if (midi->latency == 0) when = 0; 502 | when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; 503 | /* make sure we don't go backward in time */ 504 | if (when_ns < m->last_time) when_ns = m->last_time; 505 | m->last_time = when_ns; 506 | timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); 507 | 508 | message[0] = Pm_MessageStatus(what); 509 | message[1] = Pm_MessageData1(what); 510 | message[2] = Pm_MessageData2(what); 511 | messageLength = midi_length(what); 512 | 513 | /* Add this message to the packet list */ 514 | return send_packet(midi, message, messageLength, timestamp); 515 | } 516 | 517 | 518 | static PmError 519 | midi_begin_sysex(PmInternal *midi, PmTimestamp when) 520 | { 521 | UInt64 when_ns; 522 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 523 | assert(m); 524 | m->sysex_byte_count = 0; 525 | 526 | /* compute timestamp */ 527 | if (when == 0) when = midi->now; 528 | /* if latency == 0, midi->now is not valid. We will just set it to zero */ 529 | if (midi->latency == 0) when = 0; 530 | when_ns = ((UInt64) (when + midi->latency) * (UInt64) 1000000) + m->delta; 531 | m->sysex_timestamp = (MIDITimeStamp) AudioConvertNanosToHostTime(when_ns); 532 | 533 | if (m->packet == NULL) { 534 | m->packet = MIDIPacketListInit(m->packetList); 535 | /* this can never fail, right? failure would indicate something 536 | unrecoverable */ 537 | assert(m->packet); 538 | } 539 | return pmNoError; 540 | } 541 | 542 | 543 | static PmError 544 | midi_end_sysex(PmInternal *midi, PmTimestamp when) 545 | { 546 | PmError err; 547 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 548 | assert(m); 549 | 550 | /* make sure we don't go backward in time */ 551 | if (m->sysex_timestamp < m->last_time) m->sysex_timestamp = m->last_time; 552 | 553 | /* now send what's in the buffer */ 554 | err = send_packet(midi, m->sysex_buffer, m->sysex_byte_count, 555 | m->sysex_timestamp); 556 | m->sysex_byte_count = 0; 557 | if (err != pmNoError) { 558 | m->packet = NULL; /* flush everything in the packet list */ 559 | return err; 560 | } 561 | return pmNoError; 562 | } 563 | 564 | 565 | static PmError 566 | midi_write_byte(PmInternal *midi, unsigned char byte, PmTimestamp timestamp) 567 | { 568 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 569 | assert(m); 570 | if (m->sysex_byte_count >= SYSEX_BUFFER_SIZE) { 571 | PmError err = midi_end_sysex(midi, timestamp); 572 | if (err != pmNoError) return err; 573 | } 574 | m->sysex_buffer[m->sysex_byte_count++] = byte; 575 | return pmNoError; 576 | } 577 | 578 | 579 | static PmError 580 | midi_write_realtime(PmInternal *midi, PmEvent *event) 581 | { 582 | /* to send a realtime message during a sysex message, first 583 | flush all pending sysex bytes into packet list */ 584 | PmError err = midi_end_sysex(midi, 0); 585 | if (err != pmNoError) return err; 586 | /* then we can just do a normal midi_write_short */ 587 | return midi_write_short(midi, event); 588 | } 589 | 590 | static unsigned int midi_has_host_error(PmInternal *midi) 591 | { 592 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 593 | return (m->callback_error[0] != 0) || (m->error[0] != 0); 594 | } 595 | 596 | 597 | static void midi_get_host_error(PmInternal *midi, char *msg, unsigned int len) 598 | { 599 | midi_macosxcm_type m = (midi_macosxcm_type) midi->descriptor; 600 | msg[0] = 0; /* initialize to empty string */ 601 | if (m) { /* make sure there is an open device to examine */ 602 | if (m->error[0]) { 603 | strncpy(msg, m->error, len); 604 | m->error[0] = 0; /* clear the error */ 605 | } else if (m->callback_error[0]) { 606 | strncpy(msg, m->callback_error, len); 607 | m->callback_error[0] = 0; /* clear the error */ 608 | } 609 | msg[len - 1] = 0; /* make sure string is terminated */ 610 | } 611 | } 612 | 613 | 614 | MIDITimeStamp timestamp_pm_to_cm(PmTimestamp timestamp) 615 | { 616 | UInt64 nanos; 617 | if (timestamp <= 0) { 618 | return (MIDITimeStamp)0; 619 | } else { 620 | nanos = (UInt64)timestamp * (UInt64)1000000; 621 | return (MIDITimeStamp)AudioConvertNanosToHostTime(nanos); 622 | } 623 | } 624 | 625 | PmTimestamp timestamp_cm_to_pm(MIDITimeStamp timestamp) 626 | { 627 | UInt64 nanos; 628 | nanos = AudioConvertHostTimeToNanos(timestamp); 629 | return (PmTimestamp)(nanos / (UInt64)1000000); 630 | } 631 | 632 | 633 | // 634 | // Code taken from http://developer.apple.com/qa/qa2004/qa1374.html 635 | ////////////////////////////////////// 636 | // Obtain the name of an endpoint without regard for whether it has connections. 637 | // The result should be released by the caller. 638 | CFStringRef EndpointName(MIDIEndpointRef endpoint, bool isExternal) 639 | { 640 | CFMutableStringRef result = CFStringCreateMutable(NULL, 0); 641 | CFStringRef str; 642 | 643 | // begin with the endpoint's name 644 | str = NULL; 645 | MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &str); 646 | if (str != NULL) { 647 | CFStringAppend(result, str); 648 | CFRelease(str); 649 | } 650 | 651 | MIDIEntityRef entity = NULL; 652 | MIDIEndpointGetEntity(endpoint, &entity); 653 | if (entity == NULL) 654 | // probably virtual 655 | return result; 656 | 657 | if (CFStringGetLength(result) == 0) { 658 | // endpoint name has zero length -- try the entity 659 | str = NULL; 660 | MIDIObjectGetStringProperty(entity, kMIDIPropertyName, &str); 661 | if (str != NULL) { 662 | CFStringAppend(result, str); 663 | CFRelease(str); 664 | } 665 | } 666 | // now consider the device's name 667 | MIDIDeviceRef device = NULL; 668 | MIDIEntityGetDevice(entity, &device); 669 | if (device == NULL) 670 | return result; 671 | 672 | str = NULL; 673 | MIDIObjectGetStringProperty(device, kMIDIPropertyName, &str); 674 | if (CFStringGetLength(result) == 0) { 675 | CFRelease(result); 676 | return str; 677 | } 678 | if (str != NULL) { 679 | // if an external device has only one entity, throw away 680 | // the endpoint name and just use the device name 681 | if (isExternal && MIDIDeviceGetNumberOfEntities(device) < 2) { 682 | CFRelease(result); 683 | return str; 684 | } else { 685 | if (CFStringGetLength(str) == 0) { 686 | CFRelease(str); 687 | return result; 688 | } 689 | // does the entity name already start with the device name? 690 | // (some drivers do this though they shouldn't) 691 | // if so, do not prepend 692 | if (CFStringCompareWithOptions( result, /* endpoint name */ 693 | str /* device name */, 694 | CFRangeMake(0, CFStringGetLength(str)), 0) != kCFCompareEqualTo) { 695 | // prepend the device name to the entity name 696 | if (CFStringGetLength(result) > 0) 697 | CFStringInsert(result, 0, CFSTR(" ")); 698 | CFStringInsert(result, 0, str); 699 | } 700 | CFRelease(str); 701 | } 702 | } 703 | return result; 704 | } 705 | 706 | 707 | // Obtain the name of an endpoint, following connections. 708 | // The result should be released by the caller. 709 | static CFStringRef ConnectedEndpointName(MIDIEndpointRef endpoint) 710 | { 711 | CFMutableStringRef result = CFStringCreateMutable(NULL, 0); 712 | CFStringRef str; 713 | OSStatus err; 714 | int i; 715 | 716 | // Does the endpoint have connections? 717 | CFDataRef connections = NULL; 718 | int nConnected = 0; 719 | bool anyStrings = false; 720 | err = MIDIObjectGetDataProperty(endpoint, kMIDIPropertyConnectionUniqueID, &connections); 721 | if (connections != NULL) { 722 | // It has connections, follow them 723 | // Concatenate the names of all connected devices 724 | nConnected = CFDataGetLength(connections) / sizeof(MIDIUniqueID); 725 | if (nConnected) { 726 | const SInt32 *pid = (const SInt32 *)(CFDataGetBytePtr(connections)); 727 | for (i = 0; i < nConnected; ++i, ++pid) { 728 | MIDIUniqueID id = EndianS32_BtoN(*pid); 729 | MIDIObjectRef connObject; 730 | MIDIObjectType connObjectType; 731 | err = MIDIObjectFindByUniqueID(id, &connObject, &connObjectType); 732 | if (err == noErr) { 733 | if (connObjectType == kMIDIObjectType_ExternalSource || 734 | connObjectType == kMIDIObjectType_ExternalDestination) { 735 | // Connected to an external device's endpoint (10.3 and later). 736 | str = EndpointName((MIDIEndpointRef)(connObject), true); 737 | } else { 738 | // Connected to an external device (10.2) (or something else, catch-all) 739 | str = NULL; 740 | MIDIObjectGetStringProperty(connObject, kMIDIPropertyName, &str); 741 | } 742 | if (str != NULL) { 743 | if (anyStrings) 744 | CFStringAppend(result, CFSTR(", ")); 745 | else anyStrings = true; 746 | CFStringAppend(result, str); 747 | CFRelease(str); 748 | } 749 | } 750 | } 751 | } 752 | CFRelease(connections); 753 | } 754 | if (anyStrings) 755 | return result; 756 | 757 | // Here, either the endpoint had no connections, or we failed to obtain names for any of them. 758 | return EndpointName(endpoint, false); 759 | } 760 | 761 | 762 | char* cm_get_full_endpoint_name(MIDIEndpointRef endpoint) 763 | { 764 | #ifdef OLDCODE 765 | MIDIEntityRef entity; 766 | MIDIDeviceRef device; 767 | 768 | CFStringRef endpointName = NULL; 769 | CFStringRef deviceName = NULL; 770 | #endif 771 | CFStringRef fullName = NULL; 772 | CFStringEncoding defaultEncoding; 773 | char* newName; 774 | 775 | /* get the default string encoding */ 776 | defaultEncoding = CFStringGetSystemEncoding(); 777 | 778 | fullName = ConnectedEndpointName(endpoint); 779 | 780 | #ifdef OLDCODE 781 | /* get the entity and device info */ 782 | MIDIEndpointGetEntity(endpoint, &entity); 783 | MIDIEntityGetDevice(entity, &device); 784 | 785 | /* create the nicely formated name */ 786 | MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName); 787 | MIDIObjectGetStringProperty(device, kMIDIPropertyName, &deviceName); 788 | if (deviceName != NULL) { 789 | fullName = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@: %@"), 790 | deviceName, endpointName); 791 | } else { 792 | fullName = endpointName; 793 | } 794 | #endif 795 | /* copy the string into our buffer */ 796 | newName = (char *) malloc(CFStringGetLength(fullName) + 1); 797 | CFStringGetCString(fullName, newName, CFStringGetLength(fullName) + 1, 798 | defaultEncoding); 799 | 800 | /* clean up */ 801 | #ifdef OLDCODE 802 | if (endpointName) CFRelease(endpointName); 803 | if (deviceName) CFRelease(deviceName); 804 | #endif 805 | if (fullName) CFRelease(fullName); 806 | 807 | return newName; 808 | } 809 | 810 | 811 | 812 | pm_fns_node pm_macosx_in_dictionary = { 813 | none_write_short, 814 | none_sysex, 815 | none_sysex, 816 | none_write_byte, 817 | none_write_short, 818 | none_write_flush, 819 | none_synchronize, 820 | midi_in_open, 821 | midi_abort, 822 | midi_in_close, 823 | success_poll, 824 | midi_has_host_error, 825 | midi_get_host_error, 826 | }; 827 | 828 | pm_fns_node pm_macosx_out_dictionary = { 829 | midi_write_short, 830 | midi_begin_sysex, 831 | midi_end_sysex, 832 | midi_write_byte, 833 | midi_write_realtime, 834 | midi_write_flush, 835 | midi_synchronize, 836 | midi_out_open, 837 | midi_abort, 838 | midi_out_close, 839 | success_poll, 840 | midi_has_host_error, 841 | midi_get_host_error, 842 | }; 843 | 844 | 845 | PmError pm_macosxcm_init(void) 846 | { 847 | ItemCount numInputs, numOutputs, numDevices; 848 | MIDIEndpointRef endpoint; 849 | int i; 850 | OSStatus macHostError; 851 | char *error_text; 852 | 853 | /* Determine the number of MIDI devices on the system */ 854 | numDevices = MIDIGetNumberOfDevices(); 855 | numInputs = MIDIGetNumberOfSources(); 856 | numOutputs = MIDIGetNumberOfDestinations(); 857 | 858 | /* Return prematurely if no devices exist on the system 859 | Note that this is not an error. There may be no devices. 860 | Pm_CountDevices() will return zero, which is correct and 861 | useful information 862 | */ 863 | if (numDevices <= 0) { 864 | return pmNoError; 865 | } 866 | 867 | 868 | /* Initialize the client handle */ 869 | macHostError = MIDIClientCreate(CFSTR("PortMidi"), NULL, NULL, &client); 870 | if (macHostError != noErr) { 871 | error_text = "MIDIClientCreate() in pm_macosxcm_init()"; 872 | goto error_return; 873 | } 874 | 875 | /* Create the input port */ 876 | macHostError = MIDIInputPortCreate(client, CFSTR("Input port"), readProc, 877 | NULL, &portIn); 878 | if (macHostError != noErr) { 879 | error_text = "MIDIInputPortCreate() in pm_macosxcm_init()"; 880 | goto error_return; 881 | } 882 | 883 | /* Create the output port */ 884 | macHostError = MIDIOutputPortCreate(client, CFSTR("Output port"), &portOut); 885 | if (macHostError != noErr) { 886 | error_text = "MIDIOutputPortCreate() in pm_macosxcm_init()"; 887 | goto error_return; 888 | } 889 | 890 | /* Iterate over the MIDI input devices */ 891 | for (i = 0; i < numInputs; i++) { 892 | endpoint = MIDIGetSource(i); 893 | if (endpoint == NULL) { 894 | continue; 895 | } 896 | 897 | /* set the first input we see to the default */ 898 | if (pm_default_input_device_id == -1) 899 | pm_default_input_device_id = pm_descriptor_index; 900 | 901 | /* Register this device with PortMidi */ 902 | pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), 903 | TRUE, (void*)endpoint, &pm_macosx_in_dictionary); 904 | } 905 | 906 | /* Iterate over the MIDI output devices */ 907 | for (i = 0; i < numOutputs; i++) { 908 | endpoint = MIDIGetDestination(i); 909 | if (endpoint == NULL) { 910 | continue; 911 | } 912 | 913 | /* set the first output we see to the default */ 914 | if (pm_default_output_device_id == -1) 915 | pm_default_output_device_id = pm_descriptor_index; 916 | 917 | /* Register this device with PortMidi */ 918 | pm_add_device("CoreMIDI", cm_get_full_endpoint_name(endpoint), 919 | FALSE, (void*)endpoint, &pm_macosx_out_dictionary); 920 | } 921 | return pmNoError; 922 | 923 | error_return: 924 | pm_hosterror = macHostError; 925 | sprintf(pm_hosterror_text, "Host error %ld: %s\n", macHostError, error_text); 926 | pm_macosxcm_term(); /* clear out any opened ports */ 927 | return pmHostError; 928 | } 929 | 930 | void pm_macosxcm_term(void) 931 | { 932 | if (client != NULL) MIDIClientDispose(client); 933 | if (portIn != NULL) MIDIPortDispose(portIn); 934 | if (portOut != NULL) MIDIPortDispose(portOut); 935 | } 936 | -------------------------------------------------------------------------------- /portmidi/pm_mac/pmmacosxcm.h: -------------------------------------------------------------------------------- 1 | /* system-specific definitions */ 2 | 3 | PmError pm_macosxcm_init(void); 4 | void pm_macosxcm_term(void); -------------------------------------------------------------------------------- /portmidi/pm_win/pmwin.c: -------------------------------------------------------------------------------- 1 | /* pmwin.c -- PortMidi os-dependent code */ 2 | 3 | /* This file only needs to implement: 4 | pm_init(), which calls various routines to register the 5 | available midi devices, 6 | Pm_GetDefaultInputDeviceID(), and 7 | Pm_GetDefaultOutputDeviceID(). 8 | This file must 9 | be separate from the main portmidi.c file because it is system 10 | dependent, and it is separate from, say, pmwinmm.c, because it 11 | might need to register devices for winmm, directx, and others. 12 | */ 13 | 14 | #include "stdlib.h" 15 | #include "portmidi.h" 16 | #include "pmutil.h" 17 | #include "pminternal.h" 18 | #include "pmwinmm.h" 19 | #ifdef USE_DLL_FOR_CLEANUP 20 | #include "pmdll.h" /* used to close ports on exit */ 21 | #endif 22 | #ifdef DEBUG 23 | #include "stdio.h" 24 | #endif 25 | 26 | /* pm_exit is called when the program exits. 27 | It calls pm_term to make sure PortMidi is properly closed. 28 | If DEBUG is on, we prompt for input to avoid losing error messages. 29 | */ 30 | static void pm_exit(void) { 31 | pm_term(); 32 | #ifdef DEBUG 33 | #define STRING_MAX 80 34 | { 35 | char line[STRING_MAX]; 36 | printf("Type ENTER...\n"); 37 | /* note, w/o this prompting, client console application can not see one 38 | of its errors before closing. */ 39 | fgets(line, STRING_MAX, stdin); 40 | } 41 | #endif 42 | } 43 | 44 | 45 | /* pm_init is the windows-dependent initialization.*/ 46 | void pm_init(void) 47 | { 48 | #ifdef USE_DLL_FOR_CLEANUP 49 | /* we were hoping a DLL could offer more robust cleanup after errors, 50 | but the DLL does not seem to run after crashes. Thus, the atexit() 51 | mechanism is just as powerful, and simpler to implement. 52 | */ 53 | pm_set_close_function(pm_exit); 54 | #ifdef DEBUG 55 | printf("registered pm_term with cleanup DLL\n"); 56 | #endif 57 | #else 58 | atexit(pm_exit); 59 | #ifdef DEBUG 60 | printf("registered pm_exit with atexit()\n"); 61 | #endif 62 | #endif 63 | pm_winmm_init(); 64 | /* initialize other APIs (DirectX?) here */ 65 | } 66 | 67 | 68 | void pm_term(void) { 69 | pm_winmm_term(); 70 | } 71 | 72 | 73 | PmDeviceID Pm_GetDefaultInputDeviceID() { 74 | /* This routine should check the environment and the registry 75 | as specified in portmidi.h, but for now, it just returns 76 | the first device of the proper input/output flavor. 77 | */ 78 | int i; 79 | Pm_Initialize(); /* make sure descriptors exist! */ 80 | for (i = 0; i < pm_descriptor_index; i++) { 81 | if (descriptors[i].pub.input) { 82 | return i; 83 | } 84 | } 85 | return pmNoDevice; 86 | } 87 | 88 | PmDeviceID Pm_GetDefaultOutputDeviceID() { 89 | /* This routine should check the environment and the registry 90 | as specified in portmidi.h, but for now, it just returns 91 | the first device of the proper input/output flavor. 92 | */ 93 | int i; 94 | Pm_Initialize(); /* make sure descriptors exist! */ 95 | for (i = 0; i < pm_descriptor_index; i++) { 96 | if (descriptors[i].pub.output) { 97 | return i; 98 | } 99 | } 100 | return pmNoDevice; 101 | return 0; 102 | } 103 | 104 | #include "stdio.h" 105 | 106 | void *pm_alloc(size_t s) { 107 | return malloc(s); 108 | } 109 | 110 | 111 | void pm_free(void *ptr) { 112 | free(ptr); 113 | } 114 | 115 | -------------------------------------------------------------------------------- /portmidi/pm_win/pmwinmm.h: -------------------------------------------------------------------------------- 1 | /* midiwin32.h -- system-specific definitions */ 2 | 3 | void pm_winmm_init( void ); 4 | void pm_winmm_term( void ); 5 | 6 | -------------------------------------------------------------------------------- /portmidi/porttime/porttime.c: -------------------------------------------------------------------------------- 1 | /* porttime.c -- portable API for millisecond timer */ 2 | 3 | /* There is no machine-independent implementation code to put here */ 4 | -------------------------------------------------------------------------------- /portmidi/porttime/porttime.h: -------------------------------------------------------------------------------- 1 | /* porttime.h -- portable interface to millisecond timer */ 2 | 3 | /* CHANGE LOG FOR PORTTIME 4 | 10-Jun-03 Mark Nelson & RBD 5 | boost priority of timer thread in ptlinux.c implementation 6 | */ 7 | 8 | /* Should there be a way to choose the source of time here? */ 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | 15 | typedef enum { 16 | ptNoError = 0, /* success */ 17 | ptHostError = -10000, /* a system-specific error occurred */ 18 | ptAlreadyStarted, /* cannot start timer because it is already started */ 19 | ptAlreadyStopped, /* cannot stop timer because it is already stopped */ 20 | ptInsufficientMemory /* memory could not be allocated */ 21 | } PtError; 22 | 23 | 24 | typedef long PtTimestamp; 25 | 26 | typedef void (PtCallback)( PtTimestamp timestamp, void *userData ); 27 | 28 | /* 29 | Pt_Start() starts a real-time service. 30 | 31 | resolution is the timer resolution in ms. The time will advance every 32 | resolution ms. 33 | 34 | callback is a function pointer to be called every resolution ms. 35 | 36 | userData is passed to callback as a parameter. 37 | 38 | return value: 39 | Upon success, returns ptNoError. See PtError for other values. 40 | */ 41 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData); 42 | 43 | /* 44 | Pt_Stop() stops the timer. 45 | 46 | return value: 47 | Upon success, returns ptNoError. See PtError for other values. 48 | */ 49 | PtError Pt_Stop(); 50 | 51 | /* 52 | Pt_Started() returns true iff the timer is running. 53 | */ 54 | int Pt_Started(); 55 | 56 | /* 57 | Pt_Time() returns the current time in ms. 58 | */ 59 | PtTimestamp Pt_Time(); 60 | 61 | /* 62 | Pt_Sleep() pauses, allowing other threads to run. 63 | 64 | duration is the length of the pause in ms. The true duration 65 | of the pause may be rounded to the nearest or next clock tick 66 | as determined by resolution in Pt_Start(). 67 | */ 68 | void Pt_Sleep(long duration); 69 | 70 | #ifdef __cplusplus 71 | } 72 | #endif 73 | -------------------------------------------------------------------------------- /portmidi/porttime/ptlinux.c: -------------------------------------------------------------------------------- 1 | /* ptlinux.c -- portable timer implementation for linux */ 2 | 3 | 4 | /* IMPLEMENTATION NOTES (by Mark Nelson): 5 | 6 | Unlike Windows, Linux has no system call to request a periodic callback, 7 | so if Pt_Start() receives a callback parameter, it must create a thread 8 | that wakes up periodically and calls the provided callback function. 9 | If running as superuser, use setpriority() to renice thread to -20. 10 | One could also set the timer thread to a real-time priority (SCHED_FIFO 11 | and SCHED_RR), but this is dangerous for This is necessary because 12 | if the callback hangs it'll never return. A more serious reason 13 | is that the current scheduler implementation busy-waits instead 14 | of sleeping when realtime threads request a sleep of <=2ms (as a way 15 | to get around the 10ms granularity), which means the thread would never 16 | let anyone else on the CPU. 17 | 18 | CHANGE LOG 19 | 20 | 18-Jul-03 Roger Dannenberg -- Simplified code to set priority of timer 21 | thread. Simplified implementation notes. 22 | 23 | */ 24 | /* stdlib, stdio, unistd, and sys/types were added because they appeared 25 | * in a Gentoo patch, but I'm not sure why they are needed. -RBD 26 | */ 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "porttime.h" 32 | #include "sys/time.h" 33 | #include "sys/resource.h" 34 | #include "sys/timeb.h" 35 | #include "pthread.h" 36 | 37 | #define TRUE 1 38 | #define FALSE 0 39 | 40 | static int time_started_flag = FALSE; 41 | static struct timeb time_offset = {0, 0, 0, 0}; 42 | static pthread_t pt_thread_pid; 43 | 44 | /* note that this is static data -- we only need one copy */ 45 | typedef struct { 46 | int id; 47 | int resolution; 48 | PtCallback *callback; 49 | void *userData; 50 | } pt_callback_parameters; 51 | 52 | static int pt_callback_proc_id = 0; 53 | 54 | static void *Pt_CallbackProc(void *p) 55 | { 56 | pt_callback_parameters *parameters = (pt_callback_parameters *) p; 57 | int mytime = 1; 58 | /* to kill a process, just increment the pt_callback_proc_id */ 59 | /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, 60 | parameters->id); */ 61 | if (geteuid() == 0) setpriority(PRIO_PROCESS, 0, -20); 62 | while (pt_callback_proc_id == parameters->id) { 63 | /* wait for a multiple of resolution ms */ 64 | struct timeval timeout; 65 | int delay = mytime++ * parameters->resolution - Pt_Time(); 66 | if (delay < 0) delay = 0; 67 | timeout.tv_sec = 0; 68 | timeout.tv_usec = delay * 1000; 69 | select(0, NULL, NULL, NULL, &timeout); 70 | (*(parameters->callback))(Pt_Time(), parameters->userData); 71 | } 72 | /* printf("Pt_CallbackProc exiting\n"); */ 73 | // free(parameters); 74 | return NULL; 75 | } 76 | 77 | 78 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) 79 | { 80 | if (time_started_flag) return ptNoError; 81 | ftime(&time_offset); /* need this set before process runs */ 82 | if (callback) { 83 | int res; 84 | pt_callback_parameters *parms = (pt_callback_parameters *) 85 | malloc(sizeof(pt_callback_parameters)); 86 | if (!parms) return ptInsufficientMemory; 87 | parms->id = pt_callback_proc_id; 88 | parms->resolution = resolution; 89 | parms->callback = callback; 90 | parms->userData = userData; 91 | res = pthread_create(&pt_thread_pid, NULL, 92 | Pt_CallbackProc, parms); 93 | if (res != 0) return ptHostError; 94 | } 95 | time_started_flag = TRUE; 96 | return ptNoError; 97 | } 98 | 99 | 100 | PtError Pt_Stop() 101 | { 102 | /* printf("Pt_Stop called\n"); */ 103 | pt_callback_proc_id++; 104 | pthread_join(pt_thread_pid, NULL); 105 | time_started_flag = FALSE; 106 | return ptNoError; 107 | } 108 | 109 | 110 | int Pt_Started() 111 | { 112 | return time_started_flag; 113 | } 114 | 115 | 116 | PtTimestamp Pt_Time() 117 | { 118 | long seconds, milliseconds; 119 | struct timeb now; 120 | ftime(&now); 121 | seconds = now.time - time_offset.time; 122 | milliseconds = now.millitm - time_offset.millitm; 123 | return seconds * 1000 + milliseconds; 124 | } 125 | 126 | 127 | void Pt_Sleep(long duration) 128 | { 129 | usleep(duration * 1000); 130 | } 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /portmidi/porttime/ptmacosx_mach.c: -------------------------------------------------------------------------------- 1 | /* ptmacosx.c -- portable timer implementation for mac os x */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #import 8 | #import 9 | #import 10 | #import 11 | #include 12 | 13 | #include "porttime.h" 14 | #include "sys/time.h" 15 | #include "pthread.h" 16 | 17 | #define NSEC_PER_MSEC 1000000 18 | #define THREAD_IMPORTANCE 30 19 | 20 | static int time_started_flag = FALSE; 21 | static UInt64 start_time; 22 | static pthread_t pt_thread_pid; 23 | 24 | /* note that this is static data -- we only need one copy */ 25 | typedef struct { 26 | int id; 27 | int resolution; 28 | PtCallback *callback; 29 | void *userData; 30 | } pt_callback_parameters; 31 | 32 | static int pt_callback_proc_id = 0; 33 | 34 | static void *Pt_CallbackProc(void *p) 35 | { 36 | pt_callback_parameters *parameters = (pt_callback_parameters *) p; 37 | int mytime = 1; 38 | 39 | kern_return_t error; 40 | thread_extended_policy_data_t extendedPolicy; 41 | thread_precedence_policy_data_t precedencePolicy; 42 | 43 | extendedPolicy.timeshare = 0; 44 | error = thread_policy_set(mach_thread_self(), THREAD_EXTENDED_POLICY, 45 | (thread_policy_t)&extendedPolicy, 46 | THREAD_EXTENDED_POLICY_COUNT); 47 | if (error != KERN_SUCCESS) { 48 | mach_error("Couldn't set thread timeshare policy", error); 49 | } 50 | 51 | precedencePolicy.importance = THREAD_IMPORTANCE; 52 | error = thread_policy_set(mach_thread_self(), THREAD_PRECEDENCE_POLICY, 53 | (thread_policy_t)&precedencePolicy, 54 | THREAD_PRECEDENCE_POLICY_COUNT); 55 | if (error != KERN_SUCCESS) { 56 | mach_error("Couldn't set thread precedence policy", error); 57 | } 58 | 59 | 60 | /* to kill a process, just increment the pt_callback_proc_id */ 61 | /* printf("pt_callback_proc_id %d, id %d\n", pt_callback_proc_id, parameters->id); */ 62 | while (pt_callback_proc_id == parameters->id) { 63 | /* wait for a multiple of resolution ms */ 64 | UInt64 wait_time; 65 | int delay = mytime++ * parameters->resolution - Pt_Time(); 66 | long timestamp; 67 | if (delay < 0) delay = 0; 68 | wait_time = AudioConvertNanosToHostTime((UInt64)delay * NSEC_PER_MSEC); 69 | wait_time += AudioGetCurrentHostTime(); 70 | error = mach_wait_until(wait_time); 71 | timestamp = Pt_Time(); 72 | (*(parameters->callback))(timestamp, parameters->userData); 73 | } 74 | free(parameters); 75 | return NULL; 76 | } 77 | 78 | 79 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) 80 | { 81 | if (time_started_flag) return ptAlreadyStarted; 82 | start_time = AudioGetCurrentHostTime(); 83 | 84 | if (callback) { 85 | int res; 86 | pt_callback_parameters *parms; 87 | 88 | parms = (pt_callback_parameters *) malloc(sizeof(pt_callback_parameters)); 89 | if (!parms) return ptInsufficientMemory; 90 | parms->id = pt_callback_proc_id; 91 | parms->resolution = resolution; 92 | parms->callback = callback; 93 | parms->userData = userData; 94 | res = pthread_create(&pt_thread_pid, NULL, Pt_CallbackProc, parms); 95 | if (res != 0) return ptHostError; 96 | } 97 | 98 | time_started_flag = TRUE; 99 | return ptNoError; 100 | } 101 | 102 | 103 | PtError Pt_Stop() 104 | { 105 | /* printf("Pt_Stop called\n"); */ 106 | pt_callback_proc_id++; 107 | time_started_flag = FALSE; 108 | return ptNoError; 109 | } 110 | 111 | 112 | int Pt_Started() 113 | { 114 | return time_started_flag; 115 | } 116 | 117 | 118 | PtTimestamp Pt_Time() 119 | { 120 | UInt64 clock_time, nsec_time; 121 | clock_time = AudioGetCurrentHostTime() - start_time; 122 | nsec_time = AudioConvertHostTimeToNanos(clock_time); 123 | return (PtTimestamp)(nsec_time / NSEC_PER_MSEC); 124 | } 125 | 126 | 127 | void Pt_Sleep(long duration) 128 | { 129 | usleep(duration * 1000); 130 | } 131 | -------------------------------------------------------------------------------- /portmidi/porttime/ptwinmm.c: -------------------------------------------------------------------------------- 1 | /* ptwinmm.c -- portable timer implementation for win32 */ 2 | 3 | 4 | #include "porttime.h" 5 | #include "windows.h" 6 | #include "time.h" 7 | 8 | 9 | TIMECAPS caps; 10 | 11 | static long time_offset = 0; 12 | static int time_started_flag = FALSE; 13 | static long time_resolution; 14 | static MMRESULT timer_id; 15 | static PtCallback *time_callback; 16 | 17 | void CALLBACK winmm_time_callback(UINT uID, UINT uMsg, DWORD_PTR dwUser, 18 | DWORD_PTR dw1, DWORD_PTR dw2) 19 | { 20 | (*time_callback)(Pt_Time(), (void *) dwUser); 21 | } 22 | 23 | 24 | PtError Pt_Start(int resolution, PtCallback *callback, void *userData) 25 | { 26 | if (time_started_flag) return ptAlreadyStarted; 27 | timeBeginPeriod(resolution); 28 | time_resolution = resolution; 29 | time_offset = timeGetTime(); 30 | time_started_flag = TRUE; 31 | time_callback = callback; 32 | if (callback) { 33 | timer_id = timeSetEvent(resolution, 1, winmm_time_callback, 34 | (DWORD_PTR) userData, TIME_PERIODIC | TIME_CALLBACK_FUNCTION); 35 | if (!timer_id) return ptHostError; 36 | } 37 | return ptNoError; 38 | } 39 | 40 | 41 | PtError Pt_Stop() 42 | { 43 | if (!time_started_flag) return ptAlreadyStopped; 44 | if (time_callback && timer_id) { 45 | timeKillEvent(timer_id); 46 | time_callback = NULL; 47 | timer_id = 0; 48 | } 49 | time_started_flag = FALSE; 50 | timeEndPeriod(time_resolution); 51 | return ptNoError; 52 | } 53 | 54 | 55 | int Pt_Started() 56 | { 57 | return time_started_flag; 58 | } 59 | 60 | 61 | PtTimestamp Pt_Time() 62 | { 63 | return timeGetTime() - time_offset; 64 | } 65 | 66 | 67 | void Pt_Sleep(long duration) 68 | { 69 | Sleep(duration); 70 | } 71 | -------------------------------------------------------------------------------- /stack.yaml: -------------------------------------------------------------------------------- 1 | 2 | resolver: lts-16.2 3 | 4 | packages: 5 | - '.' 6 | 7 | extra-deps: [] 8 | 9 | flags: {} 10 | 11 | extra-package-dbs: [] 12 | --------------------------------------------------------------------------------