summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUwe Klotz <uklotz@mixxx.org>2020-06-20 02:12:23 +0200
committerUwe Klotz <uklotz@mixxx.org>2020-06-20 02:12:23 +0200
commitc3d69aed24546dec3bdb01898a629024808c0028 (patch)
tree3e7a6fca2649cf620ed88cf2c2bc0a273b7bae3a
parenteef9cec0d8c4e096e8971ffc78ba7d350718038e (diff)
parent4e2cacceb1550c2075289826db944a9045f34933 (diff)
Merge branch '2.3' of git@github.com:mixxxdj/mixxx.git
-rw-r--r--CHANGELOG.md1
-rw-r--r--appveyor.yml90
-rw-r--r--lib/rigtorp/SPSCQueue/README.md53
-rw-r--r--lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h78
-rw-r--r--res/controllers/Numark iDJ Live II.midi.xml339
-rw-r--r--res/controllers/Numark-iDJ-Live-II-scripts.js117
6 files changed, 658 insertions, 20 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9535a03ba4..4101f5d311 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -69,6 +69,7 @@
* Add logging when script ControlObject callback is disconnected successfully [#2054](https://github.com/mixxxdj/mixxx/pull/2054)
* Add controller mapping for Roland DJ-505 [#2111](https://github.com/mixxxdj/mixxx/pull/2111)
* Update controller mapping for Allen & Heath Xone K2 to add intro/outro cues [#2236](https://github.com/mixxxdj/mixxx/pull/2236)
+* Add controller mapping for Numark iDJ Live II [#2818](https://github.com/mixxxdj/mixxx/pull/2818)
### Development ###
* Add CMake build system with Ccache support for faster compilation time [#2280](https://github.com/mixxxdj/mixxx/pull/2280)
diff --git a/appveyor.yml b/appveyor.yml
index 55ab0cf3c8..51118e62ea 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,9 +1,11 @@
---
version: '{branch}-{build}'
skip_tags: true
+shallow_clone: true
max_jobs: 1
image:
- Visual Studio 2017
+ - Ubuntu2004
- Ubuntu1804
configuration:
@@ -23,6 +25,8 @@ matrix:
# Ubuntu doesn't support "release-fastbuild".
- image: Ubuntu1804
configuration: release-fastbuild
+ - image: Ubuntu2004
+ configuration: release-fastbuild
# We only want "release-fastbuild" for Windows since "release" consumes too
# much memory due to link-time code generation / whole-program optimization.
- image: Visual Studio 2017
@@ -42,6 +46,90 @@ skip_commits:
for:
########## UBUNTU SPECIFIC CONFIGURATION ##########
+
+-
+ matrix:
+ only:
+ - image: Ubuntu2004
+
+ clone_folder: /home/appveyor/projects/mixxx
+
+ cache:
+ - /home/appveyor/.ccache
+
+ install:
+ - sudo apt update
+ - sudo apt -y install
+ ccache
+ cmake
+ libavcodec-dev
+ libavformat-dev
+ libavutil-dev
+ libchromaprint-dev
+ libflac-dev
+ libid3tag0-dev
+ liblilv-dev
+ libmad0-dev
+ libmodplug-dev
+ libmp3lame-dev
+ libopus-dev
+ libopusfile-dev
+ libportmidi-dev
+ libprotobuf-dev
+ libqt5opengl5-dev
+ libqt5sql5-sqlite
+ libqt5svg5-dev
+ libqt5x11extras5-dev
+ librubberband-dev
+ libshout3-dev
+ libsndfile1-dev
+ libsoundtouch-dev
+ libsqlite3-dev
+ libswresample-dev
+ libtag1-dev
+ libupower-glib-dev
+ libusb-1.0-0-dev
+ libwavpack-dev
+ portaudio19-dev
+ protobuf-compiler
+ qt5-default
+ qtdeclarative5-dev
+ qt5keychain-dev
+ qtscript5-dev
+ xsltproc
+
+ before_build:
+ # Limit cache size to 100 MB
+ - ccache -M 100M
+ - ccache -c
+ - ccache -s
+
+ build_script:
+ - export CMAKE_BUILD_PARALLEL_LEVEL="$(nproc)"
+ - mkdir cmake_build
+ - cd cmake_build
+ - cmake
+ -L
+ -DFFMPEG=ON
+ -DHSS1394=OFF
+ -DLOCALECOMPARE=ON
+ -DMAD=ON
+ -DMODPLUG=ON
+ -DOPUS=ON
+ -DWAVPACK=ON
+ ..
+ - cmake --build .
+ - sudo cmake --build . --target install
+
+ test_script:
+ - export CTEST_OUTPUT_ON_FAILURE=1
+ - xvfb-run -- ctest -T test --no-compress-output
+ - xvfb-run -- cmake --build . --target benchmark
+
+ after_test:
+ - xsltproc -o ctest-to-junit-results.xml ../cmake/ctest-to-junit.xsl Testing/*/Test.xml
+ - curl -F 'file=@ctest-to-junit-results.xml' "https://ci.appveyor.com/api/testresults/junit/$APPVEYOR_JOB_ID"
+
-
matrix:
only:
@@ -56,7 +144,6 @@ for:
- sudo apt-get update
- sudo apt-get -y install
ccache
- gdb
libavformat-dev
libchromaprint-dev
libfaad-dev
@@ -126,7 +213,6 @@ for:
- xsltproc -o ctest-to-junit-results.xml ../cmake/ctest-to-junit.xsl Testing/*/Test.xml
- curl -F 'file=@ctest-to-junit-results.xml' "https://ci.appveyor.com/api/testresults/junit/$APPVEYOR_JOB_ID"
-
########## END UBUNTU SPECIFIC CONFIGURATION ##########
########## WINDOWS SPECIFIC CONFIGURATION ##########
diff --git a/lib/rigtorp/SPSCQueue/README.md b/lib/rigtorp/SPSCQueue/README.md
index ba0ef3b9e1..85326e656a 100644
--- a/lib/rigtorp/SPSCQueue/README.md
+++ b/lib/rigtorp/SPSCQueue/README.md
@@ -1,6 +1,7 @@
# SPSCQueue.h
[![Build Status](https://travis-ci.org/rigtorp/SPSCQueue.svg?branch=master)](https://travis-ci.org/rigtorp/SPSCQueue)
+[![C/C++ CI](https://github.com/rigtorp/SPSCQueue/workflows/C/C++%20CI/badge.svg)](https://github.com/rigtorp/SPSCQueue/actions)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/rigtorp/SPSCQueue/master/LICENSE)
A single producer single consumer wait-free and lock-free fixed size
@@ -19,6 +20,8 @@ q.push(1);
t.join();
```
+See `src/SPSCQueueExample.cpp` for the full example.
+
## Usage
- `SPSCQueue<T>(size_t capacity);`
@@ -70,6 +73,54 @@ Only a single writer thread can perform enqueue operations and only a
single reader thread can perform dequeue operations. Any other usage
is invalid.
+## Huge page support
+
+In addition to supporting custom allocation through the [standard custom
+allocator interface](https://en.cppreference.com/w/cpp/named_req/Allocator) this
+library also supports standard proposal [P0401R3 Providing size feedback in the
+Allocator
+interface](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p0401r3.html).
+This allows convenient use of [huge
+pages](https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html)
+without wasting any allocated space. Using size feedback is only supported when
+C++17 is enabled.
+
+The library currently doesn't include a huge page allocator since the APIs for
+allocating huge pages are platform dependent and handling of huge page size and
+NUMA awareness is application specific.
+
+Below is an example huge page allocator for Linux:
+```cpp
+#include <sys/mman.h>
+
+template <typename T> struct Allocator {
+ using value_type = T;
+
+ struct AllocationResult {
+ T *ptr;
+ size_t count;
+ };
+
+ size_t roundup(size_t n) { return (((n - 1) >> 21) + 1) << 21; }
+
+ AllocationResult allocate_at_least(size_t n) {
+ size_t count = roundup(sizeof(T) * n);
+ auto p = static_cast<T *>(mmap(nullptr, count, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB,
+ -1, 0));
+ if (p == MAP_FAILED) {
+ throw std::bad_alloc();
+ }
+ return {p, count / sizeof(T)};
+ }
+
+ void deallocate(T *p, size_t n) { munmap(p, roundup(sizeof(T) * n)); }
+};
+```
+
+See `src/SPSCQueueExampleHugepages.cpp` for the full example on how to use huge
+pages on Linux.
+
## Implementation
![Memory layout](https://github.com/rigtorp/SPSCQueue/blob/master/spsc.png)
@@ -80,7 +131,7 @@ The underlying implementation is a
Care has been taken to make sure to avoid any issues with
[false sharing](https://en.wikipedia.org/wiki/False_sharing). The head
and tail pointers are aligned and padded to the false sharing range
-(currently hard coded to 128 bytes). The slots buffer is padded with
+(cache line size). The slots buffer is padded with
the false sharing range at the beginning and end.
References:
diff --git a/lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h b/lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h
index 881847d01a..b2bb56f9cf 100644
--- a/lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h
+++ b/lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2018 Erik Rigtorp <erik@rigtorp.se>
+Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -25,23 +25,56 @@ SOFTWARE.
#include <atomic>
#include <cassert>
#include <cstddef>
+#include <memory> // std::allocator
+#include <new> // std::hardware_destructive_interference_size
#include <stdexcept>
-#include <type_traits>
+#include <type_traits> // std::enable_if, std::is_*_constructible
namespace rigtorp {
-template <typename T> class SPSCQueue {
+template <typename T, typename Allocator = std::allocator<T>> class SPSCQueue {
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
+ template <typename Alloc2, typename = void>
+ struct has_allocate_at_least : std::false_type {};
+
+ template <typename Alloc2>
+ struct has_allocate_at_least<
+ Alloc2, std::void_t<typename Alloc2::value_type,
+ decltype(std::declval<Alloc2 &>().allocate_at_least(
+ size_t{}))>> : std::true_type {};
+#endif
+
public:
- explicit SPSCQueue(const size_t capacity)
- : capacity_(capacity),
- slots_(capacity_ < 2 ? nullptr
- : static_cast<T *>(operator new[](
- sizeof(T) * (capacity_ + 2 * kPadding)))),
- head_(0), tail_(0) {
- if (capacity_ < 2) {
- throw std::invalid_argument("size < 2");
+ explicit SPSCQueue(const size_t capacity,
+ const Allocator &allocator = Allocator())
+ : capacity_(capacity), allocator_(allocator), head_(0), tail_(0) {
+ // The queue needs at least one element
+ if (capacity_ < 1) {
+ capacity_ = 1;
+ }
+ capacity_++; // Needs one slack element
+ // Prevent overflowing size_t
+ if (capacity_ > SIZE_MAX - 2 * kPadding) {
+ capacity_ = SIZE_MAX - 2 * kPadding;
}
- assert(alignof(SPSCQueue<T>) >= kCacheLineSize);
+
+#if defined(__cpp_if_constexpr) && defined(__cpp_lib_void_t)
+ if constexpr (has_allocate_at_least<Allocator>::value) {
+ auto res = allocator_.allocate_at_least(capacity_ + 2 * kPadding);
+ slots_ = res.ptr;
+ capacity_ = res.count - 2 * kPadding;
+ } else {
+ slots_ = std::allocator_traits<Allocator>::allocate(
+ allocator_, capacity_ + 2 * kPadding);
+ }
+#else
+ slots_ = std::allocator_traits<Allocator>::allocate(
+ allocator_, capacity_ + 2 * kPadding);
+#endif
+
+ static_assert(alignof(SPSCQueue<T>) == kCacheLineSize, "");
+ static_assert(sizeof(SPSCQueue<T>) >= 3 * kCacheLineSize, "");
assert(reinterpret_cast<char *>(&tail_) -
reinterpret_cast<char *>(&head_) >=
static_cast<std::ptrdiff_t>(kCacheLineSize));
@@ -51,7 +84,8 @@ public:
while (front()) {
pop();
}
- operator delete[](slots_);
+ std::allocator_traits<Allocator>::deallocate(allocator_, slots_,
+ capacity_ + 2 * kPadding);
}
// non-copyable and non-movable
@@ -149,17 +183,27 @@ public:
bool empty() const noexcept { return size() == 0; }
- size_t capacity() const noexcept { return capacity_; }
+ size_t capacity() const noexcept { return capacity_ - 1; }
private:
- static constexpr size_t kCacheLineSize = 128;
+#ifdef __cpp_lib_hardware_interference_size
+ static constexpr size_t kCacheLineSize =
+ std::hardware_destructive_interference_size;
+#else
+ static constexpr size_t kCacheLineSize = 64;
+#endif
// Padding to avoid false sharing between slots_ and adjacent allocations
static constexpr size_t kPadding = (kCacheLineSize - 1) / sizeof(T) + 1;
private:
- const size_t capacity_;
- T *const slots_;
+ size_t capacity_;
+ T *slots_;
+#if defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address)
+ Allocator allocator_ [[no_unique_address]];
+#else
+ Allocator allocator_;
+#endif
// Align to avoid false sharing between head_ and tail_
alignas(kCacheLineSize) std::atomic<size_t> head_;
diff --git a/res/controllers/Numark iDJ Live II.midi.xml b/res/controllers/Numark iDJ Live II.midi.xml
new file mode 100644
index 0000000000..ee0fb02ea4
--- /dev/null
+++ b/res/controllers/Numark iDJ Live II.midi.xml
@@ -0,0 +1,339 @@
+<?xml version='1.0' encoding='utf-8'?>
+<MixxxControllerPreset mixxxVersion="2.2.3" schemaVersion="1">
+ <info>
+ <name>Numark iDJ Live II</name>
+ <author>Nathan Korth</author>
+ <description>Complete mapping for iDJ Live II</description>
+ <wiki>https://mixxx.org/wiki/doku.php/numark_idj_live_ii</wiki>
+ </info>
+ <controller id="Numark">
+ <scriptfiles>
+ <file functionprefix="Numark" filename="Numark-iDJ-Live-II-scripts.js"/>
+ </scriptfiles>
+ <controls>
+ <!-- Master -->
+ <control>
+ <group>[Master]</group>
+ <key>Numark.toggleScratchMode</key>
+ <status>0x90</status>
+ <midino>0x48</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <control>
+ <group>[Master]</group>
+ <key>gain</key>
+ <status>0xB0</status>
+ <midino>0x17</midino>
+ </control>
+ <control>
+ <group>[Master]</group>
+ <key>crossfader</key>
+ <status>0xB0</status>
+ <midino>0x0A</midino>
+ <options>
+ <invert/>
+ </options>
+ </control>
+ <!-- Library -->
+ <control>
+ <group>[Library]</group>
+ <key>MoveVertical</key>
+ <status>0xB0</status>
+ <midino>0x1A</midino>
+ <options>
+ <selectknob/>
+ </options>
+ </control>
+ <control>
+ <group>[Master]</group>
+ <key>maximize_library</key>
+ <status>0x90</status>
+ <midino>0x4F</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>LoadSelectedTrack</key>
+ <status>0x90</status>
+ <midino>0x4B</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>LoadSelectedTrack</key>
+ <status>0x90</status>
+ <midino>0x34</midino>
+ </control>
+ <!-- Deck 1 jog -->
+ <control>
+ <group>[Channel1]</group>
+ <key>Numark.jogTouch</key>
+ <status>0x90</status>
+ <midino>0x4D</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>Numark.jogTouch</key>
+ <status>0x80</status>
+ <midino>0x4D</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>Numark.jog</key>
+ <status>0xB0</status>
+ <midino>0x19</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <!-- Deck 1 knobs -->
+ <control>
+ <group>[Channel1]</group>
+ <key>volume</key>
+ <status>0xB0</status>
+ <midino>0x08</midino>
+ </control>
+ <control>
+ <group>[EqualizerRack1_[Channel1]_Effect1]</group>
+ <key>parameter1</key>
+ <status>0xB0</status>
+ <midino>0x14</midino>
+ </control>
+ <control>
+ <group>[EqualizerRack1_[Channel1]_Effect1]</group>
+ <key>parameter3</key>
+ <status>0xB0</status>
+ <midino>0x10</midino>
+ </control>
+ <!-- Deck 1 buttons -->
+ <control>
+ <group>[Channel1]</group>
+ <key>play</key>
+ <status>0x90</status>
+ <midino>0x4A</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>play</key>
+ <status>0x80</status>
+ <midino>0x4A</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>cue_goto</key>
+ <status>0x90</status>
+ <midino>0x3B</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>cue_goto</key>
+ <status>0x80</status>
+ <midino>0x3B</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>cue_set</key>
+ <status>0x90</status>
+ <midino>0x33</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>cue_set</key>
+ <status>0x80</status>
+ <midino>0x33</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>sync_enabled</key>
+ <status>0x90</status>
+ <midino>0x40</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>sync_enabled</key>
+ <status>0x80</status>
+ <midino>0x40</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>rate_temp_up</key>
+ <status>0x90</status>
+ <midino>0x43</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>rate_temp_up</key>
+ <status>0x80</status>
+ <midino>0x43</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>rate_temp_down</key>
+ <status>0x90</status>
+ <midino>0x44</midino>
+ </control>
+ <control>
+ <group>[Channel1]</group>
+ <key>rate_temp_down</key>
+ <status>0x80</status>
+ <midino>0x44</midino>
+ </control>
+ <!-- Deck 2 jog -->
+ <control>
+ <group>[Channel2]</group>
+ <key>Numark.jogTouch</key>
+ <status>0x90</status>
+ <midino>0x4E</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>Numark.jogTouch</key>
+ <status>0x80</status>
+ <midino>0x4E</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>Numark.jog</key>
+ <status>0xB0</status>
+ <midino>0x18</midino>
+ <options>
+ <script-binding/>
+ </options>
+ </control>
+ <!-- Deck 2 knobs -->
+ <control>
+ <group>[Channel2]</group>
+ <key>volume</key>
+ <status>0xB0</status>
+ <midino>0x09</midino>
+ </control>
+ <control>
+ <group>[EqualizerRack1_[Channel2]_Effect1]</group>
+ <key>parameter1</key>
+ <status>0xB0</status>
+ <midino>0x15</midino>
+ </control>
+ <control>
+ <group>[EqualizerRack1_[Channel2]_Effect1]</group>
+ <key>parameter3</key>
+ <status>0xB0</status>
+ <midino>0x11</midino>
+ </control>
+ <!-- Deck 2 buttons -->
+ <control>
+ <group>[Channel2]</group>
+ <key>play</key>
+ <status>0x90</status>
+ <midino>0x4C</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>play</key>
+ <status>0x80</status>
+ <midino>0x4C</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>cue_goto</key>
+ <status>0x90</status>
+ <midino>0x42</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>cue_goto</key>
+ <status>0x80</status>
+ <midino>0x42</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>cue_set</key>
+ <status>0x90</status>
+ <midino>0x3C</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>cue_set</key>
+ <status>0x80</status>
+ <midino>0x3C</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>sync_enabled</key>
+ <status>0x90</status>
+ <midino>0x47</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>sync_enabled</key>
+ <status>0x80</status>
+ <midino>0x47</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>rate_temp_up</key>
+ <status>0x90</status>
+ <midino>0x45</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>rate_temp_up</key>
+ <status>0x80</status>
+ <midino>0x45</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>rate_temp_down</key>
+ <status>0x90</status>
+ <midino>0x46</midino>
+ </control>
+ <control>
+ <group>[Channel2]</group>
+ <key>rate_temp_down</key>
+ <status>0x80</status>
+ <midino>0x46</midino>
+ </control>
+ </controls>
+ <outputs>
+ <output>
+ <group>[Channel1]</group>
+ <key>play_indicator</key>
+ <status>0x90</status>
+ <midino>0x4A</midino>
+ <minimum>0.5</minimum>
+ </output>
+ <output>
+ <group>[Channel2]</group>
+ <key>play_indicator</key>
+ <status>0x90</status>
+ <midino>0x4C</midino>
+ <minimum>0.5</minimum>
+ </output>
+ <output>
+ <group>[Channel1]</group>
+ <key>sync_enabled</key>
+ <status>0x90</status>
+ <midino>0x40</midino>
+ <minimum>0.5</minimum>
+ </output>
+ <output>
+ <group>[Channel2]</group>
+ <key>sync_enabled</key>
+ <status>0x90</status>
+ <midino>0x47</midino>
+ <minimum>0.5</minimum>
+ </output>
+ </outputs>
+ </controller>
+</MixxxControllerPreset>
diff --git a/res/controllers/Numark-iDJ-Live-II-scripts.js b/res/controllers/Numark-iDJ-Live-II-scripts.js
new file mode 100644
index 0000000000..b09c1d28a7
--- /dev/null
+++ b/res/controllers/Numark-iDJ-Live-II-scripts.js
@@ -0,0 +1,117 @@
+var Numark = {};
+
+// yanked from the Virtual DJ config on Numark's website
+Numark.sysex = [0xF0, 0x00, 0x01, 0x3F, 0x7F, 0x2E, 0x60, 0x00, 0x01, 0x01, 0xF7];
+
+Numark.jogResolution = 640;
+
+Numark.buttons = {
+ "[Channel1]": {
+ "play": 0x4A,
+ "sync": 0x40,
+ },
+ "[Channel2]": {
+ "play": 0x4C,
+ "sync": 0x47,
+ },
+ "scratch": 0x48,
+};
+
+Numark.ledOn = function(control) {
+ midi.sendShortMsg(0x90, control, 0x7f);
+};
+Numark.ledOff = function(control) {
+ midi.sendShortMsg(0x90, control, 0x00);
+};
+
+Numark.init = function() {
+ this.touching = {
+ "[Channel1]": false,
+ "[Channel2]": false,
+ };
+ // tell controller to send current state of knobs and crossfader
+ midi.sendSysexMsg(this.sysex, this.sysex.length);
+ // change this line to toggle the default scratch mode:
+ this.scratchMode = true;
+ // initialize LEDs
+ if (this.scratchMode) {
+ this.ledOn(this.buttons.scratch);
+ } else {
+ this.ledOff(this.buttons.scratch);
+ }
+ if (engine.getParameter("[Channel1]", "play")) {
+ this.ledOn(this.buttons["[Channel1]"].play);
+ } else {
+ this.ledOff(this.buttons["[Channel1]"].play);
+ }
+ if (engine.getParameter("[Channel2]", "play")) {
+ this.ledOn(this.buttons["[Channel2]"].play);
+ } else {
+ this.ledOff(this.buttons["[Channel2]"].play);
+ }
+};
+
+Numark.shutdown = function() {
+ this.ledOff(this.buttons["[Channel1]"].play);
+ this.ledOff(this.buttons["[Channel1]"].sync);
+ this.ledOff(this.buttons["[Channel2]"].play);
+ this.ledOff(this.buttons["[Channel2]"].sync);
+ this.ledOff(this.buttons.scratch);
+};
+
+Numark.toggleScratchMode = function() {
+ if (this.scratchMode) {
+ this.scratchMode = false;
+ this.ledOff(this.buttons.scratch);
+ // cancel any active scratches to prevent weird behavior
+ engine.scratchDisable(1);
+ engine.scratchDisable(2);
+ } else {
+ this.scratchMode = true;
+ this.ledOn(this.buttons.scratch);
+ }
+};
+
+Numark.jogTouch = function(_channel, _control, value, _status, group) {
+ var deckN = script.deckFromGroup(group);
+ if (value >= 64) {
+ this.touching[group] = true;
+ if (this.scratchMode) {
+ var alpha = 1.0/8;
+ var beta = alpha/32;
+ engine.scratchEnable(deckN, this.jogResolution, 33+(1.0/3), alpha, beta);
+ }
+ } else {
+ this.touching[group] = false;
+ engine.scratchDisable(deckN);
+ }
+};
+
+Numark.jog = function(_channel, _control, value, _status, group) {
+ if (!this.touching[group]) {
+ return;
+ }
+ // value is centered around 0
+ if (value >= 64) {
+ value -= 128;
+ }
+ var deckN = script.deckFromGroup(group);
+ if (this.scratchMode) {
+ if (engine.isScratching(deckN)) {
+ engine.scratchTick(deckN, value);
+ }
+ } else {
+ if (engine.getParameter(group, "play")) {
+ // pitch bend while playing
+ engine.setValue(group, "jog", value);
+ } else {
+ // search while paused
+ var position = engine.getValue(group, "playposition");
+ position += value * 0.0002;
+ if (position < 0) {
+ position = 0;
+ }
+ engine.setValue(group, "playposition", position);
+ }
+ }
+};