├── .gitignore ├── .gitmodules ├── .travis.yml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── Vagrantfile ├── bootstrap.sh ├── include ├── app.h └── app_defs.h ├── lib └── launchpad_pro.a ├── resources └── Launchpad Pro-1.0.154.syx ├── src └── app.c ├── stm32_flash.ld └── tools ├── hextosyx.cpp ├── osx ├── simulator-osx.c └── simulator.xcodeproj │ └── project.pbxproj └── simulator.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.la 14 | *.lo 15 | 16 | # Shared objects (inc. Windows DLLs) 17 | *.dll 18 | *.so 19 | *.so.* 20 | *.dylib 21 | 22 | # Executables 23 | *.exe 24 | *.out 25 | *.app 26 | *.i*86 27 | *.x86_64 28 | *.hex 29 | 30 | # Debug files 31 | *.dSYM/ 32 | 33 | # Environment 34 | .vagrant 35 | build 36 | .project 37 | .cproject 38 | 39 | *.xcworkspacedata 40 | 41 | *.xccheckout 42 | 43 | *.xcuserstate 44 | 45 | *.xcscheme 46 | 47 | tools/osx/simulator.xcodeproj/xcuserdata/davehodder.xcuserdatad/xcschemes/xcschememanagement.plist 48 | 49 | *.xcbkptlist 50 | tools/osx/simulator.xcodeproj/xcuserdata 51 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tools/libintelhex"] 2 | path = tools/libintelhex 3 | url = https://github.com/dvhdr/libintelhex.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: C 7 | 8 | before_install: 9 | - docker build -t novation-launchpad-pro-dev . 10 | - docker run -v $(pwd):/launchpad-pro novation-launchpad-pro-dev "make" 11 | 12 | script: 13 | - test -f build/launchpad_pro.syx 14 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # phusion 2 | FROM phusion/baseimage:0.11 3 | 4 | # Use baseimage-docker's init system. 5 | CMD ["/sbin/my_init"] 6 | 7 | # Set Env 8 | ENV DEBIAN_FRONTEND noninteractive 9 | ENV path /launchpad-pro 10 | 11 | # Set the working directory 12 | WORKDIR ${path} 13 | 14 | # Copy the current directory contents into the container at ${path} 15 | #ADD . ${path} 16 | 17 | # Distro management 18 | RUN apt-get update && apt-get install -y build-essential gcc-arm-none-eabi && \ 19 | apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* 20 | 21 | # Run shell 22 | CMD ["/bin/bash"] 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | BUILDDIR = build 2 | 3 | TOOLS = tools 4 | 5 | SOURCES += src/app.c 6 | 7 | INCLUDES += -Iinclude -I 8 | 9 | LIB = lib/launchpad_pro.a 10 | 11 | OBJECTS = $(addprefix $(BUILDDIR)/, $(addsuffix .o, $(basename $(SOURCES)))) 12 | 13 | # output files 14 | SYX = $(BUILDDIR)/launchpad_pro.syx 15 | ELF = $(BUILDDIR)/launchpad_pro.elf 16 | HEX = $(BUILDDIR)/launchpad_pro.hex 17 | HEXTOSYX = $(BUILDDIR)/hextosyx 18 | SIMULATOR = $(BUILDDIR)/simulator 19 | 20 | # tools 21 | HOST_GPP = g++ 22 | HOST_GCC = gcc 23 | CC = arm-none-eabi-gcc 24 | LD = arm-none-eabi-gcc 25 | OBJCOPY = arm-none-eabi-objcopy 26 | 27 | CFLAGS = -Os -Wall -I.\ 28 | -D_STM32F103RBT6_ -D_STM3x_ -D_STM32x_ -mthumb -mcpu=cortex-m3 \ 29 | -fsigned-char -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER -DHSE_VALUE=6000000UL \ 30 | -DCMSIS -DUSE_GLOBAL_CONFIG -ffunction-sections -std=c99 -mlittle-endian \ 31 | $(INCLUDES) -o 32 | 33 | LDSCRIPT = stm32_flash.ld 34 | 35 | LDFLAGS += -T$(LDSCRIPT) -u _start -u _Minimum_Stack_Size -mcpu=cortex-m3 -mthumb -specs=nano.specs -specs=nosys.specs -nostdlib -Wl,-static -N -nostartfiles -Wl,--gc-sections 36 | 37 | all: $(SYX) 38 | 39 | # build the final sysex file from the ELF - run the simulator first 40 | $(SYX): $(HEX) $(HEXTOSYX) $(SIMULATOR) 41 | ./$(SIMULATOR) 42 | ./$(HEXTOSYX) $(HEX) $(SYX) 43 | 44 | # build the tool for conversion of ELF files to sysex, ready for upload to the unit 45 | $(HEXTOSYX): 46 | $(HOST_GPP) -Ofast -std=c++0x -I./$(TOOLS)/libintelhex/include ./$(TOOLS)/libintelhex/src/intelhex.cc $(TOOLS)/hextosyx.cpp -o $(HEXTOSYX) 47 | 48 | # build the simulator (it's a very basic test of the code before it runs on the device!) 49 | $(SIMULATOR): 50 | $(HOST_GCC) -g3 -O0 -std=c99 -Iinclude $(TOOLS)/simulator.c $(SOURCES) -o $(SIMULATOR) 51 | 52 | $(HEX): $(ELF) 53 | $(OBJCOPY) -O ihex $< $@ 54 | 55 | $(ELF): $(OBJECTS) 56 | $(LD) $(LDFLAGS) -o $@ $(OBJECTS) $(LIB) 57 | 58 | DEPENDS := $(OBJECTS:.o=.d) 59 | 60 | -include $(DEPENDS) 61 | 62 | $(BUILDDIR)/%.o: %.c 63 | mkdir -p $(dir $@) 64 | $(CC) -c $(CFLAGS) -MMD -o $@ $< 65 | 66 | clean: 67 | rm -rf $(BUILDDIR) 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/dvhdr/launchpad-pro.svg?branch=master)](https://travis-ci.org/dvhdr/launchpad-pro) 2 | 3 | # Launchpad Pro 4 | Open source firmware for the Novation Launchpad Pro grid controller! By customising this code, you can: 5 | 6 | - Implement your own unique standalone apps 7 | - Create chorders, sequencers, light shows, games and more 8 | - Have fun! 9 | 10 | You'll definitely need *some* C programming experience, but we've deliberately kept much of the firmwarey nastiness tucked away, to make the process a little friendlier. 11 | 12 | # Philosophy 13 | We could have released the full source for the factory shipping firmware, but we decided not to for a variety of reasons. Instead, we created a simplified framework for developing "apps" on Launchpad Pro, which comprises a build environment, application entry points / API, and a library of low level source code. Our reasoning is as follows: 14 | 15 | - There is no value in customising low level routines such as LED multiplexing or ADC scanning - this code has been carefully tweaked over many months to deliver the best results, and is not something you'd want to mess with. 16 | - There is very little value in customising main() or other low level features, and again these things are hard to do well. Interrupt priorities? No. 17 | - If we shipped the application firmware as-is, we'd have a support nightmare on our hands (imagine the phone calls - my "Launchpad Pro is behaving strangely..."). Instead, we wanted to create a clear boundary between "normal" usage with Ableton, and custom firmware. As such, Ableton integration has been removed from this firmware, as has the setup / navigation functionality. In addition, the "Live" USB MIDI port has been removed, and the device has a different name and USB PID. 18 | - If we left the Ableton integration and menu structure in place, open firmware developers would have to work around it. They would also potentially consume precious RAM/CPU resources. I've a feeling this isn't what you'd want, but we're interested to hear your feedback. 19 | - Licensing requirements for the CMSIS library version we use are ambiguous. Yes, we could port to the public version, but why bother, given the above reasoning - I'd prefer to spend my time on good documentation and examples. As such, all the CMSIS code is compiled into launchpad_pro.a, and we do not need to distribute the headers. 20 | 21 | I'm sure you'll have feedback for us, so please do get in touch! I'm [blogging the process too](http://launchpadfirmware.tumblr.com/) if you'd like to read my musings. 22 | 23 | # Setup the Development Environment 24 | 25 | ## Using Docker 26 | 27 | If your system is running docker you can easily setup the environment with: 28 | 29 | ``` 30 | docker build -t novation-launchpad-pro-dev . 31 | docker run -it -v $(pwd):/launchpad-pro novation-launchpad-pro-dev 32 | make 33 | ``` 34 | 35 | ## Using Vagrant 36 | 37 | To use [Vagrant](https://www.vagrantup.com/) to manage the build environment you need to: 38 | 39 | 1. Clone this repository on your host computer (if using the command line, make sure you `git clone --recursive`). 40 | 2. Install [Vagrant](https://www.vagrantup.com/) 41 | 3. Install [VirtualBox](https://www.virtualbox.org/wiki/Downloads) 42 | 4. Open a command prompt, and navigate to the project directory 43 | 5. Type `vagrant up`, hit enter and grab a beverage of your choice. Maybe two - it is building a lovely fresh development machine just for you! 44 | 45 | If you have a poor internet connection, ummm, find a better one :) 46 | 47 | ### Building 48 | Once your new "box" is up and running, you can build the app in one of two ways. In the spirit of experimentation, we've created a full Eclipse development environment for you to use. However, you might prefer to do things on the command line. 49 | 50 | ### To use the command line interface: 51 | 1. SSH into the Vagrant "box" by doing `vagrant ssh` 52 | 2. At the command prompt, simply type `make` 53 | 54 | ### To build using Eclipse GUI 55 | 56 | **Make sure you wait until the `vagrant up` command has fully completed** before logging in to your VM. The GUI appears long before the provisioning script finishes. If you don't, you'll have to log out and log back in again before Eclipse can see the correct path. 57 | 58 | 1. Log in to the Ubuntu GUI (the password is, as is the convention, **vagrant**). 59 | 2. Launch Eclipse from the doodah on the top left (it's a bit like Spotlight) 60 | 3. Accept the default when Eclipse asks you for a workspace. I can't figure out how to store the workspace in source control, so you need to import it. 61 | 4. Click "Workbench" at the Eclipse startup screen. 62 | 5. In Eclipse, choose "File->Import..." 63 | 6. Under "C/C++", choose "Existing Code as Makefile Project", hit "Next" 64 | 7. Give the project any name you like (launchpad?) 65 | 8. Under "Existing Code Location" type `/vagrant`. The toolchain isn't important, the compiler is part of the Makefile. 66 | 9. Hit Finish - you should now see your project. 67 | 10. Select your project by clicking on it. 68 | 11. Click the hammer icon at the top, and wait while the project builds. 69 | 70 | Either of the above methods will generate the firmware image, `launchpad_pro.syx`, in the project `build` directory. You can then upload this to your Launchpad Pro from the host! 71 | 72 | ## Using macOS 73 | 74 | On macOS you can easily install the GCC ARM toolchain using the [homebrew package manager](http://brew.sh). The EABI tools are maintained in an external repository which you need to put on tap first. You can then run ```make``` to directly compile the code: 75 | 76 | ``` 77 | brew tap PX4/homebrew-px4 78 | brew install gcc-arm-none-eabi 79 | make 80 | ``` 81 | 82 | # Uploading to a Launchpad Pro 83 | Now you've got some nice new code to run! To upload it to your Launchpad Pro, you'll need a sysex tool for your host platform (I'd love to get it working from the virtual machine, but that's for later). I recommend [Sysex Librarian](http://www.snoize.com/SysExLibrarian/) on macOS, and [MIDI OX](http://www.midiox.com/) on Windows. On Linux, I'll bet you already have a tool in mind. 84 | 85 | I won't describe how to use these tools, I'm sure you already know - and if you don't, their documentation is superior to mine! Here's what you need to do: 86 | 87 | 1. Unplug your Launchpad Pro 88 | 2. Hold the "Setup" button down while connecting it to your host via USB (ensure it's connected to the host, and not to a virtual machine!) 89 | 3. The unit will start up in "bootloader" mode 90 | 4. Send your launchpad_pro.syx file to the device MIDI port - it will briefly scroll "upgrading..." across the grid. 91 | 5. Wait for the update to complete, and for the device to reboot! 92 | 93 | Tip - set the delay between sysex messages to as low a value as possible, so you're not waiting about for ages while the firmware uploads! 94 | 95 | # Bricked it! 96 | Don't worry - even if you upload toxic nonsense to the device, you cannot brick it - the bootloader is stored in a protected area of flash. If your new firmware doesn't boot, you'll get stuck at step (3) above, or with a crashed unit. Simply repeat the above process with the shipping firmware image (`resources/Launchpad Pro-1.0.154.syx`) to restore your unit to the factory defaults. Better yet, fix the bugs :) 97 | 98 | # The API 99 | The API works in two directions - from the HAL (hardware abstraction layer) to the app, and from the app to the HAL. The HAL calls into your app to: 100 | 101 | - Receive user events from the pads and buttons 102 | - Receive messages from the MIDI/USB ports 103 | - Receive a tick message to drive timer based code 104 | - Be notified when someone connects or disconnects a MIDI cable 105 | 106 | By calling into the HAL, your app can: 107 | 108 | - Write colours to the LEDs 109 | - Send messages to the MIDI/USB ports 110 | - Store and recall a little bit of data on the Launchpad Pro's flash memory 111 | 112 | The best way to learn about these is to read the documentation in `app.h`, and to study the (very basic) example code! 113 | 114 | # Debugging 115 | We decided not to support or encourage using a hardware debugger, as opening a Launchpad Pro to fit a debugging header can easily damage the FSR (force sensitive resistor) sheet. 116 | 117 | Instead, you're going to have to do things the old fashioned way - by blinking LEDs or sending MIDI messages (though hopefully no need for a 'scope!). For what it's worth, that's the way I've developed this version of the firmware - dogfooding all the way ;) 118 | 119 | If do you want to debug interactively (and of course you do), you can use the interactive desktop simulator on macOS: 120 | 121 | 1. Build the Xcode project located in `/tools/osx` 122 | 2. Connect your Launchpad Pro 123 | 3. Install the factory firmware on your Launchpad Pro 124 | 4. Put the Launchpad Pro into "Programmer" mode using the Setup button (you'll get odd behaviour otherwise) 125 | 5. Start debugging in Xcode! 126 | 127 | Currently it only supports button presses and LED messages - there's no setup button, flash storage or aftertouch (yet). It has a really awful busywaiting timer for the 1kHz tick. However, it does allow you to debug your application logic using Xcode! 128 | 129 | You can also use the simple command-line simulator located in the `/tools` directory. It is compiled and ran as part of the build process, so it serves as a very basic test of your app before it is baked into a sysex dump - more of a test harness. 130 | 131 | To debug the simulator interactively in Eclipse: 132 | 133 | 1. Click the down arrow next to the little "bug" icon in the toolbar 134 | 2. Choose "Debug configurations..." 135 | 3. Right click "C/C++ Application" and choose "New...: 136 | 4. Under "C/C++ Application" click Browse... and locate the simulator binary at `/vagrant/build/simulator` 137 | 5. Hit "Debug"! 138 | 139 | # The Hardware 140 | The Launchpad Pro is based around an ARM Cortex M3 from STMicroelectronics. Specifically, an [STM32F103RBT6](http://www.st.com/web/catalog/mmc/FM141/SC1169/SS1031/LN1565/PF164487). It's clocked at 72MHz, and has 20k RAM (I'm not sure how much of this we're using in the open build yet - should be a fair amount left but I haven't measured it). The low level LED multiplexing and pad/switch scanning consume a fair bit of CPU time in interrupt mode, but have changed a little in the open firmware library (so again, I don't have measurements for how many cycles they're using). 141 | 142 | It has 128k of flash memory, but we won't be exposing all of it as part of this API (dangerously easy to corrupt things!). 143 | 144 | # Vagrant tips 145 | When you're done developing, simply type `vagrant suspend` to halt your VM without destroying it - this will make `vagrant up` a lot quicker next time. If you're really finished, `vagrant destroy` will completely remove the VM from your system (but not any of your code). 146 | 147 | If you only want to build using the command line, you might want to run your Vagrant box headless, which you can do by modifying the Vagrantfile: `vb.gui = false`. You can also add more CPUs, RAM etc. if you want. 148 | 149 | If prefer, you can install the gcc-arm toolchain on your local machine, or you might already have it. You can find all you need [here](http://gnuarmeclipse.livius.net/). 150 | 151 | If your connection drops out while updating the Vagrant box, you can get stuck, unable to `vagrant up`. To resolve, you need to delete the temp file - `~/vagrant.d/tmp`. 152 | 153 | # Firmware development tips 154 | OK - we're not going to need to use the MISRA rules, but there are a few things to avoid. Dynamic memory allocation is a no (well it will work, but it's best avoided). Floating point will work, but it's implemented in software and will be slooooow. C++ ought to work, but you'll definitely want to avoid exceptions and RTTI! 155 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 5 | # configures the configuration version (we support older styles for 6 | # backwards compatibility). Please don't change it unless you know what 7 | # you're doing. 8 | Vagrant.configure(2) do |config| 9 | # The most common configuration options are documented and commented below. 10 | # For a complete reference, please see the online documentation at 11 | # https://docs.vagrantup.com. 12 | 13 | # Every Vagrant development environment requires a box. You can search for 14 | # boxes at https://atlas.hashicorp.com/search. 15 | config.vm.box = "drm2/ubuntu-14.04-desktop-x64" 16 | 17 | # Disable automatic box update checking. If you disable this, then 18 | # boxes will only be checked for updates when the user runs 19 | # `vagrant box outdated`. This is not recommended. 20 | # config.vm.box_check_update = false 21 | 22 | config.vm.provision :shell, path: "bootstrap.sh" 23 | 24 | # Create a forwarded port mapping which allows access to a specific port 25 | # within the machine from a port on the host machine. In the example below, 26 | # accessing "localhost:8080" will access port 80 on the guest machine. 27 | # config.vm.network "forwarded_port", guest: 80, host: 8080 28 | 29 | # Create a private network, which allows host-only access to the machine 30 | # using a specific IP. 31 | # config.vm.network "private_network", ip: "192.168.33.10" 32 | 33 | # Create a public network, which generally matched to bridged network. 34 | # Bridged networks make the machine appear as another physical device on 35 | # your network. 36 | # config.vm.network "public_network" 37 | 38 | # Share an additional folder to the guest VM. The first argument is 39 | # the path on the host to the actual folder. The second argument is 40 | # the path on the guest to mount the folder. And the optional third 41 | # argument is a set of non-required options. 42 | # config.vm.synced_folder "../data", "/vagrant_data" 43 | 44 | # Provider-specific configuration so you can fine-tune various 45 | # backing providers for Vagrant. These expose provider-specific options. 46 | # Example for VirtualBox: 47 | # 48 | config.vm.provider "virtualbox" do |vb| 49 | # Display the VirtualBox GUI when booting the machine 50 | vb.gui = true 51 | 52 | # Customize the amount of memory on the VM: 53 | vb.memory = "2048" 54 | 55 | # let’s have something usable for proper development 56 | vb.cpus = 2 57 | end 58 | 59 | # View the documentation for the provider you are using for more 60 | # information on available options. 61 | 62 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 63 | # such as FTP and Heroku are also available. See the documentation at 64 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 65 | # config.push.define "atlas" do |push| 66 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 67 | # end 68 | 69 | # Enable provisioning with a shell script. Additional provisioners such as 70 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 71 | # documentation for more information about their specific syntax and use. 72 | # config.vm.provision "shell", inline: <<-SHELL 73 | # sudo apt-get update 74 | # sudo apt-get install -y apache2 75 | # SHELL 76 | end 77 | -------------------------------------------------------------------------------- /bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # setup as described here: http://gnuarmeclipse.livius.net/blog/toolchain-install/#GNULinux 4 | # We're using Luna - need to update plugin install locations below if switching to Kepler etc. 5 | ECLIPSE_URL=http://mirror.netcologne.de/eclipse//technology/epp/downloads/release/luna/SR2/eclipse-cpp-luna-SR2-linux-gtk-x86_64.tar.gz 6 | 7 | # latest GCC-ARM version 8 | GCC_ARM_URL=https://launchpad.net/gcc-arm-embedded/4.8/4.8-2014-q3-update/+download/gcc-arm-none-eabi-4_8-2014q3-20140805-linux.tar.bz2 9 | 10 | # install tools for running 32-bit binaries 11 | apt-get update 12 | sudo apt-get install -y lib32z1 lib32ncurses5 lib32bz2-1.0 13 | 14 | # install Java 15 | sudo apt-get install -y openjdk-7-jdk 16 | 17 | # download Eclipse 18 | wget -O /tmp/eclipse.tar.gz $ECLIPSE_URL 19 | 20 | # Extract files 21 | sudo tar zxvf /tmp/eclipse.tar.gz -C /opt/ 22 | # Change ownership to root 23 | sudo chown -R root:root /opt/eclipse/ 24 | 25 | # Create launch script in /usr/bin 26 | echo '#!/bin/sh' | sudo tee /usr/bin/eclipse 27 | echo 'export ECLIPSE_HOME="/opt/eclipse"' | sudo tee -a /usr/bin/eclipse 28 | echo '$ECLIPSE_HOME/eclipse $*' | sudo tee -a /usr/bin/eclipse 29 | sudo chmod 755 /usr/bin/eclipse 30 | 31 | # Create menu entry 32 | echo "[Desktop Entry]" | sudo tee /usr/share/applications/eclipse.desktop 33 | echo "Name=Eclipse IDE" | sudo tee -a /usr/share/applications/eclipse.desktop 34 | echo "Comment=Integrated Development Environment" | sudo tee -a /usr/share/applications/eclipse.desktop 35 | echo "TryExec=/usr/bin/eclipse" | sudo tee -a /usr/share/applications/eclipse.desktop 36 | echo "Exec=/usr/bin/eclipse" | sudo tee -a /usr/share/applications/eclipse.desktop 37 | echo "Icon=/opt/eclipse/icon.xpm" | sudo tee -a /usr/share/applications/eclipse.desktop 38 | echo "Categories=Development;IDE;Java;" | sudo tee -a /usr/share/applications/eclipse.desktop 39 | echo "Terminal=false" | sudo tee -a /usr/share/applications/eclipse.desktop 40 | echo "Type=Application" | sudo tee -a /usr/share/applications/eclipse.desktop 41 | echo "StartupNotify=true" | sudo tee -a /usr/share/applications/eclipse.desktop 42 | 43 | # Delete the eclipse tarball 44 | rm /tmp/eclipse.tar.gz 45 | 46 | # download the gcc-arm tools 47 | wget -O /tmp/gcc-arm.tar.bz2 $GCC_ARM_URL 48 | 49 | # expand and add it to the path (yes, I know it says don't ever do this - but this is a Vagrant box ;) 50 | sudo tar xjf /tmp/gcc-arm.tar.bz2 -C /usr/local 51 | 52 | # set up path to arm gcc on login 53 | sudo echo "export PATH=/usr/local/gcc-arm-none-eabi-4_8-2014q3/bin:$PATH" >> /etc/profile.d/env.sh 54 | 55 | # Delete the gcc-arm tarball 56 | rm /tmp/gcc-arm.tar.bz2 57 | 58 | # helpfully cd to the /vagrant dir on login 59 | echo "cd /vagrant" >> /home/vagrant/.bashrc 60 | -------------------------------------------------------------------------------- /include/app.h: -------------------------------------------------------------------------------- 1 | #ifndef LAUNCHPAD_APP_H 2 | #define LAUNCHPAD_APP_H 3 | 4 | /****************************************************************************** 5 | 6 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | *****************************************************************************/ 35 | 36 | // ____________________________________________________________________________ 37 | // 38 | // Don't modify this file! This declares the binary interface to the library, 39 | // so modifying it will probably break things. 40 | // ____________________________________________________________________________ 41 | // 42 | #include "app_defs.h" 43 | 44 | /****************************************************************************** 45 | Button indexing is as follows - numbers in brackets do not correspond to real 46 | buttons, but can be harmessly sent in hal_set_led. 47 | 48 | (90)91 92 93 94 95 96 97 98 (99) 49 | ....... 50 | 20 21 22 23 24 25 26 27 28 29 51 | 10 11 12 13 14 15 16 17 18 19 52 | (0) 1 2 3 4 5 6 7 8 (9) 53 | 54 | *****************************************************************************/ 55 | 56 | // ____________________________________________________________________________ 57 | // 58 | // Interface to the hardware (implemented in launchpad_pro.a library) 59 | // ____________________________________________________________________________ 60 | 61 | /** 62 | * Set an LED's RGB value. This function is safe to call from any 63 | * of the app functions below, at any time. 64 | * 65 | * @param type - TYPEPAD to address any pad or button, TYPESETUP to address the Setup button 66 | * @param index - The index of the button. The buttons are indexed from the bottom left as detailed above. 67 | * @param red - red colour value, in [0, MAXLED] 68 | * @param green - green colour value, in [0, MAXLED] 69 | * @param blue - blue colour value, in [0, MAXLED] 70 | */ 71 | void hal_plot_led(u8 type, u8 index, u8 red, u8 green, u8 blue); 72 | 73 | /** 74 | * Read the RGB value of an LED. This function is safe to call from any 75 | * of the app functions below, at any time. Result is undefined if an invalid address is passed 76 | * for any of the red, green or blue components. 77 | * 78 | * @param type - TYPEPAD to address any pad or button, TYPESETUP to address the Setup LED 79 | * @param index - The index of the button, as above 80 | * @param red - address to read red colour value, in [0, MAXLED]. 81 | * @param green - address to read green colour value, in [0, MAXLED] 82 | * @param blue - address to read blue colour value, in [0, MAXLED] 83 | */ 84 | void hal_read_led(u8 type, u8 index, u8 *red, u8 *green, u8 *blue); 85 | 86 | /** 87 | * Send a MIDI message to either USB port or to the DIN output. 88 | * 89 | * @param port - which port to send the message to - can be USBSTANDALONE, USBMIDI or DINMIDI. 90 | * @param status - MIDI status byte 91 | * @param data1 - first MIDI data byte 92 | * @param data2 - second MIDI data byte 93 | * 94 | * There is little error checking in this code - if you send invalid MIDI, the results are undefined! 95 | */ 96 | void hal_send_midi(u8 port, u8 status, u8 data1, u8 data2); 97 | 98 | /** 99 | * Send system exclusive to USB or DIN. Messages must be correctly formatted 100 | * (F0 ... F7) and must not exceed 320 bytes. 101 | * 102 | * @param port - which port to send the message to - can be USBSTANDALONE, USBMIDI or DINMIDI. 103 | * @param data - pointer to array containing sysex data. Can be on the stack. 104 | * @param length - must not exceed 320 bytes, behaviour undefined if it does. 105 | */ 106 | void hal_send_sysex(u8 port, const u8* data, u16 length); 107 | 108 | /** 109 | * Read some data from flash. 110 | * 111 | * Flash storage is in a single block, currently corresponding to one page. 112 | * The block is always USER_AREA_SIZE bytes long. You can read/write any bytes 113 | * within it - you do not need to worry about paging, that's handled by the HAL. 114 | * 115 | * The block size may increase to span multiple pages in future :) 116 | * 117 | * @param offset - how far into the USER_AREA_SIZE byte block to start reading 118 | * @param data - buffer to receive data (must be at least length bytes long) 119 | * @param length - bytes to read 120 | * 121 | * Attempts to read beyond the end of the block will fail silently. 122 | * 123 | * Note that your first ever read from a new device will contain 0xFF's until 124 | * you overwrite them. 125 | */ 126 | void hal_read_flash(u32 offset, u8 *data, u32 length); 127 | 128 | /** 129 | * Write data to flash 130 | * 131 | * Do take care to avoid thrashing it, as you can wear it out with excessive 132 | * writes. The HAL does not currently do anything clever to mitigate this. 133 | * 134 | * @param offset - how far into the USER_AREA_SIZE byte block to start writing 135 | * @param data - buffer to write (must be at least length bytes long) 136 | * @param length - bytes to write 137 | * 138 | * Attempts to write beyond the end of the block will fail silently 139 | */ 140 | void hal_write_flash(u32 offset,const u8 *data, u32 length); 141 | 142 | /** 143 | * Retrieve the device ID bootloader option 144 | * 145 | * Users can set a unique ID from 1-16 in the bootloader. This is useful 146 | * for USB apps, as it helps multi-Launchpad setups behave predictably. 147 | * 148 | * @result the zero-based device ID [0-15] assigned to this Launchpad Pro. 149 | */ 150 | u8 hal_read_device_id(); 151 | 152 | /** 153 | * Retrieve the "layout text" bootloader option 154 | * 155 | * This setting determines whether the factory firmware will scroll text on changing 156 | * layouts. This may be useful as a preference for open firmware apps as well. 157 | * 158 | * @result 1 to scroll text on layout changes, 0 not to. 159 | */ 160 | u8 hal_read_layout_text(); 161 | 162 | // ____________________________________________________________________________ 163 | // 164 | // Callbacks from the hardware (implemented in your app.c) 165 | // ____________________________________________________________________________ 166 | 167 | /** 168 | * Called on startup, this is a good place to do any initialisation. 169 | * 170 | * @param adc_buffer - this is a pointer to the raw ADC frame buffer. The 171 | * data is 12 bit unsigned. Note the indexing is strange - 172 | * translate ADC indices to LED/button indices using the 173 | * ADC_MAP table declared in app_defs.h. 174 | */ 175 | void app_init(const u16 *adc_buffer); 176 | 177 | /** 178 | * 1kHz (1ms) timer. You can set LEDs and send MIDI out from this function, 179 | * but you will get LED tearing as there is (currently) no double buffering. 180 | * 181 | * You will get some jitter. 182 | */ 183 | void app_timer_event(); 184 | 185 | /** 186 | * Called when a MIDI message is received from USB or DIN. 187 | * 188 | * @param port - the port the message was received from - USBSTANDALONE, USBMIDI or DINMIDI. 189 | * @param status - MIDI status byte 190 | * @param data1 - first MIDI data byte 191 | * @param data2 - second MIDI data byte 192 | */ 193 | void app_midi_event(u8 port, u8 status, u8 d1, u8 d2); 194 | 195 | /** 196 | * As above, but for system exclusive messages. Low level hardware buffering sets 197 | * a maximum message size of 320 bytes, messages larger than this will not work. 198 | * 199 | * @param port - the port the message was received from - USBSTANDALONE, USBMIDI or DINMIDI. 200 | * @param data - pointer to array containing sysex data. Only valid in the scope of this callback. 201 | * @param length - the amount of data received. 202 | */ 203 | void app_sysex_event(u8 port, u8 * data, u16 count); 204 | 205 | /** 206 | * Called when a MIDI DIN breakout cable is connected or disconnected. Note that 207 | * you can still write MIDI events to the DIN ports even if no cable is connected. 208 | * 209 | * @param type - which cable was connected/disconnected - MIDI_IN_CABLE or MIDI_OUT_CABLE. 210 | * @param value - 0 = disconnected, nonzero = connected. 211 | */ 212 | void app_cable_event(u8 type, u8 value); 213 | 214 | /** 215 | * Called when the user presses or releases any button or pad on the control surface. 216 | * 217 | * @param type - TYPEPAD for normal pads or buttons, TYPESETUP for the Setup button 218 | * @param index - The index of the button, as detailed at the start of this file. 219 | * @param value - 0 for release, nonzero for press. 220 | */ 221 | void app_surface_event(u8 type, u8 index, u8 value); 222 | 223 | /** 224 | * Called when the low level pad scanning reports an aftertouch (pad pressure) event. 225 | * A pad press event will always come first. Note that the factory firmware sets a 226 | * threshold to prevent excessive aftertouch after the initial hit, at the expense of 227 | * dynamic range. This firmware does not - the full range of the pad is transmitted, 228 | * with the side effect that aftertouch onset is more rapid. 229 | * 230 | * @param index - The index of the pad, as detailed at the start of this file. 231 | * @param value - the aftertouch value in [0, 127] 232 | */ 233 | void app_aftertouch_event(u8 index, u8 value); 234 | 235 | 236 | #endif 237 | -------------------------------------------------------------------------------- /include/app_defs.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef APP_TYPES_H 3 | #define APP_TYPES_H 4 | 5 | /****************************************************************************** 6 | 7 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 8 | All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are met: 12 | 13 | * Redistributions of source code must retain the above copyright notice, this 14 | list of conditions and the following disclaimer. 15 | 16 | * Redistributions in binary form must reproduce the above copyright notice, 17 | this list of conditions and the following disclaimer in the documentation 18 | and/or other materials provided with the distribution. 19 | 20 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 21 | contributors may be used to endorse or promote products derived from 22 | this software without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 25 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 27 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 28 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 30 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 31 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 32 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 33 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 | 35 | *****************************************************************************/ 36 | 37 | // ____________________________________________________________________________ 38 | // 39 | // Don't modify this file! This declares the binary interface to the library, 40 | // so modifying it will probably break things. 41 | // ____________________________________________________________________________ 42 | // 43 | // Types 44 | // ____________________________________________________________________________ 45 | 46 | typedef signed long s32; 47 | typedef signed short s16; 48 | typedef signed char s8; 49 | 50 | typedef unsigned long u32; 51 | typedef unsigned short u16; 52 | typedef unsigned char u8; 53 | 54 | // ____________________________________________________________________________ 55 | // 56 | // App structure 57 | // ____________________________________________________________________________ 58 | 59 | #define TYPEPAD 0 60 | #define TYPESETUP 1 61 | 62 | #define MAXLED 63 63 | 64 | // ____________________________________________________________________________ 65 | // 66 | // Useful MIDI constants 67 | // ____________________________________________________________________________ 68 | 69 | #define NOTEON 0x90 70 | #define NOTEOFF 0x80 71 | #define POLYAFTERTOUCH 0xA0 72 | #define CC 0xB0 73 | #define CHANNELAFTERTOUCH 0xD0 74 | #define SONGPOSITIONPOINTER 0xF2 75 | #define MIDITIMINGCLOCK 0xF8 76 | #define MIDISTART 0xFA 77 | #define MIDICONTINUE 0xFB 78 | #define MIDISTOP 0xFC 79 | 80 | // ____________________________________________________________________________ 81 | // 82 | // MIDI ports 83 | // ____________________________________________________________________________ 84 | 85 | // USB MIDI: "Standalone" port 86 | #define USBSTANDALONE 0 87 | 88 | // USB MIDI: "MIDI" port 89 | #define USBMIDI 1 90 | 91 | // MIDI DIN port 92 | #define DINMIDI 2 93 | 94 | // ____________________________________________________________________________ 95 | // 96 | // MIDI Jack Socket Switch IDs 97 | // ____________________________________________________________________________ 98 | 99 | #define MIDI_IN_CABLE 0 100 | #define MIDI_OUT_CABLE 1 101 | 102 | // ____________________________________________________________________________ 103 | // 104 | // Flash storage 105 | // ____________________________________________________________________________ 106 | 107 | #define USER_AREA_SIZE 1024 108 | 109 | // ____________________________________________________________________________ 110 | // 111 | // Raw ADC reads. For technical reasons, the ADC layout is not the same as the 112 | // LED layout. Be sure to translate ADC values using the table below as you 113 | // need them! 114 | // ____________________________________________________________________________ 115 | 116 | #define PAD_COUNT 64 117 | 118 | static const u8 ADC_MAP[PAD_COUNT] = 119 | { 120 | 11, 51, 12, 52, 13, 53, 14, 54, 121 | 15, 55, 16, 56, 17 ,57, 18, 58, 122 | 21, 61, 22, 62, 23, 63, 24, 64, 123 | 25, 65, 26, 66, 27, 67, 28, 68, 124 | 31, 71, 32, 72, 33, 73, 34, 74, 125 | 35, 75, 36, 76, 37, 77, 38, 78, 126 | 41, 81, 42, 82, 43, 83, 44, 84, 127 | 45, 85, 46, 86, 47, 87, 48, 88, 128 | }; 129 | 130 | // ____________________________________________________________________________ 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /lib/launchpad_pro.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvhdr/launchpad-pro/315d8d16fcd39073b4b868db342009c772da2256/lib/launchpad_pro.a -------------------------------------------------------------------------------- /resources/Launchpad Pro-1.0.154.syx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dvhdr/launchpad-pro/315d8d16fcd39073b4b868db342009c772da2256/resources/Launchpad Pro-1.0.154.syx -------------------------------------------------------------------------------- /src/app.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *****************************************************************************/ 32 | 33 | //______________________________________________________________________________ 34 | // 35 | // Headers 36 | //______________________________________________________________________________ 37 | 38 | #include "app.h" 39 | 40 | //______________________________________________________________________________ 41 | // 42 | // This is where the fun is! Add your code to the callbacks below to define how 43 | // your app behaves. 44 | // 45 | // In this example, we either render the raw ADC data as LED rainbows or store 46 | // and recall the pad state from flash. 47 | //______________________________________________________________________________ 48 | 49 | // store ADC frame pointer 50 | static const u16 *g_ADC = 0; 51 | 52 | // buffer to store pad states for flash save 53 | #define BUTTON_COUNT 100 54 | 55 | u8 g_Buttons[BUTTON_COUNT] = {0}; 56 | 57 | //______________________________________________________________________________ 58 | 59 | void app_surface_event(u8 type, u8 index, u8 value) 60 | { 61 | switch (type) 62 | { 63 | case TYPEPAD: 64 | { 65 | // toggle it and store it off, so we can save to flash if we want to 66 | if (value) 67 | { 68 | g_Buttons[index] = MAXLED * !g_Buttons[index]; 69 | } 70 | 71 | // example - light / extinguish pad LEDs 72 | hal_plot_led(TYPEPAD, index, 0, 0, g_Buttons[index]); 73 | 74 | // example - send MIDI 75 | hal_send_midi(DINMIDI, NOTEON | 0, index, value); 76 | 77 | } 78 | break; 79 | 80 | case TYPESETUP: 81 | { 82 | if (value) 83 | { 84 | // save button states to flash (reload them by power cycling the hardware!) 85 | hal_write_flash(0, g_Buttons, BUTTON_COUNT); 86 | } 87 | } 88 | break; 89 | } 90 | } 91 | 92 | //______________________________________________________________________________ 93 | 94 | void app_midi_event(u8 port, u8 status, u8 d1, u8 d2) 95 | { 96 | // example - MIDI interface functionality for USB "MIDI" port -> DIN port 97 | if (port == USBMIDI) 98 | { 99 | hal_send_midi(DINMIDI, status, d1, d2); 100 | } 101 | 102 | // // example -MIDI interface functionality for DIN -> USB "MIDI" port port 103 | if (port == DINMIDI) 104 | { 105 | hal_send_midi(USBMIDI, status, d1, d2); 106 | } 107 | } 108 | 109 | //______________________________________________________________________________ 110 | 111 | void app_sysex_event(u8 port, u8 * data, u16 count) 112 | { 113 | // example - respond to UDI messages? 114 | } 115 | 116 | //______________________________________________________________________________ 117 | 118 | void app_aftertouch_event(u8 index, u8 value) 119 | { 120 | // example - send poly aftertouch to MIDI ports 121 | hal_send_midi(USBMIDI, POLYAFTERTOUCH | 0, index, value); 122 | 123 | 124 | } 125 | 126 | //______________________________________________________________________________ 127 | 128 | void app_cable_event(u8 type, u8 value) 129 | { 130 | // example - light the Setup LED to indicate cable connections 131 | if (type == MIDI_IN_CABLE) 132 | { 133 | hal_plot_led(TYPESETUP, 0, 0, value, 0); // green 134 | } 135 | else if (type == MIDI_OUT_CABLE) 136 | { 137 | hal_plot_led(TYPESETUP, 0, value, 0, 0); // red 138 | } 139 | } 140 | 141 | //______________________________________________________________________________ 142 | 143 | void app_timer_event() 144 | { 145 | // example - send MIDI clock at 125bpm 146 | #define TICK_MS 20 147 | 148 | static u8 ms = TICK_MS; 149 | 150 | if (++ms >= TICK_MS) 151 | { 152 | ms = 0; 153 | 154 | // send a clock pulse up the USB 155 | hal_send_midi(USBSTANDALONE, MIDITIMINGCLOCK, 0, 0); 156 | } 157 | 158 | // alternative example - show raw ADC data as LEDs 159 | for (int i=0; i < PAD_COUNT; ++i) 160 | { 161 | // raw adc values are 12 bit, but LEDs are 6 bit. 162 | // Let's saturate into r;g;b for a rainbow effect to show pressure 163 | u16 r = 0; 164 | u16 g = 0; 165 | u16 b = 0; 166 | 167 | u16 x = (3 * MAXLED * g_ADC[i]) >> 12; 168 | 169 | if (x < MAXLED) 170 | { 171 | r = x; 172 | } 173 | else if (x >= MAXLED && x < (2*MAXLED)) 174 | { 175 | r = 2*MAXLED - x; 176 | g = x - MAXLED; 177 | } 178 | else 179 | { 180 | g = 3*MAXLED - x; 181 | b = x - 2*MAXLED; 182 | } 183 | 184 | hal_plot_led(TYPEPAD, ADC_MAP[i], r, g, b); 185 | } 186 | } 187 | 188 | //______________________________________________________________________________ 189 | 190 | void app_init(const u16 *adc_raw) 191 | { 192 | // example - load button states from flash 193 | hal_read_flash(0, g_Buttons, BUTTON_COUNT); 194 | 195 | // example - light the LEDs to say hello! 196 | for (int i=0; i < 10; ++i) 197 | { 198 | for (int j=0; j < 10; ++j) 199 | { 200 | u8 b = g_Buttons[j*10 + i]; 201 | 202 | hal_plot_led(TYPEPAD, j*10 + i, 0, 0, b); 203 | } 204 | } 205 | 206 | // store off the raw ADC frame pointer for later use 207 | g_ADC = adc_raw; 208 | } 209 | -------------------------------------------------------------------------------- /stm32_flash.ld: -------------------------------------------------------------------------------- 1 | /* Entry Point */ 2 | ENTRY(Reset_Handler) 3 | 4 | /* Highest address of the user mode stack */ 5 | _estack = 0x20005000; /* end of 20K RAM */ 6 | 7 | /* Generate a link error if heap and stack don't fit into RAM */ 8 | _Min_Heap_Size = 0; /* required amount of heap */ 9 | _Min_Stack_Size = 0x200; /* required amount of stack */ 10 | _Reserve_Stack_Value = 4; 11 | 12 | /* Specify the memory areas. Set FLASH ORIGIN to 0x08000000 for debugging. */ 13 | MEMORY 14 | { 15 | FLASH (rx) : ORIGIN = 0x08006400, LENGTH = 128K 16 | RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 20K 17 | MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K 18 | } 19 | 20 | /* Define output sections */ 21 | SECTIONS 22 | { 23 | /* The startup code goes first into FLASH */ 24 | .isr_vector : 25 | { 26 | . = ALIGN(4); 27 | KEEP(*(.isr_vector)) /* Startup code */ 28 | . = ALIGN(4); 29 | } >FLASH 30 | 31 | /* The program code and other data goes into FLASH */ 32 | .text : 33 | { 34 | . = ALIGN(4); 35 | *(.text) /* .text sections (code) */ 36 | *(.text*) /* .text* sections (code) */ 37 | *(.rodata) /* .rodata sections (constants, strings, etc.) */ 38 | *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ 39 | *(.glue_7) /* glue arm to thumb code */ 40 | *(.glue_7t) /* glue thumb to arm code */ 41 | 42 | KEEP (*(.init)) 43 | KEEP (*(.fini)) 44 | 45 | . = ALIGN(4); 46 | _etext = .; /* define a global symbols at end of code */ 47 | } >FLASH 48 | 49 | 50 | .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH 51 | .ARM : { 52 | __exidx_start = .; 53 | *(.ARM.exidx*) 54 | __exidx_end = .; 55 | } >FLASH 56 | 57 | .ARM.attributes : { *(.ARM.attributes) } > FLASH 58 | 59 | .preinit_array : 60 | { 61 | PROVIDE_HIDDEN (__preinit_array_start = .); 62 | KEEP (*(.preinit_array*)) 63 | PROVIDE_HIDDEN (__preinit_array_end = .); 64 | } >FLASH 65 | .init_array : 66 | { 67 | PROVIDE_HIDDEN (__init_array_start = .); 68 | KEEP (*(SORT(.init_array.*))) 69 | KEEP (*(.init_array*)) 70 | PROVIDE_HIDDEN (__init_array_end = .); 71 | } >FLASH 72 | .fini_array : 73 | { 74 | PROVIDE_HIDDEN (__fini_array_start = .); 75 | KEEP (*(.fini_array*)) 76 | KEEP (*(SORT(.fini_array.*))) 77 | PROVIDE_HIDDEN (__fini_array_end = .); 78 | } >FLASH 79 | 80 | /* used by the startup to initialize data */ 81 | _sidata = .; 82 | 83 | /* Initialized data sections goes into RAM, load LMA copy after code */ 84 | .data : AT ( _sidata ) 85 | { 86 | . = ALIGN(4); 87 | _sdata = .; /* create a global symbol at data start */ 88 | *(.data) /* .data sections */ 89 | *(.data*) /* .data* sections */ 90 | 91 | . = ALIGN(4); 92 | _edata = .; /* define a global symbol at data end */ 93 | } >RAM 94 | 95 | /* Uninitialized data section */ 96 | . = ALIGN(4); 97 | .bss : 98 | { 99 | /* This is used by the startup in order to initialize the .bss secion */ 100 | _sbss = .; /* define a global symbol at bss start */ 101 | __bss_start__ = _sbss; 102 | *(.bss) 103 | *(.bss*) 104 | *(COMMON) 105 | 106 | . = ALIGN(4); 107 | _ebss = .; /* define a global symbol at bss end */ 108 | __bss_end__ = _ebss; 109 | } >RAM 110 | 111 | PROVIDE ( end = _ebss ); 112 | PROVIDE ( _end = _ebss ); 113 | 114 | /* User_heap_stack section, used to check that there is enough RAM left */ 115 | /* also reserve space for the stack overun protection value */ 116 | ._user_heap_stack : 117 | { 118 | . = ALIGN(4); 119 | . = . + _Min_Heap_Size; 120 | . = ALIGN(4); 121 | . = . + _Reserve_Stack_Value ; 122 | . = ALIGN(4); 123 | . = . + _Min_Stack_Size; 124 | . = ALIGN(4); 125 | } >RAM 126 | 127 | /* MEMORY_bank1 section, code must be located here explicitly */ 128 | .memory_b1_text : 129 | { 130 | *(.mb1text) /* .mb1text sections (code) */ 131 | *(.mb1text*) /* .mb1text* sections (code) */ 132 | *(.mb1rodata) /* read-only data (constants) */ 133 | *(.mb1rodata*) 134 | } >MEMORY_B1 135 | 136 | /* Remove information from the standard libraries */ 137 | /DISCARD/ : 138 | { 139 | libc.a ( * ) 140 | libm.a ( * ) 141 | libgcc.a ( * ) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /tools/hextosyx.cpp: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *****************************************************************************/ 32 | 33 | // Ported from the original Delphi version. 34 | // CLI parameters for ID, ByteWidth and BaseAddress removed for simplicity 35 | 36 | #include 37 | #include 38 | #include "intelhex.h" 39 | 40 | static const int ByteWidth = 32; 41 | static const int ID = 0x0051; 42 | 43 | static const unsigned char RESET[] = {0xf0, 0x00, 0x20, 0x29, 0x00, 0x71}; 44 | 45 | // must match unpacking code in the bootloader, obviously 46 | static void eight_to_seven(unsigned char * Output, const int OOffset, 47 | intelhex::hex_data& Input, const unsigned long IOffset) 48 | { 49 | for (int i = 0; i < 7; ++i) 50 | { 51 | if (!Input.is_set(IOffset + i)) 52 | { 53 | // pad unset addresses 54 | Input.set(IOffset + i, 0xff); 55 | } 56 | } 57 | 58 | // seven bytes of eight-bit data converted to 59 | // eight bytes of seven-bit data 60 | // render 8-bit words as 16-bit words 61 | Output[OOffset+0] = Input[IOffset+0] >> 1; 62 | Output[OOffset+1] = (Input[IOffset+0] << 6) + (Input[IOffset+1] >> 2); 63 | Output[OOffset+2] = (Input[IOffset+1] << 5) + (Input[IOffset+2] >> 3); 64 | Output[OOffset+3] = (Input[IOffset+2] << 4) + (Input[IOffset+3] >> 4); 65 | Output[OOffset+4] = (Input[IOffset+3] << 3) + (Input[IOffset+4] >> 5); 66 | Output[OOffset+5] = (Input[IOffset+4] << 2) + (Input[IOffset+5] >> 6); 67 | Output[OOffset+6] = (Input[IOffset+5] << 1) + (Input[IOffset+6] >> 7); 68 | Output[OOffset+7] = (Input[IOffset+6]); 69 | 70 | for (int i = 0; i < 8; ++i) 71 | { 72 | Output[OOffset+i] &= 0x7f; 73 | } 74 | } 75 | 76 | static void write_block(intelhex::hex_data& data, std::ofstream& ofs, const unsigned long addr, const unsigned char type) 77 | { 78 | // packet header 79 | ofs.write(reinterpret_cast(RESET), 5); 80 | ofs.put(type); 81 | 82 | int incount = 0; 83 | int outcount = 0; 84 | int outn = 1 + (ByteWidth * 8) / 7; 85 | 86 | // payload 87 | unsigned char payload[41]; 88 | 89 | while (incount < ByteWidth) 90 | { 91 | eight_to_seven(payload, outcount, data, addr+incount); 92 | 93 | incount += 7; 94 | outcount += 8; 95 | } 96 | 97 | ofs.write(reinterpret_cast(payload), outn); 98 | ofs.put(0xf7); 99 | } 100 | 101 | static void write_header(intelhex::hex_data& data, std::ofstream& ofs, size_t BaseAddress) 102 | { 103 | // human-readable version number & header block 104 | ofs.write(reinterpret_cast(RESET), 6); 105 | ofs.put(ID >> 8); 106 | ofs.put(ID & 0x7f); 107 | 108 | ofs.put(data[BaseAddress + 0x102] >> 4); 109 | ofs.put(data[BaseAddress + 0x102] & 0x0f); 110 | ofs.put(data[BaseAddress + 0x101] >> 4); 111 | ofs.put(data[BaseAddress + 0x101] & 0x0f); 112 | ofs.put(data[BaseAddress + 0x100] >> 4); 113 | ofs.put(data[BaseAddress + 0x100] & 0x0f); 114 | 115 | ofs.put(0xf7); 116 | } 117 | 118 | static void write_checksum(intelhex::hex_data& data, std::ofstream& ofs) 119 | { 120 | // device doesn't respect the checksum, but we still need this block! 121 | ofs.write(reinterpret_cast(RESET), 5); 122 | unsigned char payload[19]; 123 | 124 | const char *FIRMWARE = "Firmware"; 125 | 126 | payload[0] = 0x76; 127 | payload[1] = 0x00; 128 | for (int i = 0; i < 8; ++i) 129 | { 130 | payload[i+2] = FIRMWARE[i]; 131 | } 132 | payload[10] = 0x00; 133 | payload[11] = 0x00; 134 | payload[12] = 0x00; 135 | payload[13] = 0x00; 136 | payload[14] = 0x00; 137 | payload[15] = 0x00; 138 | payload[16] = 0x00; 139 | payload[17] = 0x00; 140 | payload[18] = 0xf7; 141 | 142 | ofs.write(reinterpret_cast(payload), 19); 143 | } 144 | 145 | int main(int argc, char *argv[]) 146 | { 147 | std::cout << "converting " << argv[1] << " to sysex file: " << argv[2] << std::endl; 148 | 149 | // read the hex file input 150 | intelhex::hex_data data; 151 | data.load(argv[1]); 152 | 153 | size_t BaseAddress = data.min_address(); 154 | size_t MaxAddress = data.max_address(); 155 | 156 | std::cout << "max addr: " << std::hex << data.max_address() << " min_addr: " << std::hex << data.min_address() << std::endl; 157 | 158 | // create output file 159 | std::ofstream ofs(argv[2] , std::ios::out | std::ios::binary); 160 | if( !ofs ) 161 | return -1; 162 | 163 | write_header(data, ofs, BaseAddress); 164 | 165 | // payload blocks... 166 | unsigned long i = BaseAddress + ByteWidth; 167 | 168 | while (i < MaxAddress) 169 | { 170 | write_block(data, ofs, i, 0x72); 171 | 172 | i += ByteWidth; 173 | } 174 | 175 | write_block(data, ofs, BaseAddress, 0x73); 176 | 177 | // footer/checksum block 178 | write_checksum(data, ofs); 179 | 180 | ofs.close(); 181 | 182 | return 0; 183 | } 184 | -------------------------------------------------------------------------------- /tools/osx/simulator-osx.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *****************************************************************************/ 32 | 33 | #include 34 | #include "app.h" 35 | 36 | #include 37 | 38 | #include 39 | 40 | ////////////////////////////////////////////////////////////////////////// 41 | static MIDIClientRef g_client = 0; 42 | 43 | static MIDIPortRef g_inDevPort = 0; 44 | static MIDIPortRef g_outDevPort = 0; 45 | 46 | static MIDIEndpointRef g_outDevEndpoint = 0; 47 | 48 | static MIDIEndpointRef g_outVirtualEndpoint = 0; 49 | static MIDIEndpointRef g_inVirtualEndpoint = 0; 50 | 51 | static const float TIMER_INTERVAL_S = 0.001; //s 52 | 53 | static const u16 g_ADC[PAD_COUNT]; 54 | 55 | static u8 g_Flash[USER_AREA_SIZE]; 56 | 57 | // ____________________________________________________________________________ 58 | // 59 | // Simulator "hal". This lets you exercise your device code without having to upload 60 | // it to the hardware, which also means you can debug it interactively. 61 | // ____________________________________________________________________________ 62 | 63 | void hal_plot_led(u8 type, u8 index, u8 red, u8 green, u8 blue) 64 | { 65 | // send this up the MIDI. Construct sysex: 66 | unsigned char data[] = {0xF0, 0x00, 0x20, 0x29, 0x02, 0x10, 0x0B, index, red, green, blue, 0xF7}; 67 | 68 | static const int MAX_OUTPUT_BUFFER_SIZE = 256; 69 | 70 | MIDIPacketList packetLst; 71 | MIDIPacket *packet = MIDIPacketListInit(&packetLst); 72 | MIDIPacketListAdd(&packetLst, MAX_OUTPUT_BUFFER_SIZE, packet, 0, sizeof(data), data); 73 | 74 | MIDISend(g_outDevPort, g_outDevEndpoint, &packetLst); 75 | } 76 | 77 | void hal_send_midi(u8 port, u8 status, u8 d1, u8 d2) 78 | { 79 | if (port == DINMIDI) 80 | { 81 | // send this up the virtual "DIN" MIDI 82 | u8 data[] = {status, d1, d2}; 83 | 84 | // MIDI needs host absolute time, NOT the proper time! 85 | 86 | uint64_t timeNow = mach_absolute_time(); 87 | 88 | MIDIPacketList packetLst; 89 | MIDIPacket *packet = MIDIPacketListInit(&packetLst); 90 | MIDIPacketListAdd(&packetLst, 3, packet, timeNow, sizeof(data), data); 91 | 92 | OSStatus err = MIDIReceived(g_outVirtualEndpoint, &packetLst); 93 | int n=0; 94 | } 95 | } 96 | 97 | void hal_send_sysex(u8 port, const u8* data, u16 length) 98 | { 99 | // as above, or just dump to console? 100 | } 101 | 102 | void hal_read_flash(u32 offset, u8 *data, u32 length) 103 | { 104 | memcpy(data, g_Flash+offset, length); 105 | } 106 | void hal_write_flash(u32 offset,const u8 *data, u32 length) 107 | { 108 | memcpy(g_Flash+offset, data, length); 109 | } 110 | 111 | ////////////////////////////////////////////////////////////////////////// 112 | static void processPacket(const unsigned char *data, int length) 113 | { 114 | // parse MIDI (very naively) 115 | while (length > 0) 116 | { 117 | unsigned char status = data[0]; 118 | 119 | // Wipe out channel (bottom four bits) 120 | status &= 0xF0; 121 | 122 | if (length >= 3) 123 | { 124 | switch (status) 125 | { 126 | case NOTEON: 127 | case NOTEOFF: 128 | app_surface_event(TYPEPAD, data[1], data[2]); 129 | data += 3; 130 | length -= 3; 131 | break; 132 | 133 | case CC: 134 | app_surface_event(TYPEPAD, data[1], data[2]); 135 | data += 3; 136 | length -= 3; 137 | break; 138 | 139 | case POLYAFTERTOUCH: 140 | app_aftertouch_event(data[1], data[2]); 141 | data += 3; 142 | length -= 3; 143 | break; 144 | 145 | default: 146 | // Don't know this message, so bail 147 | length--; 148 | data++; 149 | break; 150 | } 151 | } 152 | else 153 | { 154 | // We expected at least three bytes and didn't get them, so bail 155 | length--; 156 | data++; 157 | } 158 | } 159 | } 160 | 161 | ////////////////////////////////////////////////////////////////////////// 162 | static void processVirtualPortPacket(const unsigned char *data, int length) 163 | { 164 | // parse MIDI (very naively) 165 | while (length > 0) 166 | { 167 | unsigned char status = data[0]; 168 | 169 | // Wipe out channel (bottom four bits) 170 | status &= 0xF0; 171 | 172 | if (length >= 3) 173 | { 174 | switch (status) 175 | { 176 | case NOTEON: 177 | case NOTEOFF: 178 | case CC: 179 | app_midi_event(DINMIDI, status, data[1], data[2]); 180 | data += 3; 181 | length -= 3; 182 | break; 183 | 184 | default: 185 | // Don't know this message, so bail 186 | length--; 187 | data++; 188 | break; 189 | } 190 | } 191 | else 192 | { 193 | // We expected at least three bytes and didn't get them, so bail 194 | length--; 195 | data++; 196 | } 197 | } 198 | } 199 | 200 | static void readProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) 201 | { 202 | // farm out the packets 203 | const MIDIPacket *packet = &pktlist->packet[0]; 204 | for (int i=0; i < pktlist->numPackets; ++i) 205 | { 206 | // process the packet - in our case, receive a surface message 207 | processPacket(packet->data, packet->length); 208 | 209 | // next 210 | packet = MIDIPacketNext(packet); 211 | } 212 | } 213 | 214 | static void virtualReadProc(const MIDIPacketList *pktlist, void *readProcRefCon, void *srcConnRefCon) 215 | { 216 | // farm out the packets 217 | const MIDIPacket *packet = &pktlist->packet[0]; 218 | for (int i=0; i < pktlist->numPackets; ++i) 219 | { 220 | // process the packet - in our case, receive a surface message 221 | processVirtualPortPacket(packet->data, packet->length); 222 | 223 | // next 224 | packet = MIDIPacketNext(packet); 225 | } 226 | } 227 | 228 | static int findLaunchpadPro() 229 | { 230 | // find the input hardware port 231 | for (ItemCount i=0; i < MIDIGetNumberOfSources(); ++i) 232 | { 233 | MIDIEndpointRef endpoint = MIDIGetSource(i); 234 | if (endpoint) 235 | { 236 | // get the endpoint name (always available) 237 | CFStringRef endpointName = NULL; 238 | if (noErr != MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName)) 239 | { 240 | return -1; 241 | } 242 | 243 | if (CFStringCompare(endpointName, CFSTR("Standalone Port"), kCFCompareCaseInsensitive) == 0) 244 | { 245 | // found it! open the endpoint. 246 | if (noErr != MIDIInputPortCreate(g_client, CFSTR("In"), readProc, NULL, &g_inDevPort)) 247 | { 248 | return -1; 249 | } 250 | 251 | 252 | if (noErr != MIDIPortConnectSource(g_inDevPort, endpoint, NULL)) 253 | { 254 | return -1; 255 | } 256 | } 257 | } 258 | } 259 | 260 | // now find the output 261 | for (ItemCount i=0; i < MIDIGetNumberOfDestinations(); ++i) 262 | { 263 | MIDIEndpointRef endpoint = MIDIGetDestination(i); 264 | if (endpoint) 265 | { 266 | // get the endpoint name (always available) 267 | CFStringRef endpointName = NULL; 268 | if (noErr != MIDIObjectGetStringProperty(endpoint, kMIDIPropertyName, &endpointName)) 269 | { 270 | return -1; 271 | } 272 | 273 | if (CFStringCompare(endpointName, CFSTR("Standalone Port"), kCFCompareCaseInsensitive) == 0) 274 | { 275 | // found it! open the endpoint. 276 | if (noErr != MIDIOutputPortCreate(g_client, CFSTR("Out"), &g_outDevPort)) 277 | { 278 | return -1; 279 | } 280 | g_outDevEndpoint = endpoint; 281 | } 282 | } 283 | } 284 | 285 | return !(g_inDevPort && g_outDevPort); 286 | } 287 | 288 | static int openVirtualPorts() 289 | { 290 | // create the output port 291 | CFStringRef name = CFStringCreateWithCString(NULL, "LPP Simulator Out", kCFStringEncodingMacRoman); 292 | OSStatus result = MIDISourceCreate(g_client, name, &g_outVirtualEndpoint); 293 | CFRelease(name); 294 | 295 | if (noErr != result) 296 | { 297 | return -1; 298 | } 299 | 300 | MIDIObjectSetIntegerProperty(g_outVirtualEndpoint, kMIDIPropertyUniqueID, 12325436); 301 | 302 | // create the input port 303 | name = CFStringCreateWithCString(NULL, "LPP Simulator In", kCFStringEncodingMacRoman); 304 | result = MIDIDestinationCreate(g_client, name, virtualReadProc, NULL, &g_inVirtualEndpoint); 305 | CFRelease(name); 306 | 307 | if (noErr != result) 308 | { 309 | return -1; 310 | } 311 | MIDIObjectSetIntegerProperty(g_outVirtualEndpoint, kMIDIPropertyUniqueID, 34534563); 312 | 313 | return 0; 314 | } 315 | 316 | // ____________________________________________________________________________ 317 | 318 | static void timerCallback(CFRunLoopTimerRef timer, void * info) 319 | { 320 | app_timer_event(); 321 | CFRunLoopSourceSignal(info); 322 | } 323 | 324 | // ____________________________________________________________________________ 325 | 326 | int main(int argc, char * argv[]) 327 | { 328 | // open MIDI ports and wire them up 329 | CFStringRef strName = CFStringCreateWithCString(NULL, "Launchpad Pro Simulator", kCFStringEncodingASCII); 330 | if (noErr == MIDIClientCreate(strName, NULL, NULL, &g_client)) 331 | { 332 | CFRelease(strName); 333 | } 334 | else 335 | { 336 | // failed to open MIDI client 337 | return -1; 338 | } 339 | 340 | if (findLaunchpadPro()) 341 | { 342 | // no Launchpad Pro connected 343 | return -2; 344 | } 345 | 346 | if (openVirtualPorts()) 347 | { 348 | // no Launchpad Pro connected 349 | return -3; 350 | } 351 | 352 | // clear dummy flash & ADC 353 | memset(g_Flash, 0, USER_AREA_SIZE); 354 | memset(g_ADC, 0, sizeof(g_ADC)); 355 | 356 | // now start things up 357 | app_init(g_ADC); 358 | 359 | // start a timer loop 360 | CFRunLoopSourceContext source_context; 361 | bzero(&source_context, sizeof(source_context)); 362 | CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, & source_context); 363 | CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes); 364 | 365 | CFRunLoopTimerContext timer_context; 366 | bzero(&timer_context, sizeof(timer_context)); 367 | timer_context.info = source; 368 | CFRunLoopTimerRef timer = CFRunLoopTimerCreate(NULL, CFAbsoluteTimeGetCurrent(), TIMER_INTERVAL_S, 0, 0, timerCallback, &timer_context); 369 | CFRunLoopAddTimer(CFRunLoopGetCurrent(), timer, kCFRunLoopCommonModes); 370 | 371 | CFRunLoopRun(); 372 | 373 | return EXIT_SUCCESS; 374 | } 375 | -------------------------------------------------------------------------------- /tools/osx/simulator.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C70651A01B7CC4A20005FDD9 /* app.c in Sources */ = {isa = PBXBuildFile; fileRef = C706519F1B7CC4A20005FDD9 /* app.c */; }; 11 | C70651A81B7CC56F0005FDD9 /* simulator-osx.c in Sources */ = {isa = PBXBuildFile; fileRef = C70651A71B7CC56F0005FDD9 /* simulator-osx.c */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | C713650E1B7CC2E500AB8010 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = /usr/share/man/man1/; 19 | dstSubfolderSpec = 0; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 1; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | C706519F1B7CC4A20005FDD9 /* app.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = app.c; path = ../../src/app.c; sourceTree = ""; }; 28 | C70651A11B7CC4B20005FDD9 /* app_defs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app_defs.h; path = ../../include/app_defs.h; sourceTree = ""; }; 29 | C70651A21B7CC4B20005FDD9 /* app.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = app.h; path = ../../include/app.h; sourceTree = ""; }; 30 | C70651A71B7CC56F0005FDD9 /* simulator-osx.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = "simulator-osx.c"; sourceTree = ""; }; 31 | C71365101B7CC2E500AB8010 /* simulator */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = simulator; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | /* End PBXFileReference section */ 33 | 34 | /* Begin PBXFrameworksBuildPhase section */ 35 | C713650D1B7CC2E500AB8010 /* Frameworks */ = { 36 | isa = PBXFrameworksBuildPhase; 37 | buildActionMask = 2147483647; 38 | files = ( 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | C70651A51B7CC4D60005FDD9 /* source */ = { 46 | isa = PBXGroup; 47 | children = ( 48 | C70651A71B7CC56F0005FDD9 /* simulator-osx.c */, 49 | C706519F1B7CC4A20005FDD9 /* app.c */, 50 | ); 51 | name = source; 52 | sourceTree = ""; 53 | }; 54 | C70651A61B7CC4DF0005FDD9 /* include */ = { 55 | isa = PBXGroup; 56 | children = ( 57 | C70651A11B7CC4B20005FDD9 /* app_defs.h */, 58 | C70651A21B7CC4B20005FDD9 /* app.h */, 59 | ); 60 | name = include; 61 | sourceTree = ""; 62 | }; 63 | C71365071B7CC2E500AB8010 = { 64 | isa = PBXGroup; 65 | children = ( 66 | C70651A61B7CC4DF0005FDD9 /* include */, 67 | C70651A51B7CC4D60005FDD9 /* source */, 68 | C71365111B7CC2E500AB8010 /* Products */, 69 | ); 70 | sourceTree = ""; 71 | }; 72 | C71365111B7CC2E500AB8010 /* Products */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | C71365101B7CC2E500AB8010 /* simulator */, 76 | ); 77 | name = Products; 78 | sourceTree = ""; 79 | }; 80 | /* End PBXGroup section */ 81 | 82 | /* Begin PBXNativeTarget section */ 83 | C713650F1B7CC2E500AB8010 /* simulator */ = { 84 | isa = PBXNativeTarget; 85 | buildConfigurationList = C71365171B7CC2E500AB8010 /* Build configuration list for PBXNativeTarget "simulator" */; 86 | buildPhases = ( 87 | C713650C1B7CC2E500AB8010 /* Sources */, 88 | C713650D1B7CC2E500AB8010 /* Frameworks */, 89 | C713650E1B7CC2E500AB8010 /* CopyFiles */, 90 | ); 91 | buildRules = ( 92 | ); 93 | dependencies = ( 94 | ); 95 | name = simulator; 96 | productName = simulator; 97 | productReference = C71365101B7CC2E500AB8010 /* simulator */; 98 | productType = "com.apple.product-type.tool"; 99 | }; 100 | /* End PBXNativeTarget section */ 101 | 102 | /* Begin PBXProject section */ 103 | C71365081B7CC2E500AB8010 /* Project object */ = { 104 | isa = PBXProject; 105 | attributes = { 106 | LastUpgradeCheck = 0620; 107 | ORGANIZATIONNAME = "Focusrite Audio Engineering Ltd."; 108 | TargetAttributes = { 109 | C713650F1B7CC2E500AB8010 = { 110 | CreatedOnToolsVersion = 6.2; 111 | }; 112 | }; 113 | }; 114 | buildConfigurationList = C713650B1B7CC2E500AB8010 /* Build configuration list for PBXProject "simulator" */; 115 | compatibilityVersion = "Xcode 3.2"; 116 | developmentRegion = English; 117 | hasScannedForEncodings = 0; 118 | knownRegions = ( 119 | en, 120 | ); 121 | mainGroup = C71365071B7CC2E500AB8010; 122 | productRefGroup = C71365111B7CC2E500AB8010 /* Products */; 123 | projectDirPath = ""; 124 | projectRoot = ""; 125 | targets = ( 126 | C713650F1B7CC2E500AB8010 /* simulator */, 127 | ); 128 | }; 129 | /* End PBXProject section */ 130 | 131 | /* Begin PBXSourcesBuildPhase section */ 132 | C713650C1B7CC2E500AB8010 /* Sources */ = { 133 | isa = PBXSourcesBuildPhase; 134 | buildActionMask = 2147483647; 135 | files = ( 136 | C70651A81B7CC56F0005FDD9 /* simulator-osx.c in Sources */, 137 | C70651A01B7CC4A20005FDD9 /* app.c in Sources */, 138 | ); 139 | runOnlyForDeploymentPostprocessing = 0; 140 | }; 141 | /* End PBXSourcesBuildPhase section */ 142 | 143 | /* Begin XCBuildConfiguration section */ 144 | C71365151B7CC2E500AB8010 /* Debug */ = { 145 | isa = XCBuildConfiguration; 146 | buildSettings = { 147 | ALWAYS_SEARCH_USER_PATHS = NO; 148 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 149 | CLANG_CXX_LIBRARY = "libc++"; 150 | CLANG_ENABLE_MODULES = YES; 151 | CLANG_ENABLE_OBJC_ARC = YES; 152 | CLANG_WARN_BOOL_CONVERSION = YES; 153 | CLANG_WARN_CONSTANT_CONVERSION = YES; 154 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 155 | CLANG_WARN_EMPTY_BODY = YES; 156 | CLANG_WARN_ENUM_CONVERSION = YES; 157 | CLANG_WARN_INT_CONVERSION = YES; 158 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 159 | CLANG_WARN_UNREACHABLE_CODE = YES; 160 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 161 | COPY_PHASE_STRIP = NO; 162 | ENABLE_STRICT_OBJC_MSGSEND = YES; 163 | GCC_C_LANGUAGE_STANDARD = gnu99; 164 | GCC_DYNAMIC_NO_PIC = NO; 165 | GCC_OPTIMIZATION_LEVEL = 0; 166 | GCC_PREPROCESSOR_DEFINITIONS = ( 167 | "DEBUG=1", 168 | "$(inherited)", 169 | ); 170 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 171 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 172 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 173 | GCC_WARN_UNDECLARED_SELECTOR = YES; 174 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 175 | GCC_WARN_UNUSED_FUNCTION = YES; 176 | GCC_WARN_UNUSED_VARIABLE = YES; 177 | MACOSX_DEPLOYMENT_TARGET = 10.9; 178 | MTL_ENABLE_DEBUG_INFO = YES; 179 | ONLY_ACTIVE_ARCH = YES; 180 | SDKROOT = macosx; 181 | }; 182 | name = Debug; 183 | }; 184 | C71365161B7CC2E500AB8010 /* Release */ = { 185 | isa = XCBuildConfiguration; 186 | buildSettings = { 187 | ALWAYS_SEARCH_USER_PATHS = NO; 188 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 189 | CLANG_CXX_LIBRARY = "libc++"; 190 | CLANG_ENABLE_MODULES = YES; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_WARN_BOOL_CONVERSION = YES; 193 | CLANG_WARN_CONSTANT_CONVERSION = YES; 194 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 195 | CLANG_WARN_EMPTY_BODY = YES; 196 | CLANG_WARN_ENUM_CONVERSION = YES; 197 | CLANG_WARN_INT_CONVERSION = YES; 198 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 199 | CLANG_WARN_UNREACHABLE_CODE = YES; 200 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 201 | COPY_PHASE_STRIP = NO; 202 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 203 | ENABLE_NS_ASSERTIONS = NO; 204 | ENABLE_STRICT_OBJC_MSGSEND = YES; 205 | GCC_C_LANGUAGE_STANDARD = gnu99; 206 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 207 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 208 | GCC_WARN_UNDECLARED_SELECTOR = YES; 209 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 210 | GCC_WARN_UNUSED_FUNCTION = YES; 211 | GCC_WARN_UNUSED_VARIABLE = YES; 212 | MACOSX_DEPLOYMENT_TARGET = 10.9; 213 | MTL_ENABLE_DEBUG_INFO = NO; 214 | SDKROOT = macosx; 215 | }; 216 | name = Release; 217 | }; 218 | C71365181B7CC2E500AB8010 /* Debug */ = { 219 | isa = XCBuildConfiguration; 220 | buildSettings = { 221 | PRODUCT_NAME = "$(TARGET_NAME)"; 222 | }; 223 | name = Debug; 224 | }; 225 | C71365191B7CC2E500AB8010 /* Release */ = { 226 | isa = XCBuildConfiguration; 227 | buildSettings = { 228 | PRODUCT_NAME = "$(TARGET_NAME)"; 229 | }; 230 | name = Release; 231 | }; 232 | /* End XCBuildConfiguration section */ 233 | 234 | /* Begin XCConfigurationList section */ 235 | C713650B1B7CC2E500AB8010 /* Build configuration list for PBXProject "simulator" */ = { 236 | isa = XCConfigurationList; 237 | buildConfigurations = ( 238 | C71365151B7CC2E500AB8010 /* Debug */, 239 | C71365161B7CC2E500AB8010 /* Release */, 240 | ); 241 | defaultConfigurationIsVisible = 0; 242 | defaultConfigurationName = Release; 243 | }; 244 | C71365171B7CC2E500AB8010 /* Build configuration list for PBXNativeTarget "simulator" */ = { 245 | isa = XCConfigurationList; 246 | buildConfigurations = ( 247 | C71365181B7CC2E500AB8010 /* Debug */, 248 | C71365191B7CC2E500AB8010 /* Release */, 249 | ); 250 | defaultConfigurationIsVisible = 0; 251 | defaultConfigurationName = Release; 252 | }; 253 | /* End XCConfigurationList section */ 254 | }; 255 | rootObject = C71365081B7CC2E500AB8010 /* Project object */; 256 | } 257 | -------------------------------------------------------------------------------- /tools/simulator.c: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | 3 | Copyright (c) 2015, Focusrite Audio Engineering Ltd. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of Focusrite Audio Engineering Ltd., nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | 31 | *****************************************************************************/ 32 | 33 | #include 34 | #include "app.h" 35 | 36 | // ____________________________________________________________________________ 37 | // 38 | // Simulator "hal". This lets you exercise your device code without having to upload 39 | // it to the hardware, which also means you can debug it interactively. 40 | // ____________________________________________________________________________ 41 | 42 | void hal_plot_led(u8 type, u8 index, u8 red, u8 green, u8 blue) 43 | { 44 | // wire this up to MIDI out...? 45 | } 46 | 47 | void hal_read_led(u8 type, u8 index, u8 *red, u8 *green, u8 *blue) 48 | { 49 | } 50 | 51 | void hal_send_midi(u8 port, u8 status, u8 d1, u8 d2) 52 | { 53 | // send this up a virtual MIDI port? 54 | printf("...hal_send_midi(%d, 0x%2.2x, 0x%2.2x, 0x%2.2x);\n", port, status, d1, d2); 55 | } 56 | 57 | void hal_send_sysex(u8 port, const u8* data, u16 length) 58 | { 59 | // as above, or just dump to console? 60 | printf("...hal_send_midi(%d, (data), %d);\n", port, length); 61 | } 62 | 63 | void hal_read_flash(u32 offset, u8 *data, u32 length) 64 | { 65 | printf("...hal_read_flash(%lu, (data), %lu);\n", offset, length); 66 | } 67 | 68 | void hal_write_flash(u32 offset,const u8 *data, u32 length) 69 | { 70 | printf("...hal_write_flash(%lu, (data), %lu);\n", offset, length); 71 | } 72 | 73 | // ____________________________________________________________________________ 74 | // 75 | // App event wrappers - these just log to the console. Would be nice to wire 76 | // these up to a MIDI input from the real Launchpad Pro! 77 | // ____________________________________________________________________________ 78 | 79 | static u16 raw_ADC[64]; 80 | 81 | static void sim_app_init() 82 | { 83 | printf("calling app_init()...\n"); 84 | app_init(raw_ADC); 85 | } 86 | 87 | static void sim_app_surface_event(u8 type, u8 index, u8 value) 88 | { 89 | printf("calling sim_app_surface_event(%d, %d, %d)...\n", type, index, value); 90 | app_surface_event(type, index, value); 91 | } 92 | 93 | static void sim_app_midi_event(u8 port, u8 status, u8 d1, u8 d2) 94 | { 95 | printf("calling app_midi_event(%d, 0x%2.2x, 0x%2.2x, 0x%2.2x)...\n", port, status, d1, d2); 96 | app_midi_event(port, status, d1, d2); 97 | } 98 | 99 | static void sim_app_timer_event() 100 | { 101 | printf("calling app_timer_event()...\n"); 102 | app_timer_event(); 103 | } 104 | 105 | // ____________________________________________________________________________ 106 | 107 | int main(int argc, char * argv[]) 108 | { 109 | // let's just call a few things to give the app a very brief workout. 110 | sim_app_init(); 111 | 112 | // surface 113 | sim_app_surface_event(TYPEPAD, 35, 127); 114 | sim_app_surface_event(TYPESETUP, 0, 127); 115 | sim_app_surface_event(TYPEPAD, 35, 0); 116 | 117 | // MIDI 118 | sim_app_midi_event(USBSTANDALONE, NOTEON, 60, 127); 119 | sim_app_midi_event(USBSTANDALONE, NOTEON, 60, 0); 120 | 121 | // timer 122 | const int timerTicks = 21; 123 | printf("sending %d timer events via app_timer_event()...\n", timerTicks); 124 | for (int i=0; i < timerTicks; ++i) 125 | { 126 | sim_app_timer_event(); 127 | } 128 | return 0; 129 | } 130 | --------------------------------------------------------------------------------