├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── PRODATUM_VERSION ├── README.md ├── cmake └── config.h.in ├── include ├── Fl_Scope.h ├── cfg.h ├── data.h ├── debug.h ├── images.h ├── midi.h ├── pxk.h ├── ringbuffer.h └── widgets.h ├── lib └── .gitignore ├── prodatum.desktop ├── prodatum.fl ├── resources ├── a2k-size.png ├── arp-busy.png ├── arp-commit.png ├── arp-export.png ├── arp-exported.png ├── arp-import.png ├── arp-save-1.png ├── arp-save-2.png ├── arp-save-3.png ├── arp-save-4.png ├── cs-size.png ├── folder-structure.png ├── new-menu.png ├── p2k-size.png ├── preset-busy.png ├── preset-export.png ├── preset-exported.png ├── preset-import.png ├── prodatum.ico ├── prodatum.png ├── setup-export.png ├── setup-exported.png ├── setup-import.png └── windows.rc └── src ├── Fl_Scope.cpp ├── cfg.cpp ├── data.cpp ├── debug.cpp ├── midi.cpp ├── prodatum.cpp ├── pxk.cpp ├── ringbuffer.cpp └── widgets.cpp /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | custom: ["https://www.paypal.me/haxorhax", haxorhax.com] 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | workflow_dispatch: 5 | inputs: 6 | logLevel: 7 | description: 'Log level' 8 | required: true 9 | default: 'warning' 10 | tags: 11 | description: 'Manual' 12 | push: 13 | branches: [ "master", "dev" ] 14 | 15 | env: 16 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 17 | build_type: Release 18 | 19 | jobs: 20 | build: 21 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 22 | # You can convert this to a matrix build if you need cross-platform coverage. 23 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: [windows-latest, ubuntu-latest, macos-latest] 28 | 29 | steps: 30 | - name: Clone prodatum repository 31 | uses: actions/checkout@v3 32 | 33 | - name: Clone fltk repository 34 | uses: actions/checkout@v3 35 | with: 36 | repository: haxorhax/fltk 37 | path: lib/fltk 38 | 39 | - name: Clone portmidi repository 40 | uses: actions/checkout@v3 41 | with: 42 | repository: haxorhax/portmidi 43 | path: lib/portmidi 44 | 45 | - name: Install needed OS packages 46 | run: | 47 | if [ "$RUNNER_OS" == "Windows" ]; then 48 | echo "$RUNNER_OS supported" 49 | elif [ "$RUNNER_OS" == "Linux" ]; then 50 | sudo apt install libasound2-dev 51 | sudo apt install zlib1g-dev 52 | sudo apt install libpng-dev 53 | sudo apt install libgl1-mesa-dev 54 | sudo apt install libglu1-mesa-dev 55 | sudo apt install libx11-dev 56 | sudo apt install libxinerama-dev 57 | sudo apt install libxft-dev 58 | sudo apt install libfontconfig-dev 59 | elif [ "$RUNNER_OS" == "macOS" ]; then 60 | echo "$RUNNER_OS not supported yet" 61 | else 62 | echo "$RUNNER_OS not supported" 63 | exit 1 64 | fi 65 | shell: bash 66 | 67 | - name: Configure CMake 68 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 69 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 70 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.build_type}} 71 | 72 | - name: Build 73 | # Build your program with the given configuration 74 | run: cmake --build ${{github.workspace}}/build --config ${{env.build_type}} 75 | 76 | - name: Rename windows artifact 77 | if: ${{ matrix.os == 'windows-latest' }} 78 | run: mv ./build/${{env.build_type}}/prodatum.exe ./build/${{env.build_type}}/prodatum-win-x64.exe 79 | 80 | - name: Upload a windows build artifact 81 | if: ${{ matrix.os == 'windows-latest' }} 82 | uses: actions/upload-artifact@v3.1.0 83 | with: 84 | name: prodatum-${{ matrix.os }} 85 | path: ./build/${{env.build_type}}/prodatum-win-x64.exe 86 | 87 | - name: Rename linux artifact 88 | if: ${{ matrix.os == 'ubuntu-latest' }} 89 | run: mv ./build/prodatum ./build/prodatum-linux-x86_64 90 | 91 | - name: Upload a linux build artifact 92 | if: ${{ matrix.os == 'ubuntu-latest' }} 93 | uses: actions/upload-artifact@v3.1.0 94 | with: 95 | name: prodatum-${{ matrix.os }} 96 | path: ./build/prodatum-linux-x86_64 97 | 98 | - name: Rename macos artifact 99 | if: ${{ matrix.os == 'macos-latest' }} 100 | run: mv ./build/prodatum ./build/prodatum-macos-x64 101 | 102 | - name: Upload a macos build artifact 103 | if: ${{ matrix.os == 'macos-latest' }} 104 | uses: actions/upload-artifact@v3.1.0 105 | with: 106 | name: prodatum-${{ matrix.os }} 107 | path: ./build/prodatum-macos-x64 108 | 109 | release: 110 | name: Prepare release draft 111 | # Make sure (and wait until) the builds have succeeded 112 | needs: build 113 | runs-on: ubuntu-latest 114 | 115 | steps: 116 | - name: Check if tag exists 117 | if: ${{ !(github.event_name == 'workflow_dispatch') }} 118 | # Note the ! - it will return 0 if the command fails, and 1 otherwise 119 | run: '! git rev-parse "v${{steps.ref.outputs.version}}"' 120 | 121 | - name: Make release directory 122 | run: mkdir ./release 123 | 124 | # First, download all resulting assets from the previous steps. 125 | - name: Retrieve windows installers 126 | uses: actions/download-artifact@v2 127 | with: 128 | name: prodatum-windows-latest 129 | path: ./release 130 | 131 | - name: Retrieve macOS images 132 | uses: actions/download-artifact@v2 133 | with: 134 | name: prodatum-macos-latest 135 | path: ./release 136 | 137 | - name: Retrieve Linux installers 138 | uses: actions/download-artifact@v2 139 | with: 140 | name: prodatum-ubuntu-latest 141 | path: ./release 142 | 143 | # Now we are set, we have all five release assets on the VM. It's time to 144 | # create the SHA-checksums file and then upload everything! 145 | - name: Generate SHA256 checksums 146 | run: | 147 | cd ./release 148 | ls -al 149 | sha256sum "prodatum-win-x64.exe" > "SHA256SUMS.txt" 150 | sha256sum "prodatum-linux-x86_64" >> "SHA256SUMS.txt" 151 | sha256sum "prodatum-macos-x64" >> "SHA256SUMS.txt" 152 | cd .. 153 | 154 | - name: Verify checksums 155 | run: | 156 | cd ./release 157 | sha256sum -c SHA256SUMS.txt 158 | cd .. 159 | 160 | # OTHERWISE: Create a new release draft 161 | - name: Create release draft 162 | if: github.ref == 'refs/heads/master' 163 | uses: softprops/action-gh-release@v1 164 | env: 165 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 166 | with: 167 | # Populate the inputs of the release we already know 168 | tag_name: v${{steps.ref.outputs.version}} 169 | name: Release v${{steps.ref.outputs.version}} 170 | body: Changelog 171 | draft: true # Always create as draft, so that we can populate the remaining values easily 172 | files: | 173 | ./release/prodatum-win-x64.exe 174 | ./release/prodatum-linux-x86_64 175 | ./release/prodatum-macos-x64 176 | ./release/SHA256SUMS.txt 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | !.gitignore 2 | !*.H 3 | !*.C 4 | !prodatum.fl 5 | !prodatum.png 6 | !prodatum.ico 7 | !prodatum.desktop 8 | !windows.rc 9 | !config.h.in 10 | !CMakeLists.txt 11 | !README 12 | !build/ 13 | !include/ 14 | !lib/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required( VERSION 3.21 ) 2 | 3 | file( STRINGS "PRODATUM_VERSION" PRODATUM_VERSION_IN ) 4 | 5 | project( prodatum VERSION ${PRODATUM_VERSION_IN} ) 6 | 7 | set( PRODATUM_NAME ${PROJECT_NAME} ) 8 | set( PRODATUM_VERSION ${PROJECT_VERSION} ) 9 | set( PRODATUM_VERSION_MAJOR ${PROJECT_VERSION_MAJOR} ) 10 | set( PRODATUM_VERSION_MINOR ${PROJECT_VERSION_MINOR} ) 11 | set( PRODATUM_VERSION_PATCH ${PROJECT_VERSION_PATCH} ) 12 | 13 | message( "prodatum name : [${PRODATUM_NAME}]" ) 14 | message( "prodatum version: [${PRODATUM_VERSION}]" ) 15 | 16 | if( NOT CMAKE_BUILD_TYPE ) 17 | SET( CMAKE_BUILD_TYPE Release ) 18 | endif() 19 | 20 | macro( SET_OPTION option value ) 21 | set( ${option} ${value} CACHE INTERNAL "" FORCE ) 22 | endmacro() 23 | 24 | SET_OPTION( BUILD_SHARED_LIBS OFF ) 25 | SET_OPTION( OPTION_BUILD_SHARED_LIBS OFF ) 26 | SET_OPTION( FLTK_BUILD_TEST OFF ) 27 | SET_OPTION( FLTK_BUILD_EXAMPLES OFF ) 28 | 29 | set( SYSEX_MAX_SIZE 1024 ) 30 | set( RINGBUFFER_WRITE 2048 ) 31 | set( RINGBUFFER_READ 2048 ) 32 | set( PATH_MAX 1024 ) 33 | set( LOG_BUFFER_SIZE 1048576 ) 34 | set( RES_FILES "" ) 35 | 36 | if( WIN32 ) 37 | set( FLUID_EXECUTABLE "${CMAKE_BINARY_DIR}/lib/fltk/bin/${CMAKE_BUILD_TYPE}/fluid.exe" ) 38 | else() 39 | set( FLUID_EXECUTABLE "${CMAKE_BINARY_DIR}/lib/fltk/bin/fluid" ) 40 | endif( WIN32 ) 41 | 42 | set( UI_AUTOGEN "${CMAKE_BINARY_DIR}/ui.cpp" ) 43 | 44 | CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/cmake/config.h.in ${CMAKE_SOURCE_DIR}/include/config.h ) 45 | 46 | option( BUILD_LIBS "Enable building libs" ON ) 47 | 48 | if( BUILD_LIBS ) 49 | add_subdirectory( ${CMAKE_SOURCE_DIR}/lib/portmidi ) 50 | add_subdirectory( ${CMAKE_SOURCE_DIR}/lib/fltk ) 51 | endif( BUILD_LIBS ) 52 | 53 | add_custom_command( 54 | OUTPUT ${UI_AUTOGEN} 55 | PRE_BUILD 56 | COMMAND "${FLUID_EXECUTABLE}" 57 | "-c" 58 | "${CMAKE_SOURCE_DIR}/prodatum.fl" 59 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 60 | DEPENDS fluid 61 | ) 62 | 63 | include_directories ( 64 | ${CMAKE_SOURCE_DIR} 65 | ${CMAKE_SOURCE_DIR}/include 66 | ${CMAKE_SOURCE_DIR}/lib/portmidi/pm_common 67 | ${CMAKE_SOURCE_DIR}/lib/portmidi/porttime 68 | ${CMAKE_SOURCE_DIR}/lib/fltk 69 | ${CMAKE_BINARY_DIR} 70 | ${CMAKE_BINARY_DIR}/include 71 | ${CMAKE_BINARY_DIR}/lib/fltk 72 | ) 73 | 74 | link_directories ( 75 | ${CMAKE_SOURCE_DIR}/lib 76 | ${CMAKE_BINARY_DIR}/lib/fltk/lib 77 | ${CMAKE_BINARY_DIR}/lib/fltk/lib/${CMAKE_BUILD_TYPE} 78 | ${CMAKE_BINARY_DIR}/lib/portmidi 79 | ${CMAKE_BINARY_DIR}/lib/portmidi/${CMAKE_BUILD_TYPE} 80 | ) 81 | 82 | if ( WIN32 ) 83 | set( RES_FILES "resources/windows.rc" ) 84 | endif ( WIN32 ) 85 | 86 | if ( MINGW ) 87 | set( CMAKE_RC_COMPILER_INIT windres ) 88 | ENABLE_LANGUAGE(RC) 89 | SET( CMAKE_RC_COMPILE_OBJECT " -O coff -i -o " ) 90 | endif( MINGW ) 91 | 92 | set ( SOURCES 93 | src/cfg.cpp 94 | src/data.cpp 95 | src/debug.cpp 96 | src/Fl_Scope.cpp 97 | src/midi.cpp 98 | src/prodatum.cpp 99 | src/pxk.cpp 100 | src/ringbuffer.cpp 101 | src/widgets.cpp 102 | ) 103 | 104 | add_executable ( prodatum ${SOURCES} ${UI_AUTOGEN} ${RES_FILES} ) 105 | 106 | # Build for Windows 107 | if( WIN32 ) 108 | # using gcc 109 | if( "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" ) 110 | ADD_DEFINITIONS( -DWIN32 -DSYNCLOG -Wall -W -Wno-char-subscripts -Wno-write-strings -Wno-format ) 111 | set( CMAKE_EXE_LINKER_FLAGS "-mwindows -static-libgcc -static-libstdc++" ) 112 | set( ADDITIONAL_LIBRARIES winmm ole32 uuid comctl32 wsock32 ) 113 | 114 | # using visual studio 115 | elseif( MSVC ) 116 | ADD_DEFINITIONS( -DWIN32 -DSYNCLOG -D_CRT_SECURE_NO_WARNINGS ) 117 | set( ADDITIONAL_LIBRARIES winmm ole32 uuid comctl32 wsock32 gdi32 ) 118 | 119 | target_link_options(prodatum PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup") 120 | endif() 121 | endif( WIN32 ) 122 | 123 | # Build for MacOS 124 | if( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) 125 | ADD_DEFINITIONS( -std=c++14 -DUSE_MLOCK -DOSX -DSYNCLOG -Wall -W -Wno-char-subscripts -Wno-write-strings ) 126 | set( ADDITIONAL_LIBRARIES "-framework Carbon -framework Cocoa -framework ApplicationServices -framework CoreMIDI -framework CoreAudio" ) 127 | endif( ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) 128 | 129 | # Build for Linux 130 | if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" ) 131 | ADD_DEFINITIONS( -std=c++14 -DUSE_MLOCK -D__linux -DSYNCLOG -Wall -W -Wno-char-subscripts -Wno-write-strings ) 132 | set( CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++" ) 133 | set( ADDITIONAL_LIBRARIES X11 Xinerama Xft fontconfig asound pthread ) 134 | endif( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" ) 135 | 136 | target_include_directories( prodatum 137 | PRIVATE 138 | ${CMAKE_BUILD_DIR}/lib/fltk/lib/${CMAKE_BUILD_TYPE} 139 | ${CMAKE_BUILD_DIR}/lib/portmidi/${CMAKE_BUILD_TYPE} 140 | ) 141 | 142 | target_link_libraries ( prodatum fltk portmidi ${ADDITIONAL_LIBRARIES} ) 143 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | -------------------------------------------------------------------------------- /PRODATUM_VERSION: -------------------------------------------------------------------------------- 1 | 2.1.2 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # **prodatum - E-MU Proteus sysex editor** 2 | 3 | ### Allows easy editing of presets for the E-MU Proteus family of synthesizers. 4 | #### Specifically: 5 | - Proteus 1000/2000/2500 racks 6 | - Command Station (MP-7, XL-7, PX-7) 7 | - Audity 2000 8 |
9 |
10 | 11 | 12 | # ***Getting Started*** 13 | 14 | ### **Setting up prodatum for the first time:** 15 | 16 | - Choose the version of [Release](https://github.com/haxorhax/prodatum/releases) from the deployment page 17 | 18 | - Download the corresponding executable for your OS *or* build from source (steps below) 19 | 20 | - The executable is built portable, and you should create a ***prodatum_config*** folder alongside the executable to store the sync files 21 | 22 | - Done! 23 | 24 | > Note to linux users: a prodatum.desktop file is provided in the repo 25 | 26 |
27 |
28 | 29 | 30 | # ***Build Process*** 31 | 32 | ### **Build Process -- [ git, CMake, FLTK, portmidi ]** 33 | ```bash 34 | git clone https://github.com/haxorhax/prodatum 35 | git clone https://github.com/haxorhax/fltk prodatum/lib/fltk 36 | git clone https://github.com/haxorhax/portmidi prodatum/lib/portmidi 37 | cd prodatum 38 | cmake -S . -B build 39 | cmake --build build --config Release 40 | ``` 41 |
42 |
43 | 44 | 45 | # ***Version Info*** 46 | ## *Version 2.1.2 - 10/16/22* 47 | > Support for more than 300 arps (DRUM expansion has 400) 48 | > Dumping more than 512 presets requires load to edit buffer first 49 | > Command Station sync bug fix 50 | #### See wiki for details -> [New features v2.1](https://github.com/haxorhax/prodatum/wiki/New-features-v2.1) 51 | ###### Update provided by haxorhax (https://haxorhax.com) 52 |
53 | 54 | ## *Version 2.1.1 - 8/28/22* 55 | > Project redecorating and versioning, no functional changes 56 | #### See wiki for details -> [New features v2.1](https://github.com/haxorhax/prodatum/wiki/New-features-v2.1) 57 | ###### Update provided by haxorhax (https://haxorhax.com) 58 |
59 | 60 | ## *Version 2.1.0 - 8/26/22* 61 | > Added bulk import/export functionality 62 | #### See wiki for details -> [New features v2.1](https://github.com/haxorhax/prodatum/wiki/New-features-v2.1) 63 | ###### Update provided by haxorhax (https://haxorhax.com) 64 |
65 | 66 | ## *Version 2.0.2 - 8/21/22* 67 | > Resurrected repository. Enhanced cmake build process, and added github action support. 68 | ###### Update provided by haxorhax (https://haxorhax.com) 69 |
70 | 71 | ## *Versions 2.0.1 and older* 72 | > Last updated on 1/31/2015. 73 | #### Sourceforge repository: https://sourceforge.net/projects/prodatum 74 | ###### Original design by Jan Mann (aka Jan Eidtmann, rdxesy@yahoo.de) 75 | ###### *Copyright (C) 2014 by Jan Eidtmann* 76 | 77 | 78 | -------------------------------------------------------------------------------- /cmake/config.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #cmakedefine PRODATUM_NAME "@PRODATUM_NAME@" 4 | 5 | #cmakedefine PRODATUM_VERSION "@PRODATUM_VERSION@" 6 | 7 | #cmakedefine PRODATUM_VERSION_MAJOR @PRODATUM_VERSION_MAJOR@ 8 | #cmakedefine PRODATUM_VERSION_MINOR @PRODATUM_VERSION_MINOR@ 9 | #cmakedefine PRODATUM_VERSION_PATCH @PRODATUM_VERSION_PATCH@ 10 | 11 | #cmakedefine SYSEX_MAX_SIZE @SYSEX_MAX_SIZE@ 12 | #cmakedefine RINGBUFFER_WRITE @RINGBUFFER_WRITE@ 13 | #cmakedefine RINGBUFFER_READ @RINGBUFFER_READ@ 14 | #cmakedefine LOG_BUFFER_SIZE @LOG_BUFFER_SIZE@ 15 | 16 | #ifndef PATH_MAX 17 | #cmakedefine PATH_MAX @PATH_MAX@ 18 | #endif 19 | -------------------------------------------------------------------------------- /include/Fl_Scope.h: -------------------------------------------------------------------------------- 1 | #ifndef Fl_Scope_H 2 | #define Fl_Scope_H 3 | /*********************************************************** 4 | * Fl_Scope.h 5 | * 6 | * Author: Michael Pearce 7 | * 8 | * Started: 1 August 2003 9 | * 10 | * Copyright: Copyright 2003 Michael Pearce All Rights reserved. 11 | * 12 | * Licence: GNU/GPL 13 | * 14 | * This program is free software; you can redistribute it and/or 15 | * modify it under the terms of the GNU General Public License 16 | * as published by the Free Software Foundation; either version 2 17 | * of the License, or (at your option) any later version. 18 | * 19 | * This program is distributed in the hope that it will be useful, 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 | * GNU General Public License for more details. 23 | * 24 | * You should have received a copy of the GNU General Public License 25 | * along with this program (GNU.txt); if not, write to the Free Software 26 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 27 | * or visit http://www.gnu.org/licenses/licenses.html 28 | 29 | * 30 | ************************************************************ 31 | * See Fl_Scope.cxx for Version Information 32 | ***********************************************************/ 33 | 34 | #ifndef Fl_Widget_H 35 | #include 36 | #endif 37 | 38 | 39 | #define FL_SCOPE_TRACE_SCROLL 0 40 | #define FL_SCOPE_TRACE_LOOP 1 41 | #define FL_SCOPE_TRACE_LOOP_CLEAR 2 42 | 43 | #define FL_SCOPE_REDRAW_OFF 0 44 | #define FL_SCOPE_REDRAW_FULL 1 45 | #define FL_SCOPE_REDRAW_ALWAYS 2 46 | 47 | #define FL_SCOPE_DOT 0 48 | #define FL_SCOPE_LINE 1 49 | 50 | #define FL_SCOPE_SIGNED 0 51 | #define FL_SCOPE_UNSIGNED 1 52 | 53 | class FL_EXPORT Fl_Scope : public Fl_Widget 54 | { 55 | int _x,_y,_w,_h; /* The draw position */ 56 | 57 | //unsigned char *ScopeData; /* Pointer to dynamic array of track info */ 58 | int *ScopeData; /* Pointer to dynamic array of track info */ 59 | 60 | int ScopeDataSize; 61 | int ScopeDataPos; 62 | 63 | Fl_Color _TraceColour; /* Trace Colour */ 64 | Fl_Color _BackColour; /* Background Colour */ 65 | 66 | int TraceType; 67 | int RedrawMode; 68 | int LineType; 69 | int DataType; 70 | 71 | protected: 72 | 73 | void draw(int,int,int,int); 74 | 75 | int handle(int,int,int,int,int); 76 | 77 | void draw(); 78 | 79 | 80 | /* These are protected because changing the size screws up the buffer */ 81 | /* May Fix this problem later */ 82 | void x(int X){ _x=X;}; 83 | void y(int Y){ _y=Y;}; 84 | void w(int W){ _w=W;}; 85 | void h(int H){ _h=H;}; 86 | 87 | 88 | 89 | public: 90 | 91 | 92 | int x(){return _x;}; 93 | //void x(int X){ _x=X;}; 94 | 95 | int y(){return _y;}; 96 | //void y(int Y){ _y=Y;}; 97 | 98 | int w(){return _w;}; 99 | //void w(int W){ _w=W;}; 100 | 101 | int h(){return _h;}; 102 | //void h(int H){ _h=H;}; 103 | 104 | 105 | int tracetype(){return TraceType;}; 106 | void tracetype(int t){TraceType=t;}; 107 | 108 | int redrawmode(){return RedrawMode;}; 109 | void redrawmode(int t){RedrawMode=t;}; 110 | 111 | int linetype(){return LineType;}; 112 | void linetype(int t){LineType=t;}; 113 | 114 | 115 | int datatype(){return DataType;}; 116 | void datatype(int t){DataType=t;}; 117 | 118 | 119 | //int Add(unsigned char); /* Add Data to Scope */ 120 | int Add(int); /* Add Data to Scope */ 121 | 122 | Fl_Color TraceColour(){return _TraceColour;}; 123 | void TraceColour(Fl_Color c){_TraceColour=c;}; 124 | 125 | Fl_Color BackColour(){return _BackColour;}; 126 | void BackColour(Fl_Color c){_BackColour=c;}; 127 | 128 | virtual int handle(int); 129 | Fl_Scope(int,int,int,int,const char * = 0); 130 | ~Fl_Scope(); 131 | }; 132 | 133 | 134 | /**************** END OF FILE ******************************/ 135 | #endif 136 | 137 | 138 | -------------------------------------------------------------------------------- /include/cfg.h: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | #ifndef CFG_H_ 3 | #define CFG_H_ 4 | /** 5 | \defgroup pd_cfg prodatum Configurations 6 | @{ 7 | */ 8 | #ifdef WIN32 9 | # include 10 | # define mkdir(x,m) _mkdir(x) 11 | #else 12 | # define mkdir(x,m) mkdir(x,m) 13 | #endif 14 | 15 | #include "config.h" 16 | #define MAX_ARPS 400 17 | #define MAX_RIFFS 1000 18 | #include 19 | 20 | /** 21 | * Enum of config options 22 | */ 23 | enum CONFIG 24 | { 25 | CFG_MIDI_OUT, 26 | CFG_MIDI_IN, 27 | CFG_MIDI_THRU, 28 | CFG_CONTROL_CHANNEL, 29 | CFG_AUTOMAP, 30 | CFG_DEVICE_ID, 31 | CFG_MASTER_VOLUME, 32 | CFG_SPEED, 33 | CFG_CLOSED_LOOP_UPLOAD, 34 | CFG_CLOSED_LOOP_DOWNLOAD, 35 | CFG_TOOLTIPS, 36 | CFG_KNOBMODE, 37 | CFG_CONFIRM_EXIT, 38 | CFG_CONFIRM_RAND, 39 | CFG_CONFIRM_DISMISS, 40 | CFG_SYNCVIEW, 41 | CFG_DRLS, 42 | CFG_LOG_SYSEX_OUT, 43 | CFG_LOG_SYSEX_IN, 44 | CFG_LOG_EVENTS_OUT, 45 | CFG_LOG_EVENTS_IN, 46 | CFG_WINDOW_WIDTH, 47 | CFG_WINDOW_HEIGHT, 48 | CFG_BGR, 49 | CFG_BGG, 50 | CFG_BGB, 51 | CFG_BG2R, 52 | CFG_BG2G, 53 | CFG_BG2B, 54 | CFG_FGR, 55 | CFG_FGG, 56 | CFG_FGB, 57 | CFG_SLR, 58 | CFG_SLG, 59 | CFG_SLB, 60 | CFG_INR, 61 | CFG_ING, 62 | CFG_INB, 63 | CFG_KNOB_COLOR1, 64 | CFG_KNOB_COLOR2, 65 | NOOPTION 66 | }; 67 | 68 | /** 69 | * Configuration class. 70 | * loads, saves and manages all configuration options 71 | */ 72 | class Cfg 73 | { 74 | /// configuration directory 75 | char config_dir[PATH_MAX]; 76 | /// path to export directory 77 | char export_dir[PATH_MAX]; 78 | std::vector defaults; 79 | std::vector option; 80 | 81 | public: 82 | /** 83 | * CTOR parses config file 84 | */ 85 | Cfg(int id = -1); 86 | /** 87 | * DTOR saves config file 88 | */ 89 | ~Cfg(); 90 | /// returns the config directory path 91 | const char* get_config_dir() const; 92 | const char* get_export_dir() const; 93 | bool set_export_dir(const char* dir); 94 | /** 95 | * updates a configuration value 96 | * @param option the parameter to update 97 | * @param value the new value for the parameter 98 | */ 99 | void set_cfg_option(int option, int value); 100 | /** 101 | * get a configuration option 102 | * @param option parameter to get 103 | * @return parameter value 104 | */ 105 | int get_cfg_option(int) const; 106 | int get_default(int) const; 107 | int getset_default(int); 108 | void apply(bool colors_only = false); 109 | }; 110 | 111 | #endif /* CFG_H_ */ 112 | /** @} */ 113 | -------------------------------------------------------------------------------- /include/data.h: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | #ifndef DATA_H_ 3 | #define DATA_H_ 4 | /** 5 | \defgroup pd_data prodatum Data Classes 6 | @{ 7 | */ 8 | #include 9 | #include 10 | 11 | #define DUMP_HEADER_SIZE 36 12 | 13 | /** 14 | * Enum for generic name IDs used by the device 15 | */ 16 | enum 17 | { 18 | ID, PRESET, INSTRUMENT, ARP, SETUP, DEMO, RIFF 19 | }; 20 | 21 | /** 22 | * Preset Dump class. 23 | * holds data and informations of a preset dump 24 | */ 25 | class Preset_Dump 26 | { 27 | /// size of the complete dump 28 | int size; 29 | /// size of packets 30 | int packet_size; 31 | /// program/preset number 32 | int number; 33 | /// ROM ID of this program/preset 34 | int rom_id; 35 | /// name of program/preset 36 | unsigned char name[17]; 37 | /// wether this dump contains 4 extra controllers found on p2k modules 38 | int extra_controller; 39 | /// wether we are an audity 2000 dump 40 | int a2k; 41 | /// true if data has been edited 42 | bool data_is_changed; 43 | /// raw dump data 44 | unsigned char* data; 45 | /// undo struct 46 | struct parameter 47 | { 48 | int id; 49 | int value; 50 | int layer; 51 | }; 52 | /// undo stack 53 | std::deque undo_s; 54 | /// redo stack 55 | std::deque redo_s; 56 | /// push undo parameter on stack 57 | void add_undo(int id, int layer); 58 | void update_ui_from_xdo(int id, int value, int layer) const; 59 | /** 60 | * maps parameter IDs to offset values in the dump 61 | * @param id parameter ID 62 | * @param layer the layer of the parameter (0-3) 63 | * @param id_mapped reference variable for the calculated offset value 64 | */ 65 | void idmap(const int& id, const int& layer, int& id_mapped) const; 66 | /// hands over preset data to the piano widget 67 | void update_piano() const; 68 | /// hands over preset data to the envelope widget 69 | void update_envelopes() const; 70 | /// updates the checksums of the dump 71 | void update_checksum(); 72 | /** 73 | * Copy a given range of parameters from one layer to another. 74 | * works will all ranges (validity checks are being done internally) 75 | * @param start first parameter ID 76 | * @param end last parameter ID 77 | * @param src source layer 78 | * @param dst destination layer 79 | */ 80 | void copy_layer_parameter_range(int start, int end, int src, int dst); 81 | 82 | public: 83 | /** 84 | * CTOR for Preset Dump 85 | * @param dump_size the size of the dump in bytes 86 | * @param dump_data pointer to the dump data 87 | * @param p_size number of bytes in a packet 88 | * @param update wether to automatically update name files and browsers 89 | * from name extracted from the dump 90 | */ 91 | Preset_Dump(int dump_size, const unsigned char* dump_data, int p_size, bool update = false); 92 | /** 93 | * DTOR just frees the memory 94 | */ 95 | ~Preset_Dump(); 96 | 97 | /// pack the sysex for converted messages 98 | void repack_sysex(std::vector& v, int shift, int dest_size); 99 | /// return data status 100 | bool is_changed() const; 101 | /// return extra_controller 102 | int get_extra_controller() const; 103 | /// undo edit 104 | void undo(); 105 | /// redo edit 106 | void redo(); 107 | /// if true, nothing is pushed on the undo stack 108 | bool disable_add_undo; 109 | /** 110 | * clone this preset dump 111 | */ 112 | Preset_Dump* clone() const; 113 | /// 114 | void set_changed(bool); 115 | /// returns the preset name 116 | const char* get_name() const; 117 | /// returns the preset number 118 | int get_number() const; 119 | /// returns the ROM ID 120 | int get_rom_id() const; 121 | /** 122 | * get the value of a parameter. 123 | * extract a value for a given (layer-)parameter from the dump 124 | * @param id parameter ID 125 | * @param layer layer of the parameter 126 | * @returns integer value for the parameter 127 | */ 128 | int get_value(int id, int layer = 0) const; 129 | /** 130 | * set the value of a parameter. 131 | * update a given (layer-)parameter to the specified value 132 | * @param id parameter ID 133 | * @param value the new value for the (layer-)parameter 134 | * @param layer layer of the parameter 135 | */ 136 | int set_value(int id, int value, int layer = -2); 137 | /// set category and name 138 | void set_name(const char* val, int type, int position); 139 | /** 140 | * update UI widgets with the parameter values of this dump. 141 | * this calls \c get_value() for all parameters (except FX parameters) 142 | * and sets the corresponding widget in the UI. 143 | */ 144 | void show() const; 145 | /** 146 | * update UI-FX widgets with the parameter values of this dump. 147 | * this calls \c get_value() for all FX parameters 148 | * and sets the corresponding widget in the UI. 149 | */ 150 | void show_fx() const; 151 | /** 152 | * move preset to a new location. 153 | * @param new_number the new location for the preset 154 | */ 155 | void move(int new_number); 156 | /** 157 | * copy various parameters from one layer to another. 158 | * this is also used to save a preset to the device. 159 | * @param type what to copy (preset, fx settings, complete layer, ..) 160 | * @param src the source layer 161 | * @param dst the destination layer 162 | */ 163 | void copy(int type, int src, int dst); 164 | /** 165 | * upload this preset dump to the device. 166 | * this is used to save the preset. it will update the checksums and upload 167 | * the preset in either closed or open loop fashion (depending on what 168 | * has been configured). 169 | * @param packet the packet number to upload 170 | * @param closed wether to use close or open loop style uploads 171 | * @param show wether to show the uploaded dump when 172 | * the upload was successfull 173 | */ 174 | void upload(int packet, int closed = -1, bool show = false); 175 | /// save dump to disk 176 | void save_file(const char* save_dir, int offset=-1); 177 | }; 178 | 179 | /** 180 | * Arp Dump class. 181 | * holds data and informations of a arp dump 182 | */ 183 | class Arp_Dump 184 | { 185 | int size; 186 | int number; 187 | unsigned char name[17]; 188 | unsigned char* data; 189 | void show(bool show_editor) const; 190 | void update_name(const unsigned char* np) const; 191 | public: 192 | Arp_Dump(int dump_size, const unsigned char* dump_data, bool editor); 193 | ~Arp_Dump(); 194 | void update_sequence_length_information() const; 195 | int get_number() const; 196 | int get_value(int id, int step) const; 197 | /// rename arp 198 | void rename(const char* newname) const; 199 | void reset_step(int step) const; 200 | void reset_pattern() const; 201 | void load_file(int num) const; 202 | void save_file(const char* save_dir, int offset) const; 203 | }; 204 | 205 | /** 206 | * Setup Dump class. 207 | * holds data and informations of a setup dump 208 | */ 209 | class Setup_Dump 210 | { 211 | /// size of the complete dump 212 | int size; 213 | /// raw dump data 214 | unsigned char* data; 215 | /// wether this dump contains 4 extra controllers found on p2k modules 216 | int extra_controller; 217 | /** 218 | * maps parameter IDs to offset values in the dump 219 | * @param id parameter ID 220 | * @param channel the channel for the parameter 221 | * @param id_mapped reference variable for the calculated offset value 222 | */ 223 | void idmap(const int& id, const int& channel, int& id_mapped) const; 224 | /** 225 | * Enum for various types of data in the setup dump. 226 | */ 227 | enum 228 | { 229 | SDI_GENERAL, SDI_MIDI, SDI_FX, SDI_RESERVED, SDI_NON_CHNL, SDI_CHNLS, SDI_CHNL_PARAMS 230 | }; 231 | /// array holds vital informations about the current setup 232 | int setup_dump_info[7]; 233 | 234 | public: 235 | /** 236 | * CTOR for Setup Dump 237 | * @param dump_size the size of the dump in bytes 238 | * @param dump_data pointer to the dump data 239 | */ 240 | Setup_Dump(int dump_size, const unsigned char* dump_data); 241 | /** 242 | * DTOR just frees the memory 243 | */ 244 | ~Setup_Dump(); 245 | /** 246 | * get the value of a parameter. 247 | * extract a value for a given setup parameter from the dump 248 | * @param id parameter ID 249 | * @param hannel the channel number for the parameter 250 | * @returns integer value for the parameter 251 | */ 252 | 253 | /// name of setup 254 | unsigned char name[17]; 255 | 256 | int get_value(int id, int channel = -1) const; 257 | /** 258 | * set the value of a parameter. 259 | * update a given (channel-)parameter to the specified value 260 | * @param id parameter ID 261 | * @param value the new value for the (layer-)parameter 262 | * @param channel parameter channel to update 263 | */ 264 | int set_value(int id, int value, int channel = -1); 265 | /** 266 | * update UI widgets with the parameter values of this dump. 267 | * this calls \c get_value() for all setup parameters 268 | * and sets the corresponding widget in the UI. 269 | * @param midi_mode the currently active midimode 270 | */ 271 | void show() const; 272 | /** 273 | * update UI FX widgets with master FX settings. 274 | * this calls \c get_value() for all Master FX parameters 275 | * and sets the corresponding widget in the UI. 276 | */ 277 | void show_fx() const; 278 | /** 279 | * uploads the dump to the device to save it. 280 | */ 281 | void upload() const; 282 | /** 283 | * saves the setup to a file for backup 284 | */ 285 | void save_file(const char* save_dir) const; 286 | 287 | Setup_Dump* Clone() const; 288 | }; 289 | 290 | /** 291 | * Program change map Dump class. 292 | * holds data and informations of a program change map dump 293 | */ 294 | //class PC_Dump 295 | //{ 296 | // 297 | //}; 298 | 299 | /** 300 | * ROM class. 301 | * holds data and informations of a ROM module 302 | */ 303 | class ROM 304 | { 305 | /// rom ID 306 | int id; 307 | /// for user data (programs, arps) we need to know which device we belong to 308 | char device_id; 309 | /// number of available instruments in this ROM 310 | int instruments; 311 | /// number of available presets in this ROM 312 | int presets; 313 | /// number of available arps in this ROM 314 | int arps; 315 | /// number of available riffs in this ROM 316 | int riffs; 317 | /// storage pointer to the array of instrument names 318 | unsigned char* instrument_names; 319 | /// storage pointer to the array of preset names 320 | unsigned char* preset_names; 321 | /// storage pointer to the array of arp names 322 | unsigned char* arp_names; 323 | bool arp_names_changed; 324 | /// storage pointer to the array of riff names 325 | unsigned char* riff_names; 326 | 327 | public: 328 | /** 329 | * CTOR for the ROM class. 330 | * @param id the ROM ID 331 | * @param presets the number of presets the ROM holds 332 | * @param instruments the number of instruments the ROM holds 333 | */ 334 | ROM(int id, int presets = 0, int instruments = 0); 335 | /** 336 | * DTOR of ROM class. 337 | * triggers saving of ROM-name files (if not already saved and ID != 0) 338 | * and frees memory 339 | */ 340 | ~ROM(); 341 | /** 342 | * loads name files or triggers name request commands if loading fails 343 | * @param type of name file (PRESET; INSTRUMENT,..) 344 | * @param number index number of name to request 345 | */ 346 | void load_name(unsigned char type, int number); 347 | /** 348 | * load a name file from disk. 349 | * @param type of name file (PRESET; INSTRUMENT,..) 350 | * @returns number of available names 351 | */ 352 | int disk_load_names(unsigned char type); 353 | /** 354 | * set the name of type X with number Z to name Y 355 | * @param type of name (PRESET; INSTRUMENT,..) 356 | * @param number the number of the name 357 | * @param name the name 358 | */ 359 | int set_name(int type, int number, const unsigned char* name); 360 | void save(unsigned char); 361 | /// returns an attribute like ROM ID ... 362 | int get_attribute(int type) const; 363 | /// returns the name of this ROM 364 | const char* name() const; 365 | /** 366 | * return the name for type X with number Y 367 | * @param type of name file (PRESET; INSTRUMENT,..) 368 | * @param number the number of the name 369 | * @returns pointer to the name 370 | */ 371 | const unsigned char* get_name(int type, int number = 0) const; 372 | 373 | int get_romid(); 374 | }; 375 | 376 | #endif /*DATA_H_*/ 377 | /** @} */ 378 | -------------------------------------------------------------------------------- /include/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H_ 2 | #define DEBUG_H_ 3 | #include 4 | 5 | #if defined(NDEBUG) 6 | #if defined(WIN32) 7 | #define pmesg(format, ...) ((void)0) 8 | #else 9 | #define pmesg(format, args...) ((void)0) 10 | #endif 11 | #else 12 | void pmesg(const char *format, ...); 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /include/midi.h: -------------------------------------------------------------------------------- 1 | #ifndef MIDI_H_ 2 | #define MIDI_H_ 3 | /** 4 | \defgroup pd_midi prodatum MIDI I/O 5 | @{ 6 | */ 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MIDI_SYSEX 0xf0 13 | #define MIDI_EOX 0xf7 14 | #define NOTE_OFF 0x80 15 | #define NOTE_ON 0x90 16 | 17 | #ifdef WIN32 18 | # include 19 | # define mysleep(x) Sleep(x) 20 | #else 21 | # include 22 | # define mysleep(x) usleep((x) * 1000) 23 | #endif 24 | 25 | /** 26 | * prodatum MIDI class. 27 | * opens and closes MIDI ports, starts and stops the MIDI sender/receiver, 28 | * allocates buffers for reading and writing MIDI data, populates the UI 29 | * with available MIDI ports and offers many methods to send all kinds of 30 | * sysex commands and MIDI events 31 | */ 32 | class MIDI 33 | { 34 | /// vector for available readable MIDI ports 35 | std::vector ports_in; 36 | /// vector for writable MIDI ports 37 | std::vector ports_out; 38 | /// opened writable MIDI port 39 | int selected_port_out; 40 | /// opened readable MIDI port 41 | int selected_port_in; 42 | /// opened controller MIDI port 43 | int selected_port_thru; 44 | /// method to start the MIDI sender/receiver 45 | int start_timer(); 46 | /// method to stop the MIDI sender/receiver 47 | void stop_timer(); 48 | public: 49 | /** 50 | * CTOR for the MIDI class 51 | * populates available MIDI ports to the UI and allocates storage for 52 | * the ringbuffers, initializes default values 53 | */ 54 | MIDI(); 55 | /** 56 | * CTOR for the MIDI class 57 | * frees allocated ringbuffers, closes all opened MIDI ports and stops 58 | * the MIDI sender/receiver 59 | */ 60 | ~MIDI(); 61 | /// allows for switching the device ID on the fly 62 | void set_device_id(unsigned char id); 63 | /// sets the incoming channel filter for the control port 64 | void set_control_channel_filter(int channel) const; 65 | /// sets the incoming channel filter 66 | void set_channel_filter(int channel) const; 67 | /** 68 | * opens writable MIDI port, starts MIDI timer if not running yet. 69 | * @param out the MIDI port to open 70 | */ 71 | int connect_out(int out); 72 | /** 73 | * opens readable MIDI port, starts MIDI timer if not running yet. 74 | * @param in the MIDI port to open 75 | */ 76 | int connect_in(int in); 77 | void filter_loose() const; 78 | void filter_strict() const; 79 | /** 80 | * opens readable MIDI controller port, starts MIDI timer if not running yet. 81 | * @param thru the MIDI port to open 82 | */ 83 | int connect_thru(int thru); 84 | /** 85 | * puts a sysex message into the write buffer. 86 | * @param sysex the sysex data 87 | * @param size the size of the message in bytes 88 | */ 89 | void write_sysex(const unsigned char* sysex, unsigned int size) const; 90 | /** 91 | * puts a MIDI event into the write buffer 92 | * @param status MIDI status byte 93 | * @param value1 first MIDI data byte 94 | * @param value2 second MIDI data byte 95 | * @param channel the channel to send the event on 96 | */ 97 | void write_event(int status, int value1, int value2, int channel = -1) const; 98 | /** 99 | * send an acknowledgement for a packet. 100 | * @param packet the packet to acknowledge 101 | */ 102 | void ack(int packet) const; 103 | /** 104 | * send an negative acknowledgement for a packet. 105 | * @param packet the packet to negative acknowledge 106 | */ 107 | void nak(int packet) const; 108 | /// sends a cancel command 109 | // void cancel() const; 110 | /// send WAIT command 111 | // void wait() const; 112 | /// sends a EOF command 113 | void eof() const; 114 | /// sends a device inquiry 115 | // void request_device_inquiry(int id = -1) const; 116 | /** 117 | * sends a preset dump request. 118 | * @param timeout time to wait in ms before sending the request 119 | */ 120 | void request_preset_dump(int timeout = 0) const; 121 | /// sends a setup dump request 122 | void request_setup_dump() const; 123 | // /// sends an FX dump request 124 | // void request_fx_dump(int preset, int rom_id) const; 125 | /// sends an arp dump request 126 | void request_arp_dump(int number, int rom_id) const; 127 | /** 128 | * sends a parameter value request 129 | * @param id the parameter ID 130 | * @param channel the channel of the parameter 131 | */ 132 | //void request_parameter_value(int id, int channel = -1) const; 133 | /** 134 | * sends a generic name request. 135 | * @param type the type of name (PRESET, INSTRUMENT, ..) 136 | * @param number the items number 137 | * @param rom_ID the ROM ID of the item 138 | */ 139 | void request_name(int type, int number, int rom_ID) const; 140 | /** 141 | * sends a parameter value edit command. 142 | * @param id the parameter ID 143 | * @param value the new value for the parameter 144 | */ 145 | void edit_parameter_value(int id, int value) const; 146 | /** 147 | * renames an item on the device. 148 | * @param type the type of the item to rename (PRESET or ARP) 149 | * @param number the number of the item to rename 150 | * @param name the new name 151 | */ 152 | //void rename(int type, int number, const unsigned char* name) const; 153 | /// sends a hardware configuration request 154 | void request_hardware_config() const; 155 | /** 156 | * sets the master volume of the device 157 | * @param volume the new volume 158 | */ 159 | void master_volume(int volume) const; 160 | /** 161 | * sends a copy command. 162 | * @param cmd the type of copy command (copy preset/arp, patchcords, layer,..) 163 | * @param src the source program 164 | * @param dst the destination program 165 | * @param src_l the source layer 166 | * @param dst_l the destination layer 167 | * @param rom_id the ROM ID of the source 168 | */ 169 | void copy(int cmd, int src, int dst, int src_l = 0, int dst_l = 0, int rom_id = 0) const; 170 | /// toggles audition on the device 171 | void audit() const; 172 | /// sends a randomize preset command 173 | void randomize() const; 174 | bool in(); 175 | bool out(); 176 | void reset_handler() const; 177 | // bool Wait(); 178 | // void Wait(bool); 179 | }; 180 | 181 | #endif /*MIDI_H_*/ 182 | /** @} */ 183 | -------------------------------------------------------------------------------- /include/pxk.h: -------------------------------------------------------------------------------- 1 | /* 2 | * pxk.H 3 | * 4 | * Created on: 12 Feb 2014 5 | * Author: vvd 6 | */ 7 | 8 | #ifndef PXK_H_ 9 | #define PXK_H_ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include "ui.h" 16 | #include "data.h" 17 | 18 | /** 19 | * Enum for the three MIDI modes 20 | */ 21 | enum 22 | { 23 | OMNI, POLY, MULTI 24 | }; 25 | 26 | /** 27 | * Enum for the various copy commands of the device 28 | */ 29 | enum 30 | { 31 | C_PRESET = 0x20, 32 | C_PRESET_COMMON, 33 | C_ARP, 34 | C_FX, 35 | C_PRESET_LINK, 36 | C_LAYER, 37 | C_LAYER_COMMON, 38 | C_LAYER_FILTER, 39 | C_LAYER_LFO, 40 | C_LAYER_ENVELOPE, 41 | C_LAYER_PATCHCORD, 42 | C_ARP_PATTERN, 43 | C_SETUP, 44 | SAVE_PRESET 45 | }; 46 | 47 | class PXK 48 | { 49 | /* 50 | * general 51 | */ 52 | public: 53 | std::vector status_message; 54 | std::vector preset_list; 55 | std::vector preset_saves; 56 | std::vector arp_list; 57 | std::vector arp_saves; 58 | std::string output_dir; 59 | std::string setup_file; 60 | int preset_dump_rom; 61 | bool save_in_progress; 62 | bool started_request; 63 | bool pending_cancel; 64 | int machine_id; 65 | 66 | private: 67 | char device_id; 68 | volatile bool synchronized; 69 | public: 70 | void ConnectPorts(); 71 | bool Synchronize(); 72 | void Loading(bool upload = false); 73 | void log_add(const unsigned char*, const unsigned int, unsigned char) const; 74 | bool Synchronized() const; 75 | void new_preset(int, const unsigned char*, int); 76 | void new_arp(int, const unsigned char*); 77 | void clear_preset_handler(); 78 | bool preset_transfer_complete(); 79 | /// maps controller values to CC widget numbers (device -> UI) 80 | std::map cc_to_ctrl; 81 | private: 82 | /// maps CC widget numbers to actual controller values (UI -> device) 83 | std::map ctrl_to_cc; 84 | unsigned char nak_count; 85 | unsigned char ack_count; 86 | public: 87 | void incoming_generic_name(const unsigned char*); 88 | void incoming_ACK(int); 89 | void incoming_NAK(int); 90 | // void incoming_ERROR(int, int); 91 | void widget_callback(int, int, int layer = -2); 92 | void cc_callback(int, int); 93 | void display_status(const char*); 94 | void Join(); 95 | void reset(); 96 | 97 | /* 98 | * device specific 99 | */ 100 | public: 101 | int device_code; // PXK device code 102 | int member_code; // 2 = AUDITY 103 | private: 104 | char os_rev[5]; // OS revision 105 | int user_presets; // available user presets 106 | void create_device_info(); 107 | public: 108 | bool inquired; 109 | void Inquire(int); 110 | void incoming_inquiry_data(const unsigned char*); 111 | void incoming_hardware_config(const unsigned char*); 112 | 113 | /* 114 | * setup specific 115 | */ 116 | public: 117 | // these are used by lot's of widgets 118 | Setup_Dump* setup; 119 | const Setup_Dump* setup_copy; 120 | char selected_channel; 121 | char selected_preset_rom; 122 | int selected_preset; 123 | int selected_arp; 124 | char selected_multisetup; 125 | char setup_offset; 126 | private: 127 | unsigned char* setup_names; 128 | bool setup_names_changed; 129 | bool cc_changed; 130 | void update_fx_values(int, int) const; 131 | void update_cc_sliders(); 132 | void update_control_map(); 133 | public: 134 | char midi_mode; 135 | char selected_fx_channel; 136 | const Setup_Dump* setup_init; 137 | void save_setup(int, const char*); 138 | void incoming_setup_dump(const unsigned char*, int); 139 | unsigned char load_setup_names(unsigned char); 140 | void set_setup_name(unsigned char, const unsigned char*); 141 | void save_setup_names(bool force = false) const; 142 | void load_setup(); 143 | void store_play_as_initial(); 144 | void import_setup(); 145 | void export_setup(); 146 | 147 | 148 | /* 149 | * rom specific 150 | */ 151 | private: 152 | char rom_index[5]; 153 | const char* get_name(int) const; 154 | public: 155 | ROM* rom[5]; 156 | unsigned char roms; // number of roms 157 | unsigned char get_rom_index(char) const; 158 | 159 | /* 160 | * preset specific 161 | */ 162 | private: 163 | int mute_volume[4]; // volume of muted voices 164 | bool is_solo[4]; 165 | bool randomizing; 166 | int preset_offset; 167 | 168 | public: 169 | Preset_Dump* preset; 170 | const Preset_Dump* preset_copy; 171 | int test_checksum(const unsigned char*, int, int); 172 | void show_preset(); 173 | int selected_layer; 174 | void mute(int state, int layer); 175 | void solo(int state, int layer); 176 | void incoming_preset_dump(const unsigned char*, int, bool=false); 177 | void load_export(const char*); 178 | void start_over(); 179 | void randomize(); 180 | void bulk_preset_download(); 181 | void bulk_preset_upload(); 182 | int get_preset_and_increment(); 183 | 184 | /* 185 | * arp specific 186 | */ 187 | private: 188 | int arp_offset; 189 | 190 | public: 191 | Arp_Dump* arp; 192 | void incoming_arp_dump(const unsigned char*, int); 193 | int get_arp_and_increment(); 194 | 195 | public: 196 | /// CTOR 197 | PXK(); 198 | void Boot(bool, int __id = -1); 199 | /// DTOR 200 | ~PXK(); 201 | 202 | void bulk_pattern_download(); 203 | void bulk_pattern_upload(); 204 | }; 205 | 206 | #endif /* PXK_H_ */ 207 | -------------------------------------------------------------------------------- /include/ringbuffer.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2000 Paul Davis 3 | Copyright (C) 2003 Rohan Drape 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU Lesser General Public License as published by 7 | the Free Software Foundation; either version 2.1 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | */ 20 | 21 | // slightly modified for prodatum to use unsigned char 22 | 23 | #ifndef _RINGBUFFER_H 24 | #define _RINGBUFFER_H 25 | 26 | #ifdef __cplusplus 27 | extern "C" { 28 | #endif 29 | 30 | #include 31 | 32 | /** @file ringbuffer.h 33 | * 34 | * A set of library functions to make lock-free ringbuffers available 35 | * to JACK clients. The `capture_client.c' (in the example_clients 36 | * directory) is a fully functioning user of this API. 37 | * 38 | * The key attribute of a ringbuffer is that it can be safely accessed 39 | * by two threads simultaneously -- one reading from the buffer and 40 | * the other writing to it -- without using any synchronization or 41 | * mutual exclusion primitives. For this to work correctly, there can 42 | * only be a single reader and a single writer thread. Their 43 | * identities cannot be interchanged. 44 | */ 45 | 46 | typedef struct 47 | { 48 | unsigned char *buf; 49 | size_t len; 50 | } 51 | jack_ringbuffer_data_t ; 52 | 53 | typedef struct 54 | { 55 | unsigned char *buf; 56 | volatile size_t write_ptr; 57 | volatile size_t read_ptr; 58 | size_t size; 59 | size_t size_mask; 60 | int mlocked; 61 | } 62 | jack_ringbuffer_t ; 63 | 64 | /** 65 | * Allocates a ringbuffer data structure of a specified size. The 66 | * caller must arrange for a call to jack_ringbuffer_free() to release 67 | * the memory associated with the ringbuffer. 68 | * 69 | * @param sz the ringbuffer size in bytes. 70 | * 71 | * @return a pointer to a new jack_ringbuffer_t, if successful; NULL 72 | * otherwise. 73 | */ 74 | jack_ringbuffer_t *jack_ringbuffer_create(size_t sz); 75 | 76 | /** 77 | * Frees the ringbuffer data structure allocated by an earlier call to 78 | * jack_ringbuffer_create(). 79 | * 80 | * @param rb a pointer to the ringbuffer structure. 81 | */ 82 | void jack_ringbuffer_free(jack_ringbuffer_t *rb); 83 | 84 | /** 85 | * Fill a data structure with a description of the current readable 86 | * data held in the ringbuffer. This description is returned in a two 87 | * element array of jack_ringbuffer_data_t. Two elements are needed 88 | * because the data to be read may be split across the end of the 89 | * ringbuffer. 90 | * 91 | * The first element will always contain a valid @a len field, which 92 | * may be zero or greater. If the @a len field is non-zero, then data 93 | * can be read in a contiguous fashion using the address given in the 94 | * corresponding @a buf field. 95 | * 96 | * If the second element has a non-zero @a len field, then a second 97 | * contiguous stretch of data can be read from the address given in 98 | * its corresponding @a buf field. 99 | * 100 | * @param rb a pointer to the ringbuffer structure. 101 | * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. 102 | * 103 | */ 104 | void jack_ringbuffer_get_read_vector(const jack_ringbuffer_t *rb, 105 | jack_ringbuffer_data_t *vec); 106 | 107 | /** 108 | * Fill a data structure with a description of the current writable 109 | * space in the ringbuffer. The description is returned in a two 110 | * element array of jack_ringbuffer_data_t. Two elements are needed 111 | * because the space available for writing may be split across the end 112 | * of the ringbuffer. 113 | * 114 | * The first element will always contain a valid @a len field, which 115 | * may be zero or greater. If the @a len field is non-zero, then data 116 | * can be written in a contiguous fashion using the address given in 117 | * the corresponding @a buf field. 118 | * 119 | * If the second element has a non-zero @a len field, then a second 120 | * contiguous stretch of data can be written to the address given in 121 | * the corresponding @a buf field. 122 | * 123 | * @param rb a pointer to the ringbuffer structure. 124 | * @param vec a pointer to a 2 element array of jack_ringbuffer_data_t. 125 | */ 126 | void jack_ringbuffer_get_write_vector(const jack_ringbuffer_t *rb, 127 | jack_ringbuffer_data_t *vec); 128 | 129 | /** 130 | * Read data from the ringbuffer. 131 | * 132 | * @param rb a pointer to the ringbuffer structure. 133 | * @param dest a pointer to a buffer where data read from the 134 | * ringbuffer will go. 135 | * @param cnt the number of bytes to read. 136 | * 137 | * @return the number of bytes read, which may range from 0 to cnt. 138 | */ 139 | size_t jack_ringbuffer_read(jack_ringbuffer_t *rb, unsigned char *dest, size_t cnt); 140 | 141 | /** 142 | * Read data from the ringbuffer. Opposed to jack_ringbuffer_read() 143 | * this function does not move the read pointer. Thus it's 144 | * a convenient way to inspect data in the ringbuffer in a 145 | * continous fashion. The price is that the data is copied 146 | * into a user provided buffer. For "raw" non-copy inspection 147 | * of the data in the ringbuffer use jack_ringbuffer_get_read_vector(). 148 | * 149 | * @param rb a pointer to the ringbuffer structure. 150 | * @param dest a pointer to a buffer where data read from the 151 | * ringbuffer will go. 152 | * @param cnt the number of bytes to read. 153 | * 154 | * @return the number of bytes read, which may range from 0 to cnt. 155 | */ 156 | size_t jack_ringbuffer_peek(jack_ringbuffer_t *rb, unsigned char *dest, size_t cnt); 157 | 158 | /** 159 | * Advance the read pointer. 160 | * 161 | * After data have been read from the ringbuffer using the pointers 162 | * returned by jack_ringbuffer_get_read_vector(), use this function to 163 | * advance the buffer pointers, making that space available for future 164 | * write operations. 165 | * 166 | * @param rb a pointer to the ringbuffer structure. 167 | * @param cnt the number of bytes read. 168 | */ 169 | void jack_ringbuffer_read_advance(jack_ringbuffer_t *rb, size_t cnt); 170 | 171 | /** 172 | * Return the number of bytes available for reading. 173 | * 174 | * @param rb a pointer to the ringbuffer structure. 175 | * 176 | * @return the number of bytes available to read. 177 | */ 178 | size_t jack_ringbuffer_read_space(const jack_ringbuffer_t *rb); 179 | 180 | /** 181 | * Lock a ringbuffer data block into memory. 182 | * 183 | * Uses the mlock() system call. This is not a realtime operation. 184 | * 185 | * @param rb a pointer to the ringbuffer structure. 186 | */ 187 | int jack_ringbuffer_mlock(jack_ringbuffer_t *rb); 188 | 189 | /** 190 | * Reset the read and write pointers, making an empty buffer. 191 | * 192 | * This is not thread safe. 193 | * 194 | * @param rb a pointer to the ringbuffer structure. 195 | */ 196 | void jack_ringbuffer_reset(jack_ringbuffer_t *rb); 197 | 198 | /** 199 | * Write data into the ringbuffer. 200 | * 201 | * @param rb a pointer to the ringbuffer structure. 202 | * @param src a pointer to the data to be written to the ringbuffer. 203 | * @param cnt the number of bytes to write. 204 | * 205 | * @return the number of bytes write, which may range from 0 to cnt 206 | */ 207 | size_t jack_ringbuffer_write(jack_ringbuffer_t *rb, const unsigned char *src, 208 | size_t cnt); 209 | 210 | /** 211 | * Advance the write pointer. 212 | * 213 | * After data have been written the ringbuffer using the pointers 214 | * returned by jack_ringbuffer_get_write_vector(), use this function 215 | * to advance the buffer pointer, making the data available for future 216 | * read operations. 217 | * 218 | * @param rb a pointer to the ringbuffer structure. 219 | * @param cnt the number of bytes written. 220 | */ 221 | void jack_ringbuffer_write_advance(jack_ringbuffer_t *rb, size_t cnt); 222 | 223 | /** 224 | * Return the number of bytes available for writing. 225 | * 226 | * @param rb a pointer to the ringbuffer structure. 227 | * 228 | * @return the amount of free space (in bytes) available for writing. 229 | */ 230 | size_t jack_ringbuffer_write_space(const jack_ringbuffer_t *rb); 231 | 232 | 233 | #ifdef __cplusplus 234 | } 235 | #endif 236 | 237 | #endif 238 | -------------------------------------------------------------------------------- /include/widgets.h: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | 3 | /** 4 | \defgroup pd_widgets prodatum Widgets 5 | @{ 6 | */ 7 | 8 | /* 9 | int ret = ::handle(ev); 10 | if (!foo) 11 | return ret; 12 | switch (ev) 13 | { 14 | // Mouse Events 15 | case FL_ENTER: // 1 = receive FL_LEAVE and FL_MOVE events (widget becomes Fl::belowmouse()) 16 | case FL_LEAVE: 17 | case FL_MOVE: // sent to Fl::belowmouse() 18 | case FL_PUSH: // 1 = receive FL_DRAG and the matching (Fl::event_button()) FL_RELEASE event (becomes Fl::pushed()) 19 | case FL_RELEASE: 20 | case FL_DRAG: // button state is in Fl::event_state() (FL_SHIFT FL_CAPS_LOCK FL_CTRL FL_ALT FL_NUM_LOCK FL_META FL_SCROLL_LOCK FL_BUTTON1 FL_BUTTON2 FL_BUTTON3) 21 | case FL_MOUSEWHEEL: 22 | // keyboard events 23 | case FL_FOCUS: // 1 = receive FL_KEYDOWN, FL_KEYUP, and FL_UNFOCUS events (widget becomes Fl::focus()) 24 | case FL_UNFOCUS: // received when another widget gets the focus and we had the focus 25 | case FL_KEYDOWN: // key press (Fl::event_key()) 26 | case FL_KEYUP: // key release (Fl::event_key()) 27 | // DND events 28 | case FL_DND_ENTER: // 1 = receive FL_DND_DRAG, FL_DND_LEAVE and FL_DND_RELEASE events 29 | case FL_DND_DRAG: // to indicate if we want the data 30 | case FL_DND_RELEASE: // 1 = receive FL_PASTE 31 | } 32 | return ret; 33 | */ 34 | 35 | #ifndef WIDGETS_H_ 36 | #define WIDGETS_H_ 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | #include 51 | #include 52 | #include 53 | #include 54 | 55 | #include "config.h" 56 | 57 | /** 58 | * maps filter parameter values to index in selectors array. 59 | * also includes filter name and filter informations 60 | */ 61 | struct FilterMap 62 | { 63 | int id; 64 | const char* name; 65 | const char* info; 66 | int _index; 67 | }; 68 | 69 | struct Patchcord 70 | { 71 | int id; 72 | const char* name; 73 | }; 74 | 75 | /** 76 | * shows confirmation dialog on various occasions 77 | * @param exit wether to act as an exit dialog 78 | * @return the chosen answer 79 | */ 80 | int dismiss(char exit); 81 | 82 | /** 83 | * PWid abstract class. 84 | * all device parameter widgets derive from this. those can then all 85 | * put into the globa \c pwid[][] array, where they can be accessed 86 | * by a parameter ID and layer number 87 | */ 88 | class PWid 89 | { 90 | protected: 91 | /** 92 | * callback. 93 | * @param p parameter ID and Layer of the caller (id_layer[]) 94 | */ 95 | static void cb(PWid*, void* p); 96 | /// holds parameter ID and layer number 97 | int id_layer[2]; 98 | /// holds min and max values for this parameter 99 | int minimax[2]; 100 | public: 101 | virtual ~PWid() 102 | { 103 | ; 104 | } 105 | /** 106 | * assigns parameter ID and layer information. 107 | * connects the callback, puts pointer to this into the global \c pwid[][] 108 | * array 109 | */ 110 | virtual void set_id(int v, int l = 0) = 0; 111 | /** 112 | * updates the value of the widget. 113 | * @param v the new integer value 114 | */ 115 | virtual void set_value(int v) = 0; 116 | /** 117 | * gets the current widget value. 118 | * @return current integer value 119 | */ 120 | virtual int get_value() const = 0; 121 | /// returns a pointer to id_layer[] 122 | int* get_id_layer() 123 | { 124 | return id_layer; 125 | } 126 | /// returns a pointer to minimax[] 127 | virtual int* get_minimax() 128 | { 129 | return minimax; 130 | } 131 | }; 132 | 133 | extern PWid* pwid[2000][4]; 134 | extern PWid* pwid_editing; 135 | // some often used toltips 136 | static const char* _ms = "Modulation Source"; 137 | static const char* _md = "Modulation Destination"; 138 | static const char* _ma = "Modulation Amount (double click to toggle between 0 and current value)"; 139 | 140 | /** 141 | * Double_Window class 142 | * just overwrites the default handler to support 143 | * key press+hold+release callbacks 144 | */ 145 | class Double_Window: public Fl_Double_Window 146 | { 147 | int handle(int event); 148 | bool w__shown; 149 | bool supposed_to_be_shown; 150 | public: 151 | void showup(); 152 | virtual void hide(); 153 | bool shown_called(); 154 | void resize(int, int, int, int); 155 | bool __main; 156 | Double_Window(int w, int h, char* const label = 0) : 157 | Fl_Double_Window(w, h, label) 158 | { 159 | w__shown = false; 160 | __main = false; 161 | supposed_to_be_shown = false; 162 | } 163 | }; 164 | 165 | class DND_Box: public Fl_Box 166 | { 167 | int handle(int event); 168 | public: 169 | DND_Box(int x, int y, int w, int h, char* const label = 0) : 170 | Fl_Box(x, y, w, h, label) 171 | { 172 | clear_visible_focus(); 173 | } 174 | void dnd(); 175 | protected: 176 | char evt_txt[PATH_MAX]; 177 | }; 178 | 179 | /** 180 | * Browser class. 181 | * adds a filter and a name loader method (load_n) to the Fl_Browser class. 182 | * also adds "right-click to reset to initial" support 183 | */ 184 | class Browser: public Fl_Hold_Browser, public PWid 185 | { 186 | /// current filter string 187 | char* filter; 188 | /// adds right-click support 189 | int handle(int event); 190 | /// local memory of the ROM ID that we have currently loaded 191 | int selected_rom; 192 | public: 193 | Browser(int x, int y, int w, int h, char* const label = 0) : 194 | Fl_Hold_Browser(x, y, w, h, label) 195 | { 196 | filter = 0; 197 | has_scrollbar(VERTICAL); 198 | selected_rom = -1; 199 | } 200 | void set_id(int v, int l = 0); 201 | void set_value(int v); 202 | int get_value() const; 203 | void set_filter(const char* filter_string); 204 | void apply_filter(); 205 | void reset(); 206 | void load_n(int type, int rom_id, int preset = -1); 207 | int* get_minimax() 208 | { 209 | minimax[1] = size() - 1; 210 | return minimax; 211 | } 212 | }; 213 | 214 | /** 215 | * ROM_Choice class. 216 | * adds "right-click to reset to initial" and mousewheel support 217 | */ 218 | class ROM_Choice: public Fl_Choice, public PWid 219 | { 220 | int no_user; 221 | int handle(int event); 222 | void dependency(int v, bool get) const; 223 | public: 224 | ROM_Choice(int x, int y, int w, int h, char* const label = 0) : 225 | Fl_Choice(x, y, w, h, label) 226 | { 227 | no_user = 1; 228 | } 229 | void set_id(int v, int l = 0); 230 | void set_value(int v); 231 | int get_value() const; 232 | }; 233 | 234 | /** 235 | * Input class. 236 | * used by name and filter inputs 237 | * changes to FL_Input: disabled the unfocus on enter key 238 | */ 239 | class Input: public Fl_Input 240 | { 241 | int handle(int ev); 242 | public: 243 | Input(int x, int y, int w, int h, char const* label = 0) : 244 | Fl_Input(x, y, w, h, label) 245 | { 246 | ; 247 | } 248 | }; 249 | 250 | /** 251 | * Value_Input class. 252 | * adds "right-click to reset to initial" and mousewheel support 253 | */ 254 | class Value_Input: public Fl_Value_Input, public PWid 255 | { 256 | int handle(int event); 257 | public: 258 | Value_Input(int x, int y, int w, int h, char const* label = 0) : 259 | Fl_Value_Input(x, y, w, h, label) 260 | { 261 | ; 262 | } 263 | void set_id(int v, int l = 0); 264 | void set_value(int); 265 | int get_value() const; 266 | }; 267 | 268 | /** 269 | * Formatted_Output class. 270 | * shows formatted parameter values 271 | */ 272 | class Formatted_Output: public Fl_Value_Output 273 | { 274 | virtual int format(char* buf); 275 | int id; 276 | int layer; 277 | public: 278 | Formatted_Output(int x, int y, int w, int h, char const* label = 0) : 279 | Fl_Value_Output(x, y, w, h, label) 280 | { 281 | ; 282 | } 283 | void set_value(int p, int l, int v); 284 | }; 285 | 286 | /** 287 | * Value_Output class. 288 | * adds "right-click to reset to initial" and mousewheel support 289 | */ 290 | class Value_Output: public Fl_Value_Output, public PWid 291 | { 292 | int handle(int event); 293 | int double_click_value; // used for patchcord double click toggle 294 | public: 295 | Value_Output(int x, int y, int w, int h, char const* label = 0) : 296 | Fl_Value_Output(x, y, w, h, label) 297 | { 298 | double_click_value = 0; 299 | tooltip(_ma); 300 | } 301 | void set_id(int v, int l = 0); 302 | void set_value(int v); 303 | int get_value() const; 304 | }; 305 | 306 | /** 307 | * Slider class. 308 | * adds "right-click to reset to initial" and mousewheel support 309 | */ 310 | class Slider: public Fl_Slider, public PWid 311 | { 312 | int handle(int event); 313 | void draw_scale(int, int, int, int); 314 | mutable int prev_value; 315 | public: 316 | Slider(int x, int y, int w, int h, char const* label = 0) : 317 | Fl_Slider(x, y, w, h, label) 318 | { 319 | prev_value = -96; 320 | id_layer[0] = -1; // used by cc sliders 321 | } 322 | void set_id(int v, int l = 0); 323 | void set_value(int v); 324 | int get_value() const; 325 | void draw(); 326 | }; 327 | 328 | /** 329 | * Spinner class. 330 | * adds "right-click to reset to initial" and mousewheel support 331 | */ 332 | class Spinner: public Fl_Spinner, public PWid 333 | { 334 | int handle(int event); 335 | public: 336 | Spinner(int x, int y, int w, int h, char const* label = 0) : 337 | Fl_Spinner(x, y, w, h, label) 338 | { 339 | ; 340 | } 341 | void set_id(int v, int l = 0); 342 | void set_value(int v); 343 | int get_value() const; 344 | }; 345 | 346 | /** 347 | * Counter class. 348 | * adds "right-click to reset to initial" and mousewheel support 349 | */ 350 | class Counter: public Fl_Counter, public PWid 351 | { 352 | int handle(int event); 353 | public: 354 | Counter(int x, int y, int w, int h, char const* label = 0) : 355 | Fl_Counter(x, y, w, h, label) 356 | { 357 | ; 358 | } 359 | void set_id(int v, int l = 0); 360 | void set_value(int v); 361 | int get_value() const; 362 | }; 363 | 364 | /** 365 | * Group class. 366 | * adds "right-click to reset to initial" support 367 | */ 368 | class Group: public Fl_Group, public PWid 369 | { 370 | int handle(int event); 371 | void dependency(int v) const; 372 | public: 373 | Group(int x, int y, int w, int h, char const* label = 0) : 374 | Fl_Group(x, y, w, h, label) 375 | { 376 | ; 377 | } 378 | void set_id(int v, int l = 0); 379 | virtual void set_value(int); 380 | virtual int get_value() const; 381 | }; 382 | 383 | /** 384 | * Fl_Knob class. 385 | * adds "right-click to reset to initial" and mousewheel support 386 | */ 387 | class Fl_Knob: public Fl_Valuator, public PWid 388 | { 389 | int _type; 390 | float _percent; 391 | int _scaleticks; 392 | short a1, a2; 393 | void draw(); 394 | int handle(int event); 395 | void draw_scale(const int ox, const int oy, const int side); 396 | void draw_cursor(const int ox, const int oy, const int side); 397 | void shadow(const int offs, const uchar r, uchar g, uchar b); 398 | void dependency(int v) const; 399 | public: 400 | Fl_Knob(int xx, int yy, int ww, int hh, const char *l = 0); 401 | void cursor(const int pc); 402 | void scaleticks(const int tck); 403 | void set_id(int v, int l = 0); 404 | void set_value(int); 405 | int get_value() const; 406 | }; 407 | 408 | /** 409 | * Button class. 410 | * adds "right-click to reset to initial" support 411 | */ 412 | class Button: public Fl_Button, public PWid 413 | { 414 | int handle(int event); 415 | public: 416 | Button(int x, int y, int w, int h, char const* label = 0) : 417 | Fl_Button(x, y, w, h, label) 418 | { 419 | id_layer[0] = -1; 420 | } 421 | void set_id(int v, int l = 0); 422 | void set_value(int v); 423 | int get_value() const; 424 | // void dependency(int v) const; 425 | }; 426 | 427 | class Fixed_Button: public Fl_Button 428 | { 429 | int handle(int event); 430 | public: 431 | Fixed_Button(int x, int y, int w, int h, char const* label = 0) : 432 | Fl_Button(x, y, w, h, label) 433 | { 434 | ; 435 | } 436 | }; 437 | 438 | /** 439 | * Choice class. 440 | * adds "right-click to reset to initial" and mousewheel support 441 | */ 442 | class Choice: public Fl_Choice, public PWid 443 | { 444 | int handle(int event); 445 | void dependency(int v) const; 446 | public: 447 | Choice(int x, int y, int w, int h, char* const label = 0) : 448 | Fl_Choice(x, y, w, h, label) 449 | { 450 | ; 451 | } 452 | void set_id(int v, int l = 0); 453 | void set_value(int v); 454 | int get_value() const; 455 | }; 456 | 457 | /** 458 | * PCS_Choice class. 459 | * special class for patchcord sources 460 | */ 461 | class PCS_Choice: public Choice 462 | { 463 | int index[78]; 464 | void init(int); 465 | public: 466 | PCS_Choice(int x, int y, int w, int h, char* const label = 0) : 467 | Choice(x, y, w, h, label) 468 | { 469 | minimax[1] = 167; 470 | tooltip(_ms); 471 | } 472 | void set_value(int v); 473 | int get_value() const; 474 | }; 475 | 476 | /** 477 | * PCD_Choice class. 478 | * special class for patchcord destinations 479 | */ 480 | class PCD_Choice: public Choice 481 | { 482 | int index[68]; 483 | void init(); 484 | public: 485 | PCD_Choice(int x, int y, int w, int h, char* const label = 0) : 486 | Choice(x, y, w, h, label) 487 | { 488 | init(); 489 | minimax[1] = 191; 490 | tooltip(_md); 491 | } 492 | void set_value(int v); 493 | int get_value() const; 494 | }; 495 | 496 | /** 497 | * PPCS_Choice class. 498 | * special class for preset patchcord sources 499 | */ 500 | class PPCS_Choice: public Choice 501 | { 502 | int index[31]; 503 | char sources; 504 | void init(int, int); 505 | public: 506 | PPCS_Choice(int x, int y, int w, int h, char* const label = 0) : 507 | Choice(x, y, w, h, label) 508 | { 509 | minimax[1] = 160; 510 | tooltip(_ms); 511 | sources = 0; 512 | } 513 | void set_value(int v); 514 | int get_value() const; 515 | }; 516 | 517 | /** 518 | * PPCD_Choice class. 519 | * special class for preset patchcord destinations 520 | */ 521 | class PPCD_Choice: public Choice 522 | { 523 | int index[28]; 524 | char destinations; 525 | void init(int); 526 | public: 527 | PPCD_Choice(int x, int y, int w, int h, char* const label = 0) : 528 | Choice(x, y, w, h, label) 529 | { 530 | minimax[1] = 131; 531 | tooltip(_md); 532 | destinations = 0; 533 | } 534 | void set_value(int v); 535 | int get_value() const; 536 | }; 537 | 538 | /** 539 | * Envelope_Editor class. 540 | * features zooming and display of multiple envelopes. mousewheel switches 541 | * the currently selected envelope 542 | */ 543 | class Envelope_Editor: public Fl_Box 544 | { 545 | struct envelope 546 | { 547 | int stage[6][2]; // x/y coordinates of the 6 envelope stages 548 | char mode, repeat; 549 | }; 550 | envelope env[3]; 551 | enum 552 | { 553 | ATK_1, DCY_1, RLS_1, ATK_2, DCY_2, RLS_2 554 | }; 555 | enum 556 | { 557 | VOLUME, FILTER, AUXILIARY 558 | }; 559 | enum 560 | { 561 | FACTORY, 562 | TIME_BASED, 563 | TEMPO_BASED, 564 | OVERLAY, 565 | SYNC_VOICE_VIEW, 566 | VOLUME_SELECTED, 567 | FILTER_SELECTED, 568 | AUXILIARY_SELECTED, 569 | CPY_VOLUME, 570 | CPY_FILTER, 571 | CPY_AUXILIARY, 572 | SHAPE_A, 573 | SHAPE_B, 574 | SHAPE_C, 575 | SHAPE_D 576 | }; 577 | virtual int handle(int event); 578 | void draw(); 579 | void draw_b_label(char, Fl_Color); 580 | void draw_envelope(unsigned char type, int x0, int y0, int luma); 581 | void copy_envelope(unsigned char src, unsigned char dst); 582 | void set_shape(unsigned char dst, char shape); 583 | char layer; 584 | int ee_x0; 585 | int ee_y0; 586 | int ee_w; 587 | int ee_h; 588 | int mode_button[5]; // x0 of the mode buttons (width = 75, h = 20) 589 | int copy_button[6]; 590 | int shape_button[4]; 591 | char button_hover; 592 | unsigned char zoomlevel; 593 | int dragbox[6][2]; 594 | char hover; 595 | int hover_list; // 1, 2, 4, 8, 16, 32 596 | int push_x; 597 | int push_y; 598 | unsigned char mode; 599 | unsigned char modes; 600 | bool overlay; 601 | bool button_push; 602 | 603 | public: 604 | Envelope_Editor(int x, int y, int w, int h, char* const label = 0) : 605 | Fl_Box(x, y, w, h, label) 606 | { 607 | zoomlevel = 4; 608 | modes = 3; 609 | mode = VOLUME; 610 | hover = -1; 611 | overlay = false; 612 | // initialize with some fake data 613 | set_shape(VOLUME, SHAPE_D); 614 | set_shape(FILTER, SHAPE_A); 615 | set_shape(AUXILIARY, SHAPE_C); 616 | } 617 | void set_data(unsigned char type, int* stages, char mode, char repeat); 618 | void set_layer(char l); 619 | void sync_view(char l, char m = 0, float z = .0, bool o = false); 620 | }; 621 | 622 | /** 623 | * Piano class. 624 | * features a 127 key keyboard with velocity setting, pitch and modwheel, 625 | * 3 footswitches, layer transpose, layer, arp and link range/fade setup 626 | */ 627 | class Piano: public Fl_Box 628 | { 629 | virtual int handle(int event); 630 | void draw(); 631 | void draw_ranges(); 632 | void draw_piano(); 633 | void draw_highlights(); 634 | void draw_case(); 635 | void draw_curve(int type); 636 | void switch_mode(); 637 | void commit_changes(); 638 | void calc_hovered(int x, int y); 639 | enum 640 | { 641 | LOW_KEY, LOW_FADE, HIGH_KEY, HIGH_FADE 642 | }; 643 | enum 644 | { 645 | NONE = -1, PRESET_ARP = 4, MASTER_ARP, LINK_ONE, LINK_TWO, PIANO 646 | }; 647 | enum 648 | { 649 | KEYRANGE, VELOCITY, REALTIME 650 | }; 651 | enum 652 | { 653 | D_RANGES = 1, D_KEYS, D_HIGHLIGHT = 4, D_CASE = 8 654 | }; 655 | unsigned char mode; // 0 = keyrange, 1 = velocity, 2 = realtime 656 | unsigned char modes; 657 | int keyboard_x0, keyboard_y0, keyboard_w, keyboard_h; 658 | char h_white, w_white, h_black, w_black; 659 | int taste_x0[128][2]; 660 | int dragbox[3][8][4][2]; // mode, layer, type, x/y 661 | char highlight_dragbox[8][4]; 662 | char prev_key_value[3][8][4]; 663 | char new_key_value[3][8][4]; 664 | char pushed; // currently dragged part 665 | char pushed_range; // currently dragged range (low_key, low_fade...) 666 | char hovered_key, play_hovered_key; 667 | int active_keys[128]; 668 | char previous_hovered_key; 669 | char selected_transpose_layer; 670 | char transpose[4]; 671 | int push_x; // used for setting the key velocity 672 | char key_velocity; 673 | Fl_Color color_white; 674 | Fl_Color color_black; 675 | public: 676 | Piano(int x, int y, int w, int h, char* const label = 0) : 677 | Fl_Box(x, y, w, h, label) 678 | { 679 | color_white = FL_FOREGROUND_COLOR; 680 | color_black = FL_BACKGROUND_COLOR; 681 | // tasten- h�hen/-breiten 682 | h_white = 32; 683 | w_white = 13; // 15 684 | h_black = 20; 685 | w_black = 7; // 9 686 | hovered_key = NONE; 687 | previous_hovered_key = NONE; 688 | play_hovered_key = 0; 689 | pushed = NONE; 690 | pushed_range = NONE; 691 | mode = KEYRANGE; // load piano at startup 692 | key_velocity = 100; 693 | modes = 3; 694 | selected_transpose_layer = 0; 695 | for (int i = 0; i < 128; i++) 696 | active_keys[i] = 0; 697 | // widget is 921 pixels wide 698 | // height is about 162 699 | // calculate our position and key koordinates 700 | keyboard_x0 = this->x() + 10; 701 | keyboard_y0 = this->y() + 11; 702 | keyboard_w = 75 * (w_white - 1); 703 | keyboard_h = h_white; 704 | int offset = 0; 705 | for (int i = 0; i < 11; i++) 706 | { 707 | // for each octave on the keyboard 708 | int octave = i * 12; 709 | taste_x0[0 + octave][0] = keyboard_x0 + offset; 710 | taste_x0[0 + octave][1] = 0; 711 | taste_x0[1 + octave][0] = keyboard_x0 + 9 + offset; 712 | taste_x0[1 + octave][1] = 1; 713 | taste_x0[2 + octave][0] = keyboard_x0 + w_white - 1 + offset; 714 | taste_x0[2 + octave][1] = 0; 715 | taste_x0[3 + octave][0] = keyboard_x0 + 9 + (w_white - 1) + offset; 716 | taste_x0[3 + octave][1] = 1; 717 | taste_x0[4 + octave][0] = keyboard_x0 + 2 * (w_white - 1) + offset; 718 | taste_x0[4 + octave][1] = 0; 719 | taste_x0[5 + octave][0] = keyboard_x0 + 3 * (w_white - 1) + offset; 720 | taste_x0[5 + octave][1] = 0; 721 | taste_x0[6 + octave][0] = keyboard_x0 + 9 + 3 * (w_white - 1) + offset; 722 | taste_x0[6 + octave][1] = 1; 723 | taste_x0[7 + octave][0] = keyboard_x0 + 4 * (w_white - 1) + offset; 724 | taste_x0[7 + octave][1] = 0; 725 | if (i == 10) // keyboard is smaller than full 10 full octaves 726 | break; 727 | taste_x0[8 + octave][0] = keyboard_x0 + 9 + 4 * (w_white - 1) + offset; 728 | taste_x0[8 + octave][1] = 1; 729 | taste_x0[9 + octave][0] = keyboard_x0 + 5 * (w_white - 1) + offset; 730 | taste_x0[9 + octave][1] = 0; 731 | taste_x0[10 + octave][0] = keyboard_x0 + 9 + 5 * (w_white - 1) + offset; 732 | taste_x0[10 + octave][1] = 1; 733 | taste_x0[11 + octave][0] = keyboard_x0 + 6 * (w_white - 1) + offset; 734 | taste_x0[11 + octave][1] = 0; 735 | offset += 7 * (w_white - 1); 736 | } 737 | // y-koordinaten der dragboxes 738 | for (unsigned char m = 0; m < 3; m++) 739 | for (unsigned char i = 0; i < 4; i++) 740 | for (unsigned char j = 0; j < 4; j++) 741 | { 742 | if (j == LOW_KEY || j == HIGH_KEY) 743 | dragbox[m][i][j][1] = keyboard_y0 + 5 + keyboard_h + i * 18; 744 | else 745 | dragbox[m][i][j][1] = keyboard_y0 + 5 + keyboard_h + i * 18 + 8; 746 | } 747 | // arps 748 | dragbox[0][4][LOW_KEY][1] = dragbox[0][3][LOW_KEY][1] + 20; 749 | dragbox[0][4][HIGH_KEY][1] = dragbox[0][4][LOW_KEY][1]; 750 | dragbox[0][5][LOW_KEY][1] = dragbox[0][4][LOW_KEY][1] + 10; 751 | dragbox[0][5][HIGH_KEY][1] = dragbox[0][5][LOW_KEY][1]; 752 | // links 753 | dragbox[0][6][LOW_KEY][1] = dragbox[0][5][LOW_KEY][1] + 10; 754 | dragbox[0][6][HIGH_KEY][1] = dragbox[0][6][LOW_KEY][1]; 755 | dragbox[0][7][LOW_KEY][1] = dragbox[0][6][LOW_KEY][1] + 10; 756 | dragbox[0][7][HIGH_KEY][1] = dragbox[0][7][LOW_KEY][1]; 757 | // x koordinaten 758 | for (unsigned char m = 0; m < 3; m++) 759 | for (unsigned char i = 0; i < 8; i++) 760 | set_range_values(m, i, 0, 0, 127, 0); 761 | } 762 | 763 | void set_range_values(unsigned char md, unsigned char layer, unsigned char low_k, unsigned char low_f, 764 | unsigned char high_k, unsigned char high_f); 765 | void set_transpose(char l1, char l2, char l3, char l4); 766 | void select_transpose_layer(char l); 767 | void set_mode(char m); 768 | void activate_key(char value, unsigned char key); 769 | void reset_active_keys(); 770 | }; 771 | 772 | /** 773 | * MiniPiano. 774 | * 2 octaves of key goodness 775 | */ 776 | class MiniPiano: public Fl_Box 777 | { 778 | virtual int handle(int event); 779 | void draw(); 780 | void draw_piano(); 781 | void draw_highlights(); 782 | void draw_case(); 783 | void calc_hovered(int x, int y); 784 | void shift_octave(int); 785 | 786 | int keyboard_x0, keyboard_y0, keyboard_w, keyboard_h; 787 | float key_x, key_w, key_y; 788 | float h_white, w_white, h_black, w_black; 789 | float taste_x0[128][2]; 790 | int octave; 791 | 792 | Fl_Color color_white; 793 | Fl_Color color_black; 794 | 795 | int hovered_key, play_hovered_key; 796 | int active_keys[128]; 797 | int previous_hovered_key; 798 | int pushed; 799 | int push_x; // used for setting the key velocity 800 | int key_velocity; 801 | enum 802 | { 803 | NONE = -1, PIANO 804 | }; 805 | enum 806 | { 807 | D_KEYS = 2, D_HIGHLIGHT = 4, D_CASE = 8 808 | }; 809 | 810 | public: 811 | MiniPiano(int x, int y, int w, int h, char* const label = 0) : 812 | Fl_Box(x, y, w, h, label) 813 | { 814 | pushed = NONE; 815 | hovered_key = NONE; 816 | previous_hovered_key = NONE; 817 | key_velocity = 100; 818 | for (int i = 0; i < 128; i++) 819 | active_keys[i] = 0; 820 | octave = 4; 821 | color_white = FL_FOREGROUND_COLOR; 822 | color_black = FL_BACKGROUND_COLOR; 823 | } 824 | void activate_key(int value, int key); 825 | void reset_active_keys(); 826 | }; 827 | 828 | // ################### 829 | // 830 | // ################### 831 | class Pitch_Slider: public Fl_Slider 832 | { 833 | int handle(int event); 834 | bool hold; 835 | public: 836 | Pitch_Slider(int x, int y, int w, int h, char* const label = 0) : 837 | Fl_Slider(x, y, w, h, label) 838 | { 839 | hold = false; 840 | } 841 | }; 842 | 843 | // ################### 844 | // 845 | // ################### 846 | class Step_Type: public Fl_Group 847 | { 848 | int handle(int event); 849 | int s; 850 | public: 851 | Step_Type(int x, int y, int w, int h, char* const label = 0) : 852 | Fl_Group(x, y, w, h, label) 853 | { 854 | p = -1; 855 | c = -1; 856 | } 857 | void set_step(int step); 858 | int p; // prev value 859 | int c; // current value 860 | }; 861 | 862 | // ################### 863 | // 864 | // ################### 865 | class Step_Value: public Fl_Spinner 866 | { 867 | virtual int format(char* buf); 868 | int handle(int event); 869 | int id; 870 | int s; 871 | public: 872 | Step_Value(int x, int y, int w, int h, char const* label = 0) : 873 | Fl_Spinner(x, y, w, h, label) 874 | { 875 | ; 876 | } 877 | void set_id(int i, int step); 878 | }; 879 | 880 | // ################### 881 | // 882 | // ################### 883 | class Step_Drop : public Fl_Input_Choice 884 | { 885 | virtual int format(char* buf); 886 | int handle(int event); 887 | int id; 888 | int s; 889 | public: 890 | Step_Drop(int x, int y, int w, int h, char const* label = 0) : 891 | Fl_Input_Choice(x, y, w, h, label) 892 | { 893 | menubutton()->add("1-32"); 894 | menubutton()->add("1-16T"); 895 | menubutton()->add("1-32D"); 896 | menubutton()->add("1-16"); 897 | menubutton()->add("1-8T"); 898 | menubutton()->add("1-16D"); 899 | menubutton()->add("1-8"); 900 | menubutton()->add("1-4T"); 901 | menubutton()->add("1-8D"); 902 | menubutton()->add("1-4"); 903 | menubutton()->add("1-2T"); 904 | menubutton()->add("1-4D"); 905 | menubutton()->add("1-2"); 906 | menubutton()->add("1-1T"); 907 | menubutton()->add("1-2D"); 908 | menubutton()->add("1-1"); 909 | menubutton()->add("2-1T"); 910 | menubutton()->add("1-1D"); 911 | menubutton()->add("2-1"); 912 | } 913 | void set_id(int i, int step); 914 | }; 915 | 916 | 917 | 918 | 919 | // ################### 920 | // 921 | // ################### 922 | class Step_Offset: public Fl_Value_Slider 923 | { 924 | int handle(int event); 925 | void draw_scale(int, int, int, int); 926 | int s; 927 | char root; 928 | protected: 929 | void draw(int X, int Y, int W, int H); 930 | public: 931 | Step_Offset(int x, int y, int w, int h, char* const label = 0) : 932 | Fl_Value_Slider(x, y, w, h, label) 933 | { 934 | root = 0; 935 | } 936 | void draw(); 937 | void set_step(int step); 938 | void set_root(char); 939 | }; 940 | 941 | // ################### 942 | // 943 | // ################### 944 | class Text_Display: public Fl_Text_Display 945 | { 946 | virtual void resize(int X, int Y, int W, int H); 947 | int c_w; 948 | public: 949 | Text_Display(int x, int y, int w, int h, char const* label = 0) : 950 | Fl_Text_Display(x, y, w, h, label) 951 | { 952 | textfont(FL_COURIER); 953 | textsize(12); 954 | fl_font(FL_COURIER, 12); 955 | c_w = fl_width("w"); 956 | wrap_mode(1, w / c_w - 4); 957 | } 958 | }; 959 | #endif /* WIDGETS_H_ */ 960 | /** @} */ 961 | -------------------------------------------------------------------------------- /lib/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /prodatum.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=prodatum 3 | Comment=Sysex editor for the E-Mu Protheus 1000/2000 synthesizer family 4 | Exec=prodatum 5 | Terminal=false 6 | StartupNotify=false 7 | Icon=prodatum 8 | Type=Application 9 | Categories=Audio;AudioVideo;Midi;X-Alsa; 10 | -------------------------------------------------------------------------------- /resources/a2k-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/a2k-size.png -------------------------------------------------------------------------------- /resources/arp-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-busy.png -------------------------------------------------------------------------------- /resources/arp-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-commit.png -------------------------------------------------------------------------------- /resources/arp-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-export.png -------------------------------------------------------------------------------- /resources/arp-exported.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-exported.png -------------------------------------------------------------------------------- /resources/arp-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-import.png -------------------------------------------------------------------------------- /resources/arp-save-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-save-1.png -------------------------------------------------------------------------------- /resources/arp-save-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-save-2.png -------------------------------------------------------------------------------- /resources/arp-save-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-save-3.png -------------------------------------------------------------------------------- /resources/arp-save-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/arp-save-4.png -------------------------------------------------------------------------------- /resources/cs-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/cs-size.png -------------------------------------------------------------------------------- /resources/folder-structure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/folder-structure.png -------------------------------------------------------------------------------- /resources/new-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/new-menu.png -------------------------------------------------------------------------------- /resources/p2k-size.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/p2k-size.png -------------------------------------------------------------------------------- /resources/preset-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/preset-busy.png -------------------------------------------------------------------------------- /resources/preset-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/preset-export.png -------------------------------------------------------------------------------- /resources/preset-exported.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/preset-exported.png -------------------------------------------------------------------------------- /resources/preset-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/preset-import.png -------------------------------------------------------------------------------- /resources/prodatum.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/prodatum.ico -------------------------------------------------------------------------------- /resources/prodatum.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/prodatum.png -------------------------------------------------------------------------------- /resources/setup-export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/setup-export.png -------------------------------------------------------------------------------- /resources/setup-exported.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/setup-exported.png -------------------------------------------------------------------------------- /resources/setup-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/haxorhax/prodatum/f26a28aea309e9d526d4a3ffa9bf1130f7049268/resources/setup-import.png -------------------------------------------------------------------------------- /resources/windows.rc: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | 1 VERSIONINFO 4 | FILEVERSION PRODATUM_VERSION_MAJOR,PRODATUM_VERSION_MINOR,PRODATUM_VERSION_PATCH,1 5 | PRODUCTVERSION PRODATUM_VERSION_MAJOR,PRODATUM_VERSION_MINOR,PRODATUM_VERSION_PATCH,1 6 | BEGIN 7 | BLOCK "StringFileInfo" 8 | BEGIN 9 | BLOCK "040904E4" 10 | BEGIN 11 | VALUE "CompanyName", "haxorhax" 12 | VALUE "FileDescription", "prodatum" 13 | VALUE "FileVersion", PRODATUM_VERSION 14 | VALUE "InternalName", "prodatum" 15 | VALUE "LegalCopyright", "Jan Eidtmann" 16 | VALUE "OriginalFilename", "prodatum" 17 | VALUE "ProductName", "prodatum" 18 | VALUE "ProductVersion", PRODATUM_VERSION 19 | END 20 | END 21 | 22 | BLOCK "VarFileInfo" 23 | BEGIN 24 | VALUE "Translation", 0x409, 1252 25 | END 26 | END 27 | 28 | 2 ICON "resources\prodatum.ico" -------------------------------------------------------------------------------- /src/Fl_Scope.cpp: -------------------------------------------------------------------------------- 1 | #ifndef Fl_Scope_Version 2 | #define Fl_Scope_Version "V0.0.2" 3 | #define DEBUG 0 4 | 5 | #define MAX 127 6 | 7 | /****************************************************************** 8 | * Fl_Scope.cxx 9 | * 10 | * A simple widget that simulates an oscilloscope type trace. 11 | * Input is 8 bit. 12 | * Data starts from left and moves right, then starts scrolling. 13 | * The buffer is teh same as the width of the widget. 14 | * 15 | * Use ->add(unsigned char); to add next data point to the scope. 16 | * 17 | * Author: Michael Pearce 18 | * 19 | * Started: 18 March 2003 20 | * 21 | * Copyright: Copyright 2003 Michael Pearce All Rights reserved. 22 | * 23 | * Licence: GNU/GPL 24 | * 25 | * This program is free software; you can redistribute it and/or 26 | * modify it under the terms of the GNU General Public License 27 | * as published by the Free Software Foundation; either version 2 28 | * of the License, or (at your option) any later version. 29 | * 30 | * This program is distributed in the hope that it will be useful, 31 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | * GNU General Public License for more details. 34 | * 35 | * You should have received a copy of the GNU General Public License 36 | * along with this program (GNU.txt); if not, write to the Free Software 37 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 38 | * or visit http://www.gnu.org/licenses/licenses.html 39 | * 40 | ************************************************************ 41 | * Version Information 42 | ************************************************************ 43 | * V0.1.0 - 14 February 2005 44 | * Lots of changes: 45 | * Different Redraw modes added 46 | * Uses signed int for data 47 | * Has signed and unsigned display modes 48 | ************************************************************ 49 | * V0.0.2 - 6 August 2003 50 | * Moved x(int) etc to protected. 51 | ************************************************************ 52 | * V0.0.1 - 4 August 2003 53 | * Work on the drawing functions.... seems to keep a blank! 54 | ************************************************************ 55 | * V0.0.0 - 1 August 2003 56 | * Modified for use with Makefile. 57 | * Added Version() function to return code version. 58 | ************************************************************/ 59 | 60 | 61 | #include 62 | #include 63 | #include "Fl_Scope.h" 64 | #include 65 | #include 66 | 67 | void Fl_Scope::draw() 68 | { 69 | draw(x(),y(),w(),h()); 70 | } 71 | 72 | void Fl_Scope::draw(int xx, int yy, int ww, int hh) 73 | { 74 | //unsigned char *Ptr,*Ptr2; 75 | int *Ptr,*Ptr2; 76 | int count; 77 | int Yval,Yval2; 78 | 79 | /* Push clip for drawing */ 80 | fl_push_clip(xx,yy,ww,hh); 81 | 82 | /* Draw Main Box */ 83 | fl_draw_box(FL_FLAT_BOX,xx,yy,ww,hh,_BackColour); 84 | 85 | fl_color(_TraceColour); 86 | 87 | /* Draw the scope Data */ 88 | Ptr2=Ptr=ScopeData; 89 | Ptr2++; 90 | for(count=0;count ScopeDataSize)ScopeDataPos=0; 148 | 149 | switch(TraceType) 150 | { 151 | default: 152 | case FL_SCOPE_TRACE_SCROLL: 153 | /* Move Data to left then add data at the end */ 154 | Ptr=Ptr2=ScopeData; 155 | Ptr2++; 156 | for(count=0;count. 17 | */ 18 | 19 | #ifdef WIN32 20 | #include 21 | #else 22 | #include 23 | #endif 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "ui.h" 34 | 35 | extern PD_UI* ui; 36 | extern PXK* pxk; 37 | 38 | Cfg::Cfg(int device_id) 39 | { 40 | pmesg("Cfg::Cfg(%d) \n", device_id); 41 | // default export dir 42 | #ifdef WIN32 43 | set_export_dir(getenv("USERPROFILE")); 44 | #else 45 | set_export_dir(getenv("HOME")); 46 | #endif 47 | // check for portable config dir 48 | struct stat sbuf; 49 | config_dir[0] = 0; 50 | if (stat("./prodatum-config", &sbuf) == 0) 51 | { 52 | // check write permissions 53 | FILE *fp = fopen("./prodatum-config/.___prdtmchck", "w"); 54 | if (fp == NULL) 55 | { 56 | if (errno == EACCES) 57 | fl_alert("You don't have write permission at ./prodatum-config/."); 58 | } 59 | else 60 | { 61 | fclose(fp); 62 | #ifdef WIN32 63 | _unlink("./prodatum-config/.___prdtmchck"); 64 | #else 65 | unlink("./prodatum-config/.___prdtmchck"); 66 | #endif 67 | snprintf(config_dir, PATH_MAX, "./prodatum-config"); 68 | ui->pref_info_portable->show(); 69 | ui->open_info_portable->show(); 70 | } 71 | } 72 | if (config_dir[0] == 0) 73 | { 74 | ui->pref_info_portable->hide(); 75 | ui->open_info_portable->hide(); 76 | #ifdef WIN32 77 | snprintf(config_dir, PATH_MAX, "%s/prodatum", getenv("APPDATA")); 78 | #else 79 | snprintf(config_dir, PATH_MAX, "%s/.prodatum", export_dir); 80 | #endif 81 | // check/create cfg dir 82 | if (stat(config_dir, &sbuf) == -1) 83 | { 84 | if (mkdir(config_dir, S_IRWXU| S_IRWXG | S_IROTH | S_IXOTH) == -1) 85 | { 86 | fl_alert("Could not create configuration directory:\n%s - %s\n", config_dir, strerror(errno)); 87 | fprintf(stderr, "Could not create configuration directory:\n%s - %s\n", config_dir, strerror(errno)); 88 | #ifdef WIN32 89 | fflush(stderr); 90 | #endif 91 | } 92 | } 93 | } 94 | // defaults 95 | defaults.resize(NOOPTION, 0); 96 | option.resize(NOOPTION, 0); 97 | defaults[CFG_MIDI_OUT] = -1; 98 | defaults[CFG_MIDI_IN] = -1; 99 | defaults[CFG_MIDI_THRU] = -1; 100 | defaults[CFG_CONTROL_CHANNEL] = 0; 101 | defaults[CFG_AUTOMAP] = 1; 102 | defaults[CFG_DEVICE_ID] = device_id; 103 | defaults[CFG_MASTER_VOLUME] = 80; 104 | defaults[CFG_SPEED] = -1; 105 | defaults[CFG_CLOSED_LOOP_UPLOAD] = 1; 106 | defaults[CFG_CLOSED_LOOP_DOWNLOAD] = 1; 107 | defaults[CFG_TOOLTIPS] = 1; 108 | defaults[CFG_KNOBMODE] = 1; 109 | defaults[CFG_CONFIRM_EXIT] = 0; 110 | defaults[CFG_CONFIRM_RAND] = 1; 111 | defaults[CFG_CONFIRM_DISMISS] = 1; 112 | defaults[CFG_SYNCVIEW] = 0; 113 | defaults[CFG_DRLS] = 1; 114 | defaults[CFG_LOG_SYSEX_OUT] = 1; 115 | defaults[CFG_LOG_SYSEX_IN] = 1; 116 | defaults[CFG_LOG_EVENTS_OUT] = 0; 117 | defaults[CFG_LOG_EVENTS_IN] = 0; 118 | defaults[CFG_WINDOW_WIDTH] = 845; 119 | defaults[CFG_WINDOW_HEIGHT] = 620; 120 | defaults[CFG_BGR] = 140; 121 | defaults[CFG_BGG] = 150; 122 | defaults[CFG_BGB] = 145; 123 | defaults[CFG_BG2R] = 41; 124 | defaults[CFG_BG2G] = 48; 125 | defaults[CFG_BG2B] = 51; 126 | defaults[CFG_FGR] = 243; 127 | defaults[CFG_FGG] = 243; 128 | defaults[CFG_FGB] = 227; 129 | defaults[CFG_SLR] = 61; 130 | defaults[CFG_SLG] = 124; 131 | defaults[CFG_SLB] = 188; 132 | defaults[CFG_INR] = 241; 133 | defaults[CFG_ING] = 241; 134 | defaults[CFG_INB] = 217; 135 | defaults[CFG_KNOB_COLOR1] = 2; 136 | defaults[CFG_KNOB_COLOR2] = 2; 137 | 138 | // load config 139 | char _fname[PATH_MAX]; 140 | int sysex_id = device_id; 141 | std::ifstream file; 142 | if (sysex_id == -1) 143 | { 144 | snprintf(_fname, PATH_MAX, "%s/default.cfg", config_dir); // read default.cfg 145 | file.open(_fname); 146 | if (file.is_open()) 147 | { 148 | file >> sysex_id; 149 | file.close(); 150 | } 151 | } 152 | snprintf(_fname, PATH_MAX, "%s/%d.cfg", config_dir, sysex_id); // load actual config 153 | file.open(_fname); 154 | unsigned char i; 155 | if (!file.is_open()) // new config 156 | { 157 | for (i = 0; i < NOOPTION; i++) 158 | option[i] = defaults[i]; 159 | return; 160 | } 161 | int check_file, check = 1; 162 | for (i = 0; i < NOOPTION; i++) 163 | { 164 | file >> option[i]; 165 | check += option[i] * ((i % 5) + 3); 166 | } 167 | // checksum 168 | file >> check_file; 169 | // get export directory 170 | char buf[PATH_MAX]; 171 | char temp[PATH_MAX]; 172 | file.getline(temp, PATH_MAX); 173 | file.getline(buf, PATH_MAX); 174 | if (!file.fail()) 175 | set_export_dir(buf); 176 | file.close(); 177 | if (check_file != check) 178 | { 179 | fl_message("Configuration format changed, using default values.\n" 180 | "Sorry for the inconvenience."); 181 | #ifdef WIN32 182 | _unlink(_fname); 183 | #else 184 | unlink(_fname); 185 | #endif 186 | for (i = 0; i < NOOPTION; i++) 187 | option[i] = defaults[i]; 188 | } 189 | } 190 | 191 | Cfg::~Cfg() 192 | { 193 | pmesg("Cfg::~Cfg() \n"); 194 | if (!pxk->Synchronized()) 195 | return; 196 | // save default 197 | char _file[PATH_MAX]; 198 | snprintf(_file, PATH_MAX, "%s/default.cfg", config_dir); 199 | std::ofstream file; 200 | file.open(_file, std::ios::trunc); 201 | if (!file.is_open()) 202 | { 203 | fl_alert("Warning:\nCould not write the config file."); 204 | fprintf(stderr, "Warning:\nCould not write the config file."); 205 | #ifdef WIN32 206 | fflush(stderr); 207 | #endif 208 | return; 209 | } 210 | file << option[CFG_DEVICE_ID] << " "; 211 | file.close(); 212 | // save actual config 213 | snprintf(_file, PATH_MAX, "%s/%d.cfg", config_dir, option[CFG_DEVICE_ID]); 214 | file.open(_file, std::ios::trunc); 215 | if (!file.is_open()) 216 | { 217 | fl_alert("Warning:\nCould not write the config file."); 218 | fprintf(stderr, "Warning:\nCould not write the config file."); 219 | #ifdef WIN32 220 | fflush(stderr); 221 | #endif 222 | return; 223 | } 224 | // calc checksum 225 | int check = 1; 226 | for (unsigned char i = 0; i < NOOPTION; i++) 227 | { 228 | file << option[i] << " "; 229 | check += option[i] * ((i % 5) + 3); 230 | } 231 | file << check << std::endl; 232 | file << export_dir << std::endl; 233 | file.close(); 234 | } 235 | 236 | void Cfg::set_cfg_option(int opt, int value) 237 | { 238 | //pmesg("Cfg::set_cfg_option(%d, %d) \n", opt, value); 239 | if (opt < NOOPTION && opt >= 0) 240 | option[opt] = value; 241 | } 242 | 243 | int Cfg::get_cfg_option(int opt) const 244 | { 245 | //pmesg("Cfg::get_cfg_option(%d) \n", opt); 246 | if (opt < NOOPTION && opt >= 0) 247 | { 248 | if (opt == CFG_SPEED) 249 | return option[CFG_SPEED] * option[CFG_SPEED] * 10; 250 | return option[opt]; 251 | } 252 | return 0; 253 | } 254 | 255 | int Cfg::get_default(int opt) const 256 | { 257 | if (opt < NOOPTION && opt >= 0) 258 | return defaults[opt]; 259 | return 0; 260 | } 261 | 262 | int Cfg::getset_default(int opt) 263 | { 264 | //pmesg("Cfg::getset_default(%d) \n", opt); 265 | if (opt < NOOPTION && opt >= 0) 266 | { 267 | option[opt] = defaults[opt]; 268 | return defaults[opt]; 269 | } 270 | return 0; 271 | } 272 | 273 | const char* Cfg::get_config_dir() const 274 | { 275 | //pmesg("Cfg::get_config_dir() \n"); 276 | return config_dir; 277 | } 278 | 279 | const char* Cfg::get_export_dir() const 280 | { 281 | //pmesg("Cfg::get_export_dir() \n"); 282 | return export_dir; 283 | } 284 | 285 | bool Cfg::set_export_dir(const char* dir) 286 | { 287 | //pmesg("Cfg::set_export_dir(%s)\n", dir); 288 | struct stat sbuf; 289 | if (stat(dir, &sbuf) == -1) 290 | { 291 | fl_alert("Directory must exist."); 292 | return false; 293 | } 294 | else 295 | { 296 | char buf[PATH_MAX]; 297 | snprintf(buf, PATH_MAX, "%s/.___prdtmchck", dir); 298 | FILE *fp = fopen(buf, "w"); 299 | if (fp == NULL) 300 | { 301 | if (errno == EACCES) 302 | fl_alert("You don't have write permission at %s.", dir); 303 | return false; 304 | } 305 | else 306 | { 307 | fclose(fp); 308 | #ifdef WIN32 309 | _unlink(buf); 310 | #else 311 | unlink(buf); 312 | #endif 313 | } 314 | } 315 | snprintf(export_dir, PATH_MAX, "%s", dir); 316 | return true; 317 | } 318 | 319 | void Cfg::apply(bool colors_only) 320 | { 321 | //pmesg("Cfg::apply()\n"); 322 | ui->set_color(FL_BACKGROUND_COLOR, (unsigned char) option[CFG_BGR], (unsigned char) option[CFG_BGG], 323 | (unsigned char) option[CFG_BGB]); 324 | ui->set_color(FL_BACKGROUND2_COLOR, (unsigned char) option[CFG_BG2R], (unsigned char) option[CFG_BG2G], 325 | (unsigned char) option[CFG_BG2B]); 326 | ui->set_color(FL_FOREGROUND_COLOR, (unsigned char) option[CFG_FGR], (unsigned char) option[CFG_FGG], 327 | (unsigned char) option[CFG_FGB]); 328 | ui->set_color(FL_SELECTION_COLOR, (unsigned char) option[CFG_SLR], (unsigned char) option[CFG_SLG], 329 | (unsigned char) option[CFG_SLB]); 330 | ui->set_color(FL_INACTIVE_COLOR, (unsigned char) option[CFG_INR], (unsigned char) option[CFG_ING], 331 | (unsigned char) option[CFG_INB]); 332 | ui->set_knobcolor(0, (char) option[CFG_KNOB_COLOR1]); 333 | ui->set_knobcolor(1, (char) option[CFG_KNOB_COLOR2]); 334 | if (colors_only) 335 | return; 336 | ui->syncview = option[CFG_SYNCVIEW]; 337 | // UI INIT 338 | // midi options 339 | if (option[CFG_DEVICE_ID] != -1) 340 | { 341 | ui->device_id->value(option[CFG_DEVICE_ID]); 342 | ui->r_user_id->value(option[CFG_DEVICE_ID]); 343 | } 344 | ui->main->master_volume->value(option[CFG_MASTER_VOLUME]); 345 | ui->midi_ctrl_ch->value(option[CFG_CONTROL_CHANNEL]); 346 | ui->midi_automap->value(option[CFG_AUTOMAP]); 347 | if (option[CFG_SPEED] != -1) 348 | ui->speed->value(option[CFG_SPEED]); 349 | ui->confirm->value(option[CFG_CONFIRM_EXIT]); 350 | ui->confirm_rand->value(option[CFG_CONFIRM_RAND]); 351 | ui->confirm_dismiss->value(option[CFG_CONFIRM_DISMISS]); 352 | ((Fl_Button*) ui->g_knobmode->child(option[CFG_KNOBMODE]))->setonly(); 353 | option[CFG_CLOSED_LOOP_UPLOAD] ? ui->closed_loop_upload->set() : ui->closed_loop_upload->clear(); 354 | option[CFG_CLOSED_LOOP_DOWNLOAD] ? ui->closed_loop_download->set() : ui->closed_loop_download->clear(); 355 | ui->export_dir->value(get_export_dir()); 356 | // UI misc 357 | if (option[CFG_TOOLTIPS]) 358 | { 359 | ui->tooltips->set(); 360 | Fl_Tooltip::enable(); 361 | } 362 | else 363 | { 364 | ui->tooltips->clear(); 365 | Fl_Tooltip::disable(); 366 | } 367 | if (option[CFG_DRLS]) 368 | { 369 | ui->drls->value(1); 370 | ui->value_input->when(FL_WHEN_ENTER_KEY); 371 | } 372 | else 373 | { 374 | ui->drls->value(0); 375 | ui->value_input->when(FL_WHEN_CHANGED); 376 | } 377 | // log 378 | ui->log_sysex_out->value(option[CFG_LOG_SYSEX_OUT]); 379 | ui->log_sysex_in->value(option[CFG_LOG_SYSEX_IN]); 380 | ui->log_events_out->value(option[CFG_LOG_EVENTS_OUT]); 381 | ui->log_events_in->value(option[CFG_LOG_EVENTS_IN]); 382 | ui->main_window->resize(ui->main_window->x(), ui->main_window->y(), option[CFG_WINDOW_WIDTH], 383 | option[CFG_WINDOW_HEIGHT]); 384 | } 385 | -------------------------------------------------------------------------------- /src/debug.cpp: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include 3 | #include 4 | 5 | #if defined(NDEBUG) 6 | #else 7 | void pmesg(const char* format, ...) 8 | { 9 | va_list args; 10 | 11 | va_start(args, format); 12 | vfprintf(stderr, format, args); 13 | #ifdef WIN32 14 | fflush(stderr); 15 | #endif 16 | va_end(args); 17 | } 18 | #endif 19 | -------------------------------------------------------------------------------- /src/midi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | This file is part of prodatum. 3 | Copyright 2011-2015 Jan Eidtmann 4 | 5 | prodatum is free software: you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation, either version 3 of the License, or 8 | (at your option) any later version. 9 | 10 | prodatum is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | You should have received a copy of the GNU General Public License 16 | along with prodatum. If not, see . 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "ringbuffer.h" 26 | #include "ui.h" 27 | 28 | extern PD_UI* ui; 29 | extern Cfg* cfg; 30 | extern PXK* pxk; 31 | 32 | extern volatile bool got_answer; 33 | extern volatile bool join_bro; 34 | 35 | static bool timer_running = false; 36 | static bool midi_active = false; 37 | static bool thru_active = false; 38 | static bool process_midi_exit_flag = false; 39 | static bool automap = true; 40 | 41 | // check buffer spaces 42 | #ifdef SYNCLOG 43 | unsigned int write_space = RINGBUFFER_WRITE; 44 | unsigned int read_space = RINGBUFFER_READ; 45 | unsigned int max_write = 0; 46 | unsigned int max_read = 0; 47 | #endif 48 | 49 | /** 50 | * midi core implementation (sender/receiver/decoder). 51 | * this is where all MIDI bytes (outgoing and incoming) pass through. 52 | * this thread should never lock 53 | * in the linux version a pipe is used to notify \c process_midi_in 54 | * for new messages. 55 | */ 56 | static void process_midi(PtTimestamp, void*); 57 | /*! \fn process_midi_in 58 | * connects the MIDI receiver with the main thread. 59 | * all incoming MIDI messages are passed to this function via the 60 | * read buffer. runs in the main thread. 61 | * in the linux version this gets notified by \c process_midi if a new message 62 | * is available. on windows and mac this is a timeout function, repeated 63 | * until we close our MIDI ports 64 | */ 65 | #ifdef __linux 66 | static void process_midi_in(int fd, void*); 67 | static int p[2]; 68 | #else 69 | static void process_midi_in(void*); 70 | #endif 71 | 72 | static PmError pmerror = pmNoError; 73 | static PortMidiStream *port_in; 74 | static PortMidiStream *port_out; 75 | static PortMidiStream *port_thru; // controller port (eg keyboard) 76 | 77 | static jack_ringbuffer_t *read_buffer; 78 | static jack_ringbuffer_t *write_buffer; 79 | volatile static unsigned char midi_device_id = 127; 80 | static bool requested = false; 81 | 82 | static void show_error(void) 83 | { 84 | char* __buffer = (char*) malloc(256 * sizeof(char)); 85 | if (pmerror == -10000) 86 | Pm_GetHostErrorText(__buffer, 256); 87 | else if (pmerror < 0) 88 | snprintf(__buffer, 256, "%s", Pm_GetErrorText(pmerror)); 89 | pmerror = pmNoError; 90 | fprintf(stderr, "%s", __buffer); 91 | #ifdef WIN32 92 | fflush(stderr); 93 | #endif 94 | free(__buffer); 95 | } 96 | 97 | //static bool __midi_wait = false; 98 | static void process_midi(PtTimestamp, void*) 99 | { 100 | PmEvent ev; 101 | static unsigned char event[4]; // 3 midi bytes, one byte to distinguish device (0) and controller (1) events 102 | static unsigned char data, shift; 103 | // we only want to put full sysex messages in our ringbuffer 104 | // these are the buffers to store sysex chunks locally 105 | static unsigned char local_read_buffer[SYSEX_MAX_SIZE]; 106 | static unsigned char local_write_buffer[SYSEX_MAX_SIZE]; 107 | static unsigned int position = 3; 108 | static bool receiving_sysex = false; 109 | static bool result_out = false; 110 | static unsigned char poll = 0; 111 | if (!midi_active) 112 | { 113 | if (!jack_ringbuffer_read_space(write_buffer)) 114 | { 115 | process_midi_exit_flag = true; 116 | // __midi_wait = false; 117 | receiving_sysex = false; 118 | position = 3; 119 | result_out = false; 120 | poll = 0; 121 | return; 122 | } 123 | } 124 | do 125 | { 126 | // check if theres something from the device and write it to the read_buffer 127 | while (midi_active && Pm_Poll(port_in)) 128 | { 129 | pmerror = (PmError) Pm_Read(port_in, &ev, 1); 130 | if (pmerror < 0) 131 | { 132 | show_error(); 133 | receiving_sysex = false; 134 | break; 135 | } 136 | // got 4 bytes 137 | for (shift = 0; shift <= 24; shift += 8) 138 | { 139 | // byte for byte inspection 140 | data = (ev.message >> shift) & 0xFF; 141 | if (data == MIDI_SYSEX) 142 | { 143 | if (receiving_sysex) // Overlapping sysex messages! 144 | receiving_sysex = false; 145 | // filter sysex 146 | // e-mu proteus 147 | if (((ev.message >> 8) & 0xFF) == 0x18 && ((ev.message >> 16) & 0xFF) == 0x0F 148 | && ((ev.message >> 24) & 0xFF) == midi_device_id) 149 | receiving_sysex = true; 150 | // universal sysex 151 | else if (((ev.message >> 8) & 0xFF) == 0x7E) 152 | receiving_sysex = true; 153 | if (receiving_sysex) 154 | { 155 | position = 3; 156 | goto Copy; 157 | } 158 | else 159 | break; 160 | } 161 | // check for truncated sysex 162 | if (receiving_sysex && (((data & 0x80) == 0 || data == 0xF7) || position == 3)) 163 | { 164 | // copy data 165 | if (position < SYSEX_MAX_SIZE) 166 | { 167 | Copy: local_read_buffer[position++] = data; 168 | // hand over a complete sysex message to the main thread 169 | if (data == MIDI_EOX) 170 | { 171 | // TODO: check if its a WAIT command 172 | if (local_read_buffer[7] == 0x55 && local_read_buffer[8] == 0x7c) 173 | { 174 | pmesg("Received WAIT command\n"); 175 | // __midi_wait = true; 176 | receiving_sysex = false; 177 | break; 178 | } 179 | // TODO: check if it's an ack command 180 | // else if (__midi_wait == true && local_read_buffer[7] == 0x55 && local_read_buffer[8] == 0x7f) 181 | // __midi_wait = false; 182 | #ifdef SYNCLOG 183 | if (read_space > (jack_ringbuffer_write_space(read_buffer) - position)) 184 | read_space = jack_ringbuffer_write_space(read_buffer) - position; 185 | if (max_read < position + 3) 186 | max_read = position + 3; 187 | #endif 188 | // write a header with length info 189 | local_read_buffer[0] = MIDI_SYSEX; 190 | local_read_buffer[1] = (position - 3) / 128; 191 | local_read_buffer[2] = (position - 3) % 128; 192 | jack_ringbuffer_write(read_buffer, local_read_buffer, position); 193 | #ifdef __linux 194 | write(p[1], " ", 1); 195 | #endif 196 | receiving_sysex = false; 197 | break; 198 | } 199 | } // (position < SYSEX_MAX_SIZE) 200 | else 201 | { 202 | receiving_sysex = false; 203 | break; 204 | } 205 | } 206 | // voice message 207 | else if (shift == 0 && data > 0x7F && data < 0xF0) 208 | { 209 | event[0] = Pm_MessageStatus(ev.message); 210 | event[1] = Pm_MessageData1(ev.message); 211 | event[2] = Pm_MessageData2(ev.message); 212 | event[3] = 0; 213 | jack_ringbuffer_write(read_buffer, event, 4); 214 | #ifdef __linux 215 | write(p[1], " ", 1); 216 | #endif 217 | break; 218 | } 219 | else 220 | break; 221 | } 222 | } 223 | // check if theres something from the controller 224 | if (thru_active && Pm_Poll(port_thru)) 225 | { 226 | pmerror = (PmError) Pm_Read(port_thru, &ev, 1); 227 | if (!(pmerror < 0)) // no error 228 | { 229 | event[0] = Pm_MessageStatus(ev.message); 230 | // voice messages 231 | if (event[0] >= 0x80 && event[0] <= 0xEF) 232 | { 233 | // automap 234 | if (automap && pxk->midi_mode != OMNI) 235 | event[0] = (event[0] & ~0xf) | (pxk->selected_channel & 0xff); 236 | event[1] = Pm_MessageData1(ev.message); 237 | event[2] = Pm_MessageData2(ev.message); 238 | event[3] = 1; 239 | // write to ringbuffer for internal processing 240 | jack_ringbuffer_write(read_buffer, event, 4); 241 | #ifdef __linux 242 | write(p[1], " ", 1); 243 | #endif 244 | ev.message = Pm_Message(event[0], event[1], event[2]); 245 | } 246 | // forward message 247 | pmerror = (PmError) Pm_Write(port_out, &ev, 1); 248 | if (pmerror < 0) 249 | show_error(); 250 | } 251 | else 252 | // pmerror = (PmError) Pm_Read(port_thru, &ev, 1); 253 | show_error(); 254 | } 255 | 256 | // check if theres some MIDI to write on the bus 257 | result_out = false; 258 | if (jack_ringbuffer_peek(write_buffer, &poll, 1) == 1) 259 | { 260 | result_out = true; 261 | if (poll == MIDI_SYSEX) 262 | { 263 | // TODO: WAIT 264 | // if (!__midi_wait) 265 | // { 266 | jack_ringbuffer_read(write_buffer, local_write_buffer, 3); // read header 267 | unsigned int len = local_write_buffer[1] * 128 + local_write_buffer[2]; 268 | jack_ringbuffer_read(write_buffer, local_write_buffer, len); 269 | pmerror = Pm_WriteSysEx(port_out, 0, local_write_buffer); 270 | if (pmerror < 0) 271 | show_error(); 272 | // } 273 | } 274 | else if (jack_ringbuffer_read(write_buffer, event, 3) == 3) 275 | { 276 | ev.message = Pm_Message(event[0], event[1], event[2]); 277 | pmerror = Pm_Write(port_out, &ev, 1); 278 | if (pmerror < 0) 279 | show_error(); 280 | } 281 | } 282 | } while (result_out); 283 | } 284 | 285 | #ifdef __linux 286 | static void process_midi_in(int fd, void*) 287 | #else 288 | static void process_midi_in(void*) 289 | #endif 290 | { 291 | static unsigned long count_events = 0; 292 | static unsigned char sysex[SYSEX_MAX_SIZE]; 293 | static unsigned int len; 294 | unsigned char poll = 0; 295 | #ifdef __linux 296 | static char buf; 297 | #endif 298 | while (midi_active && jack_ringbuffer_peek(read_buffer, &poll, 1) == 1) 299 | { 300 | #ifdef __linux 301 | read(fd, &buf, 1); 302 | #endif 303 | if (poll == MIDI_SYSEX) 304 | { 305 | jack_ringbuffer_read(read_buffer, sysex, 3); // read header 306 | len = sysex[1] * 128 + sysex[2]; 307 | jack_ringbuffer_read(read_buffer, sysex, len); 308 | if (join_bro) 309 | break; 310 | // e-mu sysex 311 | if (sysex[1] == 0x18) 312 | { 313 | switch (sysex[5]) 314 | { 315 | case 0x0b: // generic name 316 | if (!pxk->Synchronized()) 317 | { 318 | got_answer = true; 319 | pxk->incoming_generic_name(sysex); 320 | } 321 | break; 322 | case 0x10: // preset dumps 323 | if (requested) 324 | switch (sysex[6]) 325 | { 326 | case 0x01: // dump header (closed) 327 | case 0x03: // dump header (open) 328 | pxk->incoming_preset_dump(sysex, len, true); 329 | break; 330 | case 0x02: // dump data (closed) 331 | pxk->incoming_preset_dump(sysex, len); 332 | break; 333 | case 0x04: // dump data (open) 334 | pxk->incoming_preset_dump(sysex, len); 335 | if (len < 253) // last packet 336 | { 337 | got_answer = true; 338 | requested = false; 339 | } 340 | break; 341 | } 342 | break; 343 | 344 | case 0x7f: // ACK 345 | pxk->incoming_ACK(sysex[7] * 128 + sysex[6]); 346 | break; 347 | 348 | case 0x7e: // NAK 349 | pxk->incoming_NAK(sysex[7] * 128 + sysex[6]); 350 | break; 351 | 352 | case 0x7b: // EOF 353 | got_answer = true; 354 | requested = false; 355 | break; 356 | 357 | case 0x1c: // setup dumps 358 | if (requested) 359 | { 360 | got_answer = true; 361 | requested = false; 362 | pxk->incoming_setup_dump(sysex, len); 363 | } 364 | break; 365 | 366 | case 0x18: // arp pattern dump 367 | if (requested) 368 | { 369 | got_answer = true; 370 | requested = false; 371 | pxk->incoming_arp_dump(sysex, len); 372 | } 373 | break; 374 | 375 | case 0x09: // hardware configuration 376 | if (!pxk->Synchronized() && requested) 377 | { 378 | requested = false; 379 | pxk->incoming_hardware_config(sysex); 380 | } 381 | break; 382 | 383 | case 0x7d: // CANCEL 384 | got_answer = true; 385 | requested = false; 386 | pxk->display_status("Device sent CANCEL."); 387 | ui->supergroup->clear_output(); 388 | break; 389 | case 0x70: // ERROR 390 | case 0x40: // remote front panel control command 391 | break; 392 | 393 | default: 394 | #ifdef SYNCLOG 395 | ui->init_log->append("\nprocess_midi_in: Received unrecognized e-mu sysex:\n"); 396 | char* __buffer = (char*) malloc(len * sizeof(char)); 397 | for (unsigned int i = 0; i < len; i++) 398 | sprintf(__buffer + 2 * i, "%02hhX", sysex[i]); 399 | ui->init_log->append(__buffer); 400 | ui->init_log->append("\n"); 401 | free(__buffer); 402 | #endif 403 | break; 404 | } 405 | pxk->log_add(sysex, len, 1); 406 | } 407 | // universal sysex 408 | else if (sysex[1] == 0x7e) 409 | { 410 | //pmesg("process_midi_in: received MIDI standard universal message: "); 411 | // device inquiry 412 | if (sysex[3] == 0x06 && sysex[4] == 0x02 && sysex[5] == 0x18) 413 | { 414 | //pmesg("device inquiry response\n"); 415 | if (!pxk->Synchronized()) 416 | pxk->incoming_inquiry_data(sysex); 417 | } 418 | pxk->log_add(sysex, len, 1); 419 | } 420 | #ifdef SYNCLOG 421 | else 422 | { 423 | ui->init_log->append("\nprocess_midi_in: Received unknown sysex:\n"); 424 | char* __buffer = (char*) malloc(len * sizeof(char)); 425 | for (unsigned int i = 0; i < len; i++) 426 | snprintf(__buffer + i, 1, "%02hhX", *(sysex + i)); 427 | ui->init_log->append(__buffer); 428 | ui->init_log->append("\n"); 429 | free(__buffer); 430 | } 431 | #endif 432 | } // if (poll == MIDI_SYSEX) 433 | 434 | else 435 | { 436 | unsigned char event[4]; 437 | jack_ringbuffer_read(read_buffer, event, 4); 438 | if (event[3] == 0) // device event 439 | { 440 | switch (event[0] >> 4) 441 | { 442 | case 0x8: // note off 443 | ui->piano->activate_key(-1, event[1]); 444 | ui->main->minipiano->activate_key(-1, event[1]); 445 | ui->global_minipiano->activate_key(-1, event[1]); 446 | ui->arp_mp->activate_key(-1, event[1]); 447 | break; 448 | case 0x9: // note-on 449 | if (event[2] == 0) 450 | { 451 | ui->piano->activate_key(-1, event[1]); 452 | ui->main->minipiano->activate_key(-1, event[1]); 453 | ui->global_minipiano->activate_key(-1, event[1]); 454 | ui->arp_mp->activate_key(-1, event[1]); 455 | } 456 | else 457 | { 458 | ui->piano->activate_key(1, event[1]); 459 | ui->main->minipiano->activate_key(1, event[1]); 460 | ui->global_minipiano->activate_key(1, event[1]); 461 | ui->arp_mp->activate_key(1, event[1]); 462 | } 463 | break; 464 | case 0xb: // controller event 465 | { 466 | if (pxk->cc_to_ctrl.find(event[1]) != pxk->cc_to_ctrl.end()) 467 | { 468 | int controller = pxk->cc_to_ctrl[event[1]]; 469 | if (controller <= 12) 470 | // sliders 471 | ((Fl_Slider*) ui->main->ctrl_x[controller])->value((double) event[2]); 472 | else 473 | // footswitches 474 | ((Fl_Button*) ui->main->ctrl_x[controller])->value(event[2] > 63 ? 1 : 0); 475 | } 476 | else if (event[1] == 1) // modwhl 477 | ui->modwheel->value((double) event[2]); 478 | else if (event[1] == 7) // channel volume 479 | pwid[131][0]->set_value(event[2]); 480 | else if (event[1] == 10) // channel pan 481 | pwid[132][0]->set_value(event[2]); 482 | } 483 | break; 484 | case 0xe: // pitchwheel 485 | { 486 | int v = event[2]; 487 | v <<= 7; 488 | v |= event[1]; 489 | ui->pitchwheel->value((double) v); 490 | } 491 | } 492 | } 493 | else // controller event 494 | { 495 | switch (event[0] >> 4) 496 | { 497 | case 0x8: // note off 498 | ui->piano->activate_key(-3, event[1]); 499 | ui->main->minipiano->activate_key(-3, event[1]); 500 | ui->global_minipiano->activate_key(-3, event[1]); 501 | ui->arp_mp->activate_key(-3, event[1]); 502 | break; 503 | case 0x9: // note-on 504 | if (event[2] == 0) 505 | { 506 | ui->piano->activate_key(-2, event[1]); 507 | ui->main->minipiano->activate_key(-2, event[1]); 508 | ui->global_minipiano->activate_key(-2, event[1]); 509 | ui->arp_mp->activate_key(-2, event[1]); 510 | } 511 | else 512 | { 513 | ui->piano->activate_key(2, event[1]); 514 | ui->main->minipiano->activate_key(2, event[1]); 515 | ui->global_minipiano->activate_key(2, event[1]); 516 | ui->arp_mp->activate_key(2, event[1]); 517 | } 518 | break; 519 | case 0xb: // controller event 520 | { 521 | if (pxk->cc_to_ctrl.find(event[1]) != pxk->cc_to_ctrl.end()) 522 | { 523 | int controller = pxk->cc_to_ctrl[event[1]]; 524 | if (controller <= 12) 525 | // sliders 526 | ((Fl_Slider*) ui->main->ctrl_x[controller])->value((double) event[2]); 527 | else 528 | // footswitches 529 | ((Fl_Button*) ui->main->ctrl_x[controller])->value(event[2] > 63 ? 1 : 0); 530 | } 531 | else if (event[1] == 1) // modwhl 532 | ui->modwheel->value((double) event[2]); 533 | else if (event[1] == 7) // channel volume 534 | pwid[131][0]->set_value(event[2]); 535 | else if (event[1] == 10) // channel pan 536 | pwid[132][0]->set_value(event[2]); 537 | } 538 | break; 539 | case 0xe: // pitchwheel 540 | { 541 | int v = event[2]; 542 | v <<= 7; 543 | v |= event[1]; 544 | ui->pitchwheel->value((double) v); 545 | } 546 | } 547 | } 548 | // log midi events 549 | if (cfg->get_cfg_option(CFG_LOG_EVENTS_IN)) 550 | { 551 | char _b[30]; 552 | snprintf(_b, 30, "\nIE.%lu::%02X%02X%02X", ++count_events, event[0], event[1], event[2]); 553 | ui->logbuf->append(_b); 554 | } 555 | } 556 | } 557 | #ifndef __linux 558 | if (timer_running) 559 | Fl::repeat_timeout(.01, process_midi_in); 560 | #endif 561 | } 562 | 563 | // ######################################## 564 | // midi connection class member definitions 565 | // ######################################## 566 | MIDI::MIDI() 567 | { 568 | pmesg("MIDI::MIDI()\n"); 569 | // initialize (global) variables and buffers 570 | selected_port_out = -1; 571 | port_out = 0; 572 | selected_port_in = -1; 573 | port_in = 0; 574 | selected_port_thru = -1; 575 | port_thru = 0; 576 | #ifdef __linux 577 | if (pipe(p) == -1) 578 | fprintf(stderr, "*** Could not open pipe\n%s", strerror(errno)); 579 | #endif 580 | read_buffer = jack_ringbuffer_create(RINGBUFFER_READ); 581 | write_buffer = jack_ringbuffer_create(RINGBUFFER_WRITE); 582 | #ifdef USE_MLOCK 583 | jack_ringbuffer_mlock(write_buffer); 584 | jack_ringbuffer_mlock(read_buffer); 585 | #endif 586 | // populate ports 587 | pxk->display_status("Populating MIDI ports..."); 588 | Fl::flush(); 589 | for (unsigned char i = 0; i < Pm_CountDevices(); i++) 590 | { 591 | const PmDeviceInfo *info = Pm_GetDeviceInfo(i); 592 | if (info->output) 593 | { 594 | ui->midi_outs->add("foo"); 595 | ui->midi_outs->replace(ui->midi_outs->size() - 2, info->name); 596 | ports_out.push_back(i); 597 | } 598 | else 599 | { 600 | ui->midi_ins->add("foo"); 601 | ui->midi_ins->replace(ui->midi_ins->size() - 2, info->name); 602 | ui->midi_ctrl->add("foo"); 603 | ui->midi_ctrl->replace(ui->midi_ctrl->size() - 2, info->name); 604 | ports_in.push_back(i); 605 | } 606 | } 607 | pxk->display_status(0); 608 | Fl::flush(); 609 | } 610 | 611 | MIDI::~MIDI() 612 | { 613 | pmesg("MIDI::~MIDI()\n"); 614 | stop_timer(); 615 | jack_ringbuffer_free(read_buffer); 616 | jack_ringbuffer_free(write_buffer); 617 | } 618 | 619 | bool MIDI::in() 620 | { 621 | if (selected_port_in != -1) 622 | return true; 623 | return false; 624 | } 625 | 626 | bool MIDI::out() 627 | { 628 | if (selected_port_out != -1) 629 | return true; 630 | return false; 631 | } 632 | 633 | // TODO: WAIT 634 | //bool MIDI::Wait() 635 | //{ 636 | // return __midi_wait; 637 | //} 638 | // 639 | //void MIDI::Wait(bool wait) 640 | //{ 641 | // __midi_wait = wait; 642 | //} 643 | 644 | void MIDI::set_device_id(unsigned char id) 645 | { 646 | pmesg("MIDI::set_device_id(%d)\n", id); 647 | midi_device_id = id; 648 | // sysex packet delay 649 | edit_parameter_value(405, cfg->get_cfg_option(CFG_SPEED)); 650 | } 651 | 652 | // start realtime receiver 653 | int MIDI::start_timer() 654 | { 655 | if (timer_running) 656 | return 1; 657 | pmesg("MIDI::start_timer()\n"); 658 | // initialize timout or filedescriptors for IPC 659 | #ifdef __linux 660 | Fl::add_fd(p[0], process_midi_in); 661 | #else 662 | Fl::add_timeout(0, process_midi_in); 663 | #endif 664 | // start timer, clean up if we couldnt 665 | if (Pt_Start(1, &process_midi, 0) < 0) 666 | { 667 | #ifdef __linux 668 | Fl::remove_fd(p[0]); 669 | close(p[0]); 670 | close(p[1]); 671 | #else 672 | Fl::remove_timeout(process_midi_in); 673 | #endif 674 | pxk->display_status("*** Could not start MIDI timer."); 675 | fprintf(stderr, "*** Could not start MIDI timer.\n"); 676 | #ifdef WIN32 677 | fflush(stderr); 678 | #endif 679 | Pt_Stop(); 680 | return 0; 681 | } 682 | timer_running = true; 683 | Pm_Initialize(); // start portmidi 684 | return 1; 685 | } 686 | 687 | // stop realtime receiver 688 | void MIDI::stop_timer() 689 | { 690 | if (!timer_running) 691 | return; 692 | pmesg("MIDI::stop_timer()\n"); 693 | process_midi_exit_flag = false; 694 | thru_active = false; 695 | timer_running = false; 696 | midi_active = false; 697 | while (!process_midi_exit_flag) 698 | mysleep(10); 699 | #ifdef __linux 700 | Fl::remove_fd(p[0]); 701 | close(p[0]); 702 | close(p[1]); 703 | #else 704 | Fl::remove_timeout(process_midi_in); 705 | #endif 706 | Pm_Terminate(); 707 | Pt_Stop(); 708 | if (selected_port_in != -1) 709 | Pm_Close(port_in); 710 | if (selected_port_out != -1) 711 | Pm_Close(port_out); 712 | if (selected_port_thru != -1) 713 | Pm_Close(port_thru); 714 | } 715 | 716 | int MIDI::connect_out(int port) 717 | { 718 | pmesg("MIDI::connect_out(port: %d)\n", port); 719 | if (port < 0 || port >= (int) ports_out.size()) 720 | return 0; 721 | if (selected_port_out == port) 722 | return 1; 723 | if (midi_active) 724 | { 725 | process_midi_exit_flag = false; 726 | thru_active = false; 727 | midi_active = false; 728 | while (!process_midi_exit_flag) 729 | mysleep(10); 730 | } 731 | if (!start_timer()) 732 | return 0; 733 | if (selected_port_out != -1) 734 | { 735 | pmerror = Pm_Close(port_out); 736 | if (pmerror < 0) 737 | { 738 | show_error(); 739 | return 0; 740 | } 741 | } 742 | // device ID validation 743 | try 744 | { 745 | ports_out.at(port); 746 | } catch (...) 747 | { 748 | fprintf(stderr, "*** Selected MIDI port (%d, out) does not exist.\n", port); 749 | #ifdef WIN32 750 | fflush(stderr); 751 | #endif 752 | return 0; 753 | } 754 | pmerror = Pm_OpenOutput(&port_out, ports_out.at(port), NULL, 0, NULL, NULL, 0); // open the port 755 | if (pmerror < 0) 756 | { 757 | show_error(); 758 | return 0; 759 | } 760 | selected_port_out = port; 761 | if (selected_port_in != -1) 762 | midi_active = true; 763 | if (selected_port_thru != -1) 764 | thru_active = true; 765 | return 1; 766 | } 767 | 768 | int MIDI::connect_in(int port) 769 | { 770 | pmesg("MIDI::connect_in(port: %d)\n", port); 771 | if (port < 0 || port >= (int) ports_in.size()) 772 | return 0; 773 | if (selected_port_in == port) 774 | return 1; 775 | if (port == selected_port_thru) 776 | { 777 | fl_message("In-port must be different from Ctrl-port."); 778 | return 0; 779 | } 780 | if (midi_active) 781 | { 782 | process_midi_exit_flag = false; 783 | midi_active = false; 784 | while (!process_midi_exit_flag) 785 | mysleep(10); 786 | } 787 | if (!start_timer()) 788 | return 0; 789 | if (selected_port_in != -1) 790 | { 791 | pmerror = Pm_Close(port_in); 792 | if (pmerror < 0) 793 | { 794 | show_error(); 795 | return 0; 796 | } 797 | } 798 | // device ID validation 799 | try 800 | { 801 | ports_in.at(port); 802 | } catch (...) 803 | { 804 | fprintf(stderr, "*** Selected MIDI port (%d, in) does not exist.\n", port); 805 | #ifdef WIN32 806 | fflush(stderr); 807 | #endif 808 | return 0; 809 | } 810 | pmerror = Pm_OpenInput(&port_in, ports_in.at(port), NULL, 512, NULL, NULL); 811 | if (pmerror < 0) 812 | { 813 | show_error(); 814 | return 0; 815 | } 816 | // only allow sysex for now 817 | pmerror = Pm_SetFilter(port_in, ~1); 818 | if (pmerror < 0) 819 | { 820 | show_error(); 821 | return 0; 822 | } 823 | selected_port_in = port; 824 | if (selected_port_out != -1) 825 | midi_active = true; 826 | return 1; 827 | } 828 | 829 | // connect midi thru 830 | int MIDI::connect_thru(int port) 831 | { 832 | pmesg("MIDI::connect_thru(port: %d)\n", port); 833 | if (port < 0 || port >= (int) ports_in.size()) 834 | return 0; 835 | if (port == selected_port_in) 836 | { 837 | fl_message("Ctrl-port must be different from In-port"); 838 | return 0; 839 | } 840 | if (thru_active) 841 | thru_active = false; 842 | if (!start_timer()) 843 | return 0; 844 | if (selected_port_thru != -1) 845 | { 846 | pmerror = Pm_Close(port_thru); 847 | if (pmerror < 0) 848 | { 849 | show_error(); 850 | return 0; 851 | } 852 | } 853 | if (selected_port_thru == port) 854 | { 855 | selected_port_thru = -1; 856 | return 0; 857 | } 858 | // device ID validation 859 | try 860 | { 861 | ports_in.at(port); 862 | } catch (...) 863 | { 864 | fprintf(stderr, "*** Selected MIDI port (%d, thru) does not exist.\n", port); 865 | #ifdef WIN32 866 | fflush(stderr); 867 | #endif 868 | return 0; 869 | } 870 | pmerror = Pm_OpenInput(&port_thru, ports_in.at(port), NULL, 512, NULL, NULL); 871 | if (pmerror < 0) 872 | { 873 | show_error(); 874 | return 0; 875 | } 876 | // filter messages we dont process 877 | pmerror = Pm_SetFilter(port_thru, PM_FILT_REALTIME | PM_FILT_SYSTEMCOMMON); 878 | if (pmerror < 0) 879 | { 880 | show_error(); 881 | return 0; 882 | } 883 | selected_port_thru = port; 884 | if (selected_port_out != -1) 885 | { 886 | cfg->get_cfg_option(CFG_AUTOMAP) ? automap = true : automap = false; 887 | thru_active = true; 888 | } 889 | set_control_channel_filter(cfg->get_cfg_option(CFG_CONTROL_CHANNEL)); 890 | return 1; 891 | } 892 | 893 | void MIDI::set_control_channel_filter(int channel) const 894 | { 895 | pmesg("MIDI::set_control_channel_filter(%d)\n", channel); 896 | cfg->set_cfg_option(CFG_CONTROL_CHANNEL, channel); 897 | if (channel == 16) // 0-15, single channels 898 | pmerror = Pm_SetChannelMask(port_thru, ~0); // all channels 899 | else 900 | pmerror = Pm_SetChannelMask(port_thru, Pm_Channel(channel)); 901 | if (pmerror < 0) 902 | show_error(); 903 | } 904 | 905 | void MIDI::set_channel_filter(int channel) const 906 | { 907 | pmesg("MIDI::set_channel_filter(%d)\n", channel); 908 | if (port_in) 909 | { 910 | pmerror = Pm_SetChannelMask(port_in, Pm_Channel(channel)); 911 | if (pmerror < 0) 912 | show_error(); 913 | } 914 | } 915 | 916 | void MIDI::filter_loose() const 917 | { 918 | pmesg("MIDI::filter_loose()\n"); 919 | int filter = ~0; // filter everything 920 | filter ^= (PM_FILT_SYSEX + PM_FILT_NOTE + PM_FILT_CONTROL + PM_FILT_PITCHBEND); 921 | if (port_in) 922 | { 923 | pmerror = Pm_SetFilter(port_in, filter); 924 | if (pmerror < 0) 925 | show_error(); 926 | } 927 | if (port_thru) 928 | { 929 | pmerror = Pm_SetFilter(port_thru, PM_FILT_REALTIME | PM_FILT_SYSTEMCOMMON); 930 | if (pmerror < 0) 931 | show_error(); 932 | } 933 | } 934 | 935 | void MIDI::filter_strict() const 936 | { 937 | pmesg("MIDI::filter_strict()\n"); 938 | if (port_in) 939 | { 940 | pmerror = Pm_SetFilter(port_in, ~1); // only sysex on input 941 | if (pmerror < 0) 942 | show_error(); 943 | } 944 | if (port_thru) 945 | { 946 | pmerror = Pm_SetFilter(port_thru, ~0); // everything on thru 947 | if (pmerror < 0) 948 | show_error(); 949 | } 950 | } 951 | 952 | void MIDI::write_sysex(const unsigned char* sysex, unsigned int len) const 953 | { 954 | //pmesg("MIDI::write_sysex(data, len: %d)\n", len); 955 | static unsigned char data[SYSEX_MAX_SIZE]; 956 | if (!midi_active || len > SYSEX_MAX_SIZE) 957 | return; 958 | data[0] = MIDI_SYSEX; 959 | data[1] = len / 128; 960 | data[2] = len % 128; 961 | memcpy(data + 3, sysex, len); 962 | #ifdef SYNCLOG 963 | if (write_space > jack_ringbuffer_write_space(write_buffer) - len - 3) 964 | write_space = jack_ringbuffer_write_space(write_buffer) - len - 3; 965 | if (max_write < len + 3) 966 | max_write = len + 3; 967 | #endif 968 | // while (jack_ringbuffer_write_space(write_buffer) < len + 3) 969 | // Fl::wait(.1); 970 | jack_ringbuffer_write(write_buffer, data, len + 3); 971 | pxk->log_add(sysex, len, 0); 972 | } 973 | 974 | void MIDI::write_event(int status, int value1, int value2, int channel) const 975 | { 976 | //pmesg("MIDI::write_event(%X, %X, %X, %d)\n", status, value1, value2, channel); 977 | if (!midi_active) 978 | return; 979 | if (channel == -1) 980 | channel = pxk->selected_channel; 981 | unsigned char stat = ((status & ~0xf) | channel) & 0xff; 982 | unsigned char v1 = value1 & 0xff; 983 | unsigned char v2 = value2 & 0xff; 984 | const unsigned char msg[] = 985 | { stat, v1, v2 }; 986 | // while (jack_ringbuffer_write_space(write_buffer) < 3) 987 | // Fl::wait(.1); 988 | jack_ringbuffer_write(write_buffer, msg, 3); 989 | // log midi events 990 | if (cfg->get_cfg_option(CFG_LOG_EVENTS_OUT)) 991 | { 992 | static unsigned long count = 0; 993 | char buf[30]; 994 | snprintf(buf, 30, "\nOE.%lu::%02x%02x%02x", ++count, stat, v1, v2); 995 | ui->logbuf->append(buf); 996 | } 997 | } 998 | 999 | void MIDI::ack(int packet) const 1000 | { 1001 | pmesg("MIDI::ack(packet: %d) \n", packet); 1002 | unsigned char l = packet % 128; 1003 | unsigned char m = packet / 128; 1004 | unsigned char a[] = 1005 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x7f, l, m, 0xf7 }; 1006 | write_sysex(a, 9); 1007 | } 1008 | 1009 | void MIDI::nak(int packet) const 1010 | { 1011 | pmesg("MIDI::nak(packet: %d) \n", packet); 1012 | unsigned char l = packet % 128; 1013 | unsigned char m = packet / 128; 1014 | unsigned char n[] = 1015 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x7e, l, m, 0xf7 }; 1016 | write_sysex(n, 9); 1017 | } 1018 | 1019 | void MIDI::eof() const 1020 | { 1021 | pmesg("MIDI::eof() \n"); 1022 | got_answer = true; 1023 | unsigned char endof[] = 1024 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x7b, 0xf7 }; 1025 | write_sysex(endof, 7); 1026 | } 1027 | 1028 | void MIDI::request_hardware_config() const 1029 | { 1030 | pmesg("MIDI::request_hardware_config() \n"); 1031 | if (requested) 1032 | return; 1033 | unsigned char request[] = 1034 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x0a, 0xf7 }; 1035 | write_sysex(request, 7); 1036 | requested = true; 1037 | } 1038 | 1039 | void request_preset_dump_timeout(void* m) 1040 | { 1041 | ((MIDI*) m)->request_preset_dump(); 1042 | } 1043 | 1044 | void MIDI::request_preset_dump(int timeout) const 1045 | { 1046 | if (timeout > 0) 1047 | { 1048 | Fl::add_timeout((double) timeout / 1000., request_preset_dump_timeout, (void*) this); 1049 | return; 1050 | } 1051 | if (requested) 1052 | return; 1053 | 1054 | 1055 | unsigned char loop = cfg->get_cfg_option(CFG_CLOSED_LOOP_DOWNLOAD) ? 0x02 : 0x04; 1056 | 1057 | unsigned char nl = pxk->selected_preset % 128; 1058 | unsigned char nm = pxk->selected_preset / 128; 1059 | unsigned char rl = pxk->selected_preset_rom % 128; 1060 | unsigned char rm = pxk->selected_preset_rom / 128; 1061 | 1062 | if (pxk->selected_preset == -1 || pxk->selected_preset >= 512) 1063 | { 1064 | nl = nm = 0x7F; 1065 | rl = rm = 0; 1066 | } 1067 | 1068 | unsigned char request[] = 1069 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x11, loop, nl, nm, rl, rm, 0xf7 }; 1070 | 1071 | write_sysex(request, 12); 1072 | pxk->Loading(); 1073 | requested = true; 1074 | } 1075 | 1076 | void MIDI::request_setup_dump() const 1077 | { 1078 | pmesg("MIDI::request_setup_dump() \n"); 1079 | pxk->display_status("Loading multisetup..."); 1080 | if (requested) 1081 | return; 1082 | unsigned char request[] = 1083 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x1d, 0xf7 }; 1084 | write_sysex(request, 7); 1085 | requested = true; 1086 | } 1087 | 1088 | void MIDI::request_arp_dump(int number, int rom_id) const 1089 | { 1090 | //pmesg("MIDI::request_arp_dump(#: %d, rom: %d)\n", number, rom_id); 1091 | if (join_bro) 1092 | return; 1093 | if (number < 0) 1094 | number += 16384; 1095 | unsigned char nl = number % 128; 1096 | unsigned char nm = number / 128; 1097 | unsigned char rl = rom_id % 128; 1098 | unsigned char rm = rom_id / 128; 1099 | unsigned char request[] = 1100 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x19, nl, nm, rl, rm, 0xf7 }; 1101 | write_sysex(request, 11); 1102 | requested = true; 1103 | } 1104 | 1105 | void MIDI::request_name(int type, int number, int rom_id) const 1106 | { 1107 | //pmesg("MIDI::request_name(type: %d, #: %d, rom_id: %d)\n", type, number, rom_id); 1108 | if (join_bro) 1109 | return; 1110 | unsigned char nl = number % 128; 1111 | unsigned char nm = number / 128; 1112 | unsigned char rl = rom_id % 128; 1113 | unsigned char rm = rom_id / 128; 1114 | unsigned char t = type & 0xff; 1115 | unsigned char request[] = 1116 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x0c, t, nl, nm, rl, rm, 0xf7 }; 1117 | write_sysex(request, 12); 1118 | } 1119 | 1120 | void MIDI::edit_parameter_value(int id, int value) const 1121 | { 1122 | //pmesg("MIDI::edit_parameter_value(id: %d, value: %d) \n", id, value); 1123 | if (id < 0) 1124 | id += 16384; 1125 | if (value < 0) 1126 | value += 16384; 1127 | unsigned char il = id % 128; 1128 | unsigned char im = id / 128; 1129 | unsigned char vl = value % 128; 1130 | unsigned char vm = value / 128; 1131 | unsigned char request[] = 1132 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x01, 0x02, il, im, vl, vm, 0xf7 }; 1133 | write_sysex(request, 12); 1134 | } 1135 | 1136 | void MIDI::master_volume(int volume) const 1137 | { 1138 | //pmesg("MIDI::master_volume(%d) \n", volume); 1139 | unsigned char vl = volume % 128; 1140 | unsigned char vm = volume / 128; 1141 | unsigned char master_vol[] = 1142 | { 0xf0, 0x7f, midi_device_id, 0x04, 0x01, vl, vm, 0xf7 }; 1143 | write_sysex(master_vol, 8); 1144 | } 1145 | 1146 | void MIDI::copy(int cmd, int src, int dst, int src_l, int dst_l, int rom_id) const 1147 | { 1148 | //pmesg("MIDI::copy(cmd: %X, src: %d, src_l: %d, dst: %d, dst_l: %d, rom: %d)\n", cmd, src, src_l, dst, dst_l, rom_id); 1149 | if (dst < 0) 1150 | dst += 16384; 1151 | if (src < 0) 1152 | src += 16384; 1153 | unsigned char c = cmd & 0xff; 1154 | unsigned char rl = rom_id % 128; 1155 | unsigned char rm = rom_id / 128; 1156 | unsigned char srcl = src % 128; 1157 | unsigned char srcm = src / 128; 1158 | unsigned char dstl = dst % 128; 1159 | unsigned char dstm = dst / 128; 1160 | // layer dependent 1161 | if (cmd > 0x24 && cmd < 0x2b) 1162 | { 1163 | unsigned char s_l = src_l & 0xff; 1164 | unsigned char d_l = dst_l & 0xff; 1165 | unsigned char cm[] = 1166 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, c, srcl, srcm, s_l, 0, dstl, dstm, d_l, 0, rl, rm, 0xf7 }; 1167 | write_sysex(cm, 17); 1168 | } 1169 | // layer independent 1170 | else 1171 | { 1172 | if (cmd == 0x2c) // copy setup 1173 | { 1174 | unsigned char cm[] = 1175 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, c, srcl, srcm, dstl, dstm, 0xf7 }; 1176 | write_sysex(cm, 11); 1177 | // set device id to our chosen device id 1178 | // so it will respond to our requests 1179 | edit_parameter_value(388, midi_device_id); 1180 | // set sysex delay to our chosen setting 1181 | edit_parameter_value(405, cfg->get_cfg_option(CFG_SPEED)); 1182 | } 1183 | else 1184 | { 1185 | unsigned char cm[] = 1186 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, c, srcl, srcm, dstl, dstm, rl, rm, 0xf7 }; 1187 | write_sysex(cm, 13); 1188 | } 1189 | } 1190 | } 1191 | 1192 | void MIDI::audit() const 1193 | { 1194 | pmesg("MIDI::audit()\n"); 1195 | // open session 1196 | unsigned char os[] = 1197 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x40, 0x10, 0xf7 }; 1198 | write_sysex(os, 8); 1199 | unsigned char press[] = 1200 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x40, 0x20, 0x04, 0x0, 0x01, 0xf7 }; 1201 | write_sysex(press, 11); 1202 | unsigned char cs[] = 1203 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x40, 0x11, 0xf7 }; 1204 | write_sysex(cs, 8); 1205 | } 1206 | 1207 | void MIDI::randomize() const 1208 | { 1209 | pmesg("MIDI::randomize()\n"); 1210 | static bool seed = true; 1211 | if (seed) 1212 | { 1213 | seed = false; 1214 | #ifdef WIN32 1215 | srand(time(0)); 1216 | int byte1 = rand() % 16384; 1217 | int byte2 = rand() % 16384; 1218 | #else 1219 | srandom(time(0)); 1220 | int byte1 = random() % 16384; 1221 | int byte2 = random() % 16384; 1222 | #endif 1223 | unsigned char b1l = byte1 % 128; 1224 | unsigned char b1m = byte1 / 128; 1225 | unsigned char b2l = byte2 % 128; 1226 | unsigned char b2m = byte2 / 128; 1227 | unsigned char seedr[] = 1228 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x72, 0x7f, 0x7f, 0, 0, b1l, b1m, b2l, b2m, 0xf7 }; 1229 | write_sysex(seedr, 15); 1230 | } 1231 | unsigned char r[] = 1232 | { 0xf0, 0x18, 0x0f, midi_device_id, 0x55, 0x71, 0x7f, 0x7f, 0, 0, 0xf7 }; 1233 | write_sysex(r, 11); 1234 | request_preset_dump(300); 1235 | } 1236 | 1237 | void MIDI::reset_handler() const 1238 | { 1239 | requested = false; 1240 | } 1241 | -------------------------------------------------------------------------------- /src/ringbuffer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2000 Paul Davis 3 | Copyright (C) 2003 Rohan Drape 4 | 5 | This program is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU Lesser General Public License as published by 7 | the Free Software Foundation; either version 2.1 of the License, or 8 | (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU Lesser General Public License for more details. 14 | 15 | You should have received a copy of the GNU Lesser General Public License 16 | along with this program; if not, write to the Free Software 17 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | ISO/POSIX C version of Paul Davis's lock free ringbuffer C++ code. 20 | This is safe for the case of one read thread and one write thread. 21 | */ 22 | 23 | // slightly modified for prodatum to use unsigned char 24 | 25 | #include 26 | #include 27 | #ifdef USE_MLOCK 28 | #include 29 | #endif /* USE_MLOCK */ 30 | 31 | #include "ringbuffer.h" 32 | 33 | /* Create a new ringbuffer to hold at least `sz' bytes of data. The 34 | actual buffer size is rounded up to the next power of two. */ 35 | 36 | jack_ringbuffer_t * 37 | jack_ringbuffer_create (size_t sz) 38 | { 39 | int power_of_two; 40 | jack_ringbuffer_t *rb; 41 | 42 | rb = (jack_ringbuffer_t*) malloc (sizeof (jack_ringbuffer_t)); 43 | 44 | for (power_of_two = 1; 1 << power_of_two < (int) sz; power_of_two++); 45 | 46 | rb->size = 1 << power_of_two; 47 | rb->size_mask = rb->size; 48 | rb->size_mask -= 1; 49 | rb->write_ptr = 0; 50 | rb->read_ptr = 0; 51 | rb->buf = (unsigned char*) malloc (rb->size); 52 | rb->mlocked = 0; 53 | 54 | return rb; 55 | } 56 | 57 | /* Free all data associated with the ringbuffer `rb'. */ 58 | 59 | void 60 | jack_ringbuffer_free (jack_ringbuffer_t * rb) 61 | { 62 | #ifdef USE_MLOCK 63 | if (rb->mlocked) { 64 | munlock (rb->buf, rb->size); 65 | } 66 | #endif /* USE_MLOCK */ 67 | free (rb->buf); 68 | free (rb); 69 | } 70 | 71 | /* Lock the data block of `rb' using the system call 'mlock'. */ 72 | 73 | int 74 | jack_ringbuffer_mlock (jack_ringbuffer_t * rb) 75 | { 76 | #ifdef USE_MLOCK 77 | if (mlock (rb->buf, rb->size)) { 78 | return -1; 79 | } 80 | #endif /* USE_MLOCK */ 81 | rb->mlocked = 1; 82 | return 0; 83 | } 84 | 85 | /* Reset the read and write pointers to zero. This is not thread 86 | safe. */ 87 | 88 | void 89 | jack_ringbuffer_reset (jack_ringbuffer_t * rb) 90 | { 91 | rb->read_ptr = 0; 92 | rb->write_ptr = 0; 93 | } 94 | 95 | /* Return the number of bytes available for reading. This is the 96 | number of bytes in front of the read pointer and behind the write 97 | pointer. */ 98 | 99 | size_t 100 | jack_ringbuffer_read_space (const jack_ringbuffer_t * rb) 101 | { 102 | size_t w, r; 103 | 104 | w = rb->write_ptr; 105 | r = rb->read_ptr; 106 | 107 | if (w > r) { 108 | return w - r; 109 | } else { 110 | return (w - r + rb->size) & rb->size_mask; 111 | } 112 | } 113 | 114 | /* Return the number of bytes available for writing. This is the 115 | number of bytes in front of the write pointer and behind the read 116 | pointer. */ 117 | 118 | size_t 119 | jack_ringbuffer_write_space (const jack_ringbuffer_t * rb) 120 | { 121 | size_t w, r; 122 | 123 | w = rb->write_ptr; 124 | r = rb->read_ptr; 125 | 126 | if (w > r) { 127 | return ((r - w + rb->size) & rb->size_mask) - 1; 128 | } else if (w < r) { 129 | return (r - w) - 1; 130 | } else { 131 | return rb->size - 1; 132 | } 133 | } 134 | 135 | /* The copying data reader. Copy at most `cnt' bytes from `rb' to 136 | `dest'. Returns the actual number of bytes copied. */ 137 | 138 | size_t 139 | jack_ringbuffer_read (jack_ringbuffer_t * rb, unsigned char *dest, size_t cnt) 140 | { 141 | size_t free_cnt; 142 | size_t cnt2; 143 | size_t to_read; 144 | size_t n1, n2; 145 | 146 | if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { 147 | return 0; 148 | } 149 | 150 | to_read = cnt > free_cnt ? free_cnt : cnt; 151 | 152 | cnt2 = rb->read_ptr + to_read; 153 | 154 | if (cnt2 > rb->size) { 155 | n1 = rb->size - rb->read_ptr; 156 | n2 = cnt2 & rb->size_mask; 157 | } else { 158 | n1 = to_read; 159 | n2 = 0; 160 | } 161 | 162 | memcpy (dest, &(rb->buf[rb->read_ptr]), n1); 163 | rb->read_ptr = (rb->read_ptr + n1) & rb->size_mask; 164 | 165 | if (n2) { 166 | memcpy (dest + n1, &(rb->buf[rb->read_ptr]), n2); 167 | rb->read_ptr = (rb->read_ptr + n2) & rb->size_mask; 168 | } 169 | 170 | return to_read; 171 | } 172 | 173 | /* The copying data reader w/o read pointer advance. Copy at most 174 | `cnt' bytes from `rb' to `dest'. Returns the actual number of bytes 175 | copied. */ 176 | 177 | size_t 178 | jack_ringbuffer_peek (jack_ringbuffer_t * rb, unsigned char *dest, size_t cnt) 179 | { 180 | size_t free_cnt; 181 | size_t cnt2; 182 | size_t to_read; 183 | size_t n1, n2; 184 | size_t tmp_read_ptr; 185 | 186 | tmp_read_ptr = rb->read_ptr; 187 | 188 | if ((free_cnt = jack_ringbuffer_read_space (rb)) == 0) { 189 | return 0; 190 | } 191 | 192 | to_read = cnt > free_cnt ? free_cnt : cnt; 193 | 194 | cnt2 = tmp_read_ptr + to_read; 195 | 196 | if (cnt2 > rb->size) { 197 | n1 = rb->size - tmp_read_ptr; 198 | n2 = cnt2 & rb->size_mask; 199 | } else { 200 | n1 = to_read; 201 | n2 = 0; 202 | } 203 | 204 | memcpy (dest, &(rb->buf[tmp_read_ptr]), n1); 205 | tmp_read_ptr = (tmp_read_ptr + n1) & rb->size_mask; 206 | 207 | if (n2) { 208 | memcpy (dest + n1, &(rb->buf[tmp_read_ptr]), n2); 209 | } 210 | 211 | return to_read; 212 | } 213 | 214 | 215 | /* The copying data writer. Copy at most `cnt' bytes to `rb' from 216 | `src'. Returns the actual number of bytes copied. */ 217 | 218 | size_t 219 | jack_ringbuffer_write (jack_ringbuffer_t * rb, const unsigned char *src, size_t cnt) 220 | { 221 | size_t free_cnt; 222 | size_t cnt2; 223 | size_t to_write; 224 | size_t n1, n2; 225 | 226 | if ((free_cnt = jack_ringbuffer_write_space (rb)) == 0) { 227 | return 0; 228 | } 229 | 230 | to_write = cnt > free_cnt ? free_cnt : cnt; 231 | 232 | cnt2 = rb->write_ptr + to_write; 233 | 234 | if (cnt2 > rb->size) { 235 | n1 = rb->size - rb->write_ptr; 236 | n2 = cnt2 & rb->size_mask; 237 | } else { 238 | n1 = to_write; 239 | n2 = 0; 240 | } 241 | 242 | memcpy (&(rb->buf[rb->write_ptr]), src, n1); 243 | rb->write_ptr = (rb->write_ptr + n1) & rb->size_mask; 244 | 245 | if (n2) { 246 | memcpy (&(rb->buf[rb->write_ptr]), src + n1, n2); 247 | rb->write_ptr = (rb->write_ptr + n2) & rb->size_mask; 248 | } 249 | 250 | return to_write; 251 | } 252 | 253 | /* Advance the read pointer `cnt' places. */ 254 | 255 | void 256 | jack_ringbuffer_read_advance (jack_ringbuffer_t * rb, size_t cnt) 257 | { 258 | size_t tmp = (rb->read_ptr + cnt) & rb->size_mask; 259 | rb->read_ptr = tmp; 260 | } 261 | 262 | /* Advance the write pointer `cnt' places. */ 263 | 264 | void 265 | jack_ringbuffer_write_advance (jack_ringbuffer_t * rb, size_t cnt) 266 | { 267 | size_t tmp = (rb->write_ptr + cnt) & rb->size_mask; 268 | rb->write_ptr = tmp; 269 | } 270 | 271 | /* The non-copying data reader. `vec' is an array of two places. Set 272 | the values at `vec' to hold the current readable data at `rb'. If 273 | the readable data is in one segment the second segment has zero 274 | length. */ 275 | 276 | void 277 | jack_ringbuffer_get_read_vector (const jack_ringbuffer_t * rb, 278 | jack_ringbuffer_data_t * vec) 279 | { 280 | size_t free_cnt; 281 | size_t cnt2; 282 | size_t w, r; 283 | 284 | w = rb->write_ptr; 285 | r = rb->read_ptr; 286 | 287 | if (w > r) { 288 | free_cnt = w - r; 289 | } else { 290 | free_cnt = (w - r + rb->size) & rb->size_mask; 291 | } 292 | 293 | cnt2 = r + free_cnt; 294 | 295 | if (cnt2 > rb->size) { 296 | 297 | /* Two part vector: the rest of the buffer after the current write 298 | ptr, plus some from the start of the buffer. */ 299 | 300 | vec[0].buf = &(rb->buf[r]); 301 | vec[0].len = rb->size - r; 302 | vec[1].buf = rb->buf; 303 | vec[1].len = cnt2 & rb->size_mask; 304 | 305 | } else { 306 | 307 | /* Single part vector: just the rest of the buffer */ 308 | 309 | vec[0].buf = &(rb->buf[r]); 310 | vec[0].len = free_cnt; 311 | vec[1].len = 0; 312 | } 313 | } 314 | 315 | /* The non-copying data writer. `vec' is an array of two places. Set 316 | the values at `vec' to hold the current writeable data at `rb'. If 317 | the writeable data is in one segment the second segment has zero 318 | length. */ 319 | 320 | void 321 | jack_ringbuffer_get_write_vector (const jack_ringbuffer_t * rb, 322 | jack_ringbuffer_data_t * vec) 323 | { 324 | size_t free_cnt; 325 | size_t cnt2; 326 | size_t w, r; 327 | 328 | w = rb->write_ptr; 329 | r = rb->read_ptr; 330 | 331 | if (w > r) { 332 | free_cnt = ((r - w + rb->size) & rb->size_mask) - 1; 333 | } else if (w < r) { 334 | free_cnt = (r - w) - 1; 335 | } else { 336 | free_cnt = rb->size - 1; 337 | } 338 | 339 | cnt2 = w + free_cnt; 340 | 341 | if (cnt2 > rb->size) { 342 | 343 | /* Two part vector: the rest of the buffer after the current write 344 | ptr, plus some from the start of the buffer. */ 345 | 346 | vec[0].buf = &(rb->buf[w]); 347 | vec[0].len = rb->size - w; 348 | vec[1].buf = rb->buf; 349 | vec[1].len = cnt2 & rb->size_mask; 350 | } else { 351 | vec[0].buf = &(rb->buf[w]); 352 | vec[0].len = free_cnt; 353 | vec[1].len = 0; 354 | } 355 | } 356 | --------------------------------------------------------------------------------