diff options
author | Uwe Klotz <uklotz@mixxx.org> | 2020-06-20 02:12:23 +0200 |
---|---|---|
committer | Uwe Klotz <uklotz@mixxx.org> | 2020-06-20 02:12:23 +0200 |
commit | c3d69aed24546dec3bdb01898a629024808c0028 (patch) | |
tree | 3e7a6fca2649cf620ed88cf2c2bc0a273b7bae3a | |
parent | eef9cec0d8c4e096e8971ffc78ba7d350718038e (diff) | |
parent | 4e2cacceb1550c2075289826db944a9045f34933 (diff) |
Merge branch '2.3' of git@github.com:mixxxdj/mixxx.git
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | appveyor.yml | 90 | ||||
-rw-r--r-- | lib/rigtorp/SPSCQueue/README.md | 53 | ||||
-rw-r--r-- | lib/rigtorp/SPSCQueue/include/rigtorp/SPSCQueue.h | 78 | ||||
-rw-r--r-- | res/controllers/Numark iDJ Live II.midi.xml | 339 | ||||
-rw-r--r-- | res/controllers/Numark-iDJ-Live-II-scripts.js | 117 |
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); + } + } +}; |