Compare commits
16 Commits
22e4fa770e
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a3e6f6415f | |||
| 60c8255c2d | |||
| 6f66a5db64 | |||
| d5dfb290c1 | |||
| 3425ec69de | |||
| f7ac34141a | |||
| 2173e366ea | |||
| 7d4f8a4cd2 | |||
| cd5ebd84d6 | |||
| 7ecc718f5d | |||
| e58f06d9fa | |||
| 583ed77a67 | |||
| 0c7b6b4442 | |||
| d6201c59b4 | |||
| 7a74cfa75e | |||
| 4db7165797 |
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(git add *)",
|
||||
"Bash(python3 -c \"import ast; ast.parse\\(open\\('main.py'\\).read\\(\\)\\)\")",
|
||||
"WebFetch(domain:raw.githubusercontent.com)",
|
||||
"WebSearch",
|
||||
"Bash(find /private/tmp/claude-502/-Users-p4piwabl0-Desktop-projects-virtual-controller-clean/29ede664-3f90-4b9a-a462-57bbd9febfef/scratchpad/helgobox -maxdepth 1 -iname target -o -maxdepth 1 -iname *.gitmodules)",
|
||||
"Bash(SRC=/private/tmp/claude-502/-Users-p4piwabl0-Desktop-projects-virtual-controller-clean/29ede664-3f90-4b9a-a462-57bbd9febfef/scratchpad/helgobox *)",
|
||||
"Bash(find /Users/p4piwabl0/Desktop/projects/virtual-controller-clean/plugin-reaper-realearn -iname .env -not -iname *.example)",
|
||||
"Bash(git commit -m ' *)",
|
||||
"Bash(git push *)",
|
||||
"Bash(git mv *)",
|
||||
"Bash(awk '{print $NF}')",
|
||||
"Bash(python3 -c ' *)",
|
||||
"Bash(timeout 6 python3 run.py)",
|
||||
"Bash(echo \"EXIT: $?\")",
|
||||
"Bash(cargo --version)",
|
||||
"Bash(xcode-select -p)",
|
||||
"Bash(brew --version)",
|
||||
"Bash(rustc --version)",
|
||||
"Read(//Users/p4piwabl0/Library/Application Support/REAPER/UserPlugins/**)",
|
||||
"Bash(/Users/p4piwabl0/.cargo/bin/rustup show *)",
|
||||
"Bash(/Users/p4piwabl0/.cargo/bin/rustup toolchain *)",
|
||||
"Bash(curl -sL https://raw.githubusercontent.com/justinfrankel/reaper-sdk/main/sdk/reaper_plugin.h -o /private/tmp/claude-502/-Users-p4piwabl0-Desktop-projects-virtual-controller-clean/96f4c045-8fa6-4b1e-add2-8903d8ee2feb/scratchpad/reaper_plugin.h --write-out \"HTTP:%{http_code} SIZE:%{size_download}\\\\n\")",
|
||||
"Bash(curl -sL https://raw.githubusercontent.com/justinfrankel/reaper-sdk/main/sdk/reaper_plugin_functions.h -o /private/tmp/claude-502/-Users-p4piwabl0-Desktop-projects-virtual-controller-clean/96f4c045-8fa6-4b1e-add2-8903d8ee2feb/scratchpad/reaper_plugin_functions.h --write-out \"HTTP:%{http_code} SIZE:%{size_download}\\\\n\")",
|
||||
"Bash(grep -i \"^swell.h\\\\|swell\\\\.h$\")",
|
||||
"Bash(set -e)",
|
||||
"Bash(mkdir -p extension-reaper-macos/vendor/reaper-sdk/sdk)",
|
||||
"Bash(mkdir -p extension-reaper-macos/vendor/reaper-sdk/WDL)",
|
||||
"Bash(mkdir -p extension-reaper-macos/src)",
|
||||
"Bash(cp __TRACKED_VAR__/reaper-sdk/sdk/reaper_plugin.h __TRACKED_VAR__/reaper-sdk/sdk/reaper_plugin_functions.h __TRACKED_VAR__/reaper-sdk/sdk/reaper_plugin_fx_embed.h __TRACKED_VAR__/reaper-sdk/sdk/LICENSE extension-reaper-macos/vendor/reaper-sdk/sdk/)",
|
||||
"Bash(chmod +x build.sh)",
|
||||
"Bash(./build.sh)",
|
||||
"Bash(nm -gU reaper_extension-reaper-macos.dylib)",
|
||||
"Bash(ls \"$HOME/Library/Application Support/REAPER/UserPlugins\" | grep -i reaper_ | head -5)",
|
||||
"Bash(ls -la \"$HOME/Library/Application Support/REAPER/UserPlugins\")",
|
||||
"Bash(cp reaper_extension-reaper-macos.dylib '/Users/p4piwabl0/Library/Application Support/REAPER/UserPlugins/')",
|
||||
"Bash(tar --exclude='*.dylib' -czf ~/Desktop/extension-reaper-macos-skeleton-backup-2026-07-15.tar.gz extension-reaper-macos)",
|
||||
"Read(//Users/p4piwabl0/Desktop/**)",
|
||||
"Bash(awk '/int REAPERAPI_LoadAPI/,0' /Users/p4piwabl0/Desktop/projects/virtual-controller-clean/extension-reaper-macos/vendor/reaper-sdk/sdk/reaper_plugin_functions.h)",
|
||||
"Bash(grep -n \"{NULL, NULL}\")",
|
||||
"Bash(ls -la \"/Applications/REAPER.app/Contents/MacOS/\" 2>&1 | head -5 *)",
|
||||
"Read(//Applications/REAPER.app/Contents/MacOS/**)",
|
||||
"Bash(src/.venv/Scripts/python.exe -m PyInstaller --name VirtualController --onedir --windowed --distpath dist --workpath build --specpath . --add-data \"src/app-presets/presets.json;app-presets\" src/main.py)"
|
||||
]
|
||||
}
|
||||
}
|
||||
+14
@@ -1,7 +1,21 @@
|
||||
venv/
|
||||
.venv/
|
||||
|
||||
__pycache__/
|
||||
|
||||
*.pyc
|
||||
|
||||
.DS_Store
|
||||
|
||||
# PyInstaller output (app-desktop)
|
||||
app-desktop/build/
|
||||
app-desktop/dist/
|
||||
|
||||
# Xcode
|
||||
*.xcuserstate
|
||||
xcuserdata/
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData/
|
||||
*.hmap
|
||||
*.ipa
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# Virtual Controller — Project Context
|
||||
|
||||
@CLAUDE_MEMORY.md
|
||||
|
||||
## What this app is
|
||||
|
||||
A PyQt6 desktop application that acts as a MIDI/OSC routing hub and control surface. It sits between hardware MIDI controllers (AKAI, JL Cooper motorized fader bank, iPad via Network MIDI) and a DAW (REAPER). It translates incoming CC numbers to different output CC numbers and channels, applies momentary/toggle logic, and manages named presets per instrument/VSTi.
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
# Carried-over session memory
|
||||
|
||||
This file is a portable dump of Claude's per-machine auto-memory for this
|
||||
project (normally stored outside the repo, keyed to the absolute path of the
|
||||
working directory — so it doesn't survive a clone to a new machine on its
|
||||
own). This copy is checked into the repo so it travels with `git clone`/`git
|
||||
pull` instead of staying stuck on one machine.
|
||||
|
||||
Auto-generated 2026-07-22. May drift from live memory over time — treat as a
|
||||
snapshot, not a live sync.
|
||||
|
||||
---
|
||||
|
||||
## How Paul wants to collaborate (feedback)
|
||||
|
||||
### Confirm before changes
|
||||
Always talk through the diagnosis/plan and get explicit confirmation before
|
||||
making code changes — do not implement on the first pass, even for what
|
||||
looks like a clear-cut bug.
|
||||
|
||||
- Stated explicitly (2026-07-06): "from here on out, always talk to me
|
||||
before we make changes." A standing rule, not one-off.
|
||||
- Stating a plan and executing it *in the same turn* does not count as
|
||||
confirming — waiting for an actual reply is required, especially for
|
||||
open/free-choice sub-decisions (a button label, a variable name, which of
|
||||
several equally-valid options to pick) that come up mid-task.
|
||||
- If Paul says something like "just do it" for a specific request, that
|
||||
authorizes that one instance, not a standing reversal of the rule.
|
||||
|
||||
### Bullet-point replies
|
||||
Explain diagnoses/what's-happening in short, clear bullet points — not
|
||||
paragraph prose.
|
||||
|
||||
- Said explicitly during iOS FocusToggleWidget color debugging: "I'm going
|
||||
to need you to start simplifying your discussions... respond to me in
|
||||
clear bullet points about what's actually happening."
|
||||
- Recurrence: bullets with bold headers still drifted into multi-sentence
|
||||
paragraphs underneath. The failure mode isn't lack of bullets, it's
|
||||
bullets containing paragraph-length reasoning. One short sentence per
|
||||
bullet, no exceptions — split into more, shorter bullets instead of
|
||||
letting one run long.
|
||||
- Second recurrence: when Paul states the correct technical answer himself,
|
||||
respond with a short confirmation ("yes, correct" + at most one
|
||||
clarifying detail) — don't re-explain it back to him in different words.
|
||||
Treat his own correct restatement as a signal the concept has landed.
|
||||
|
||||
### ReaLearn is reference-only
|
||||
`plugin-reaper-relearn` (vendored helgoboss/helgobox ReaLearn) is for
|
||||
reading/insight only — never edit, patch, or extend it directly.
|
||||
|
||||
- Paul: "the realearn stuff is its own project and we dont mod it at all...
|
||||
if we need to read from it to gain some insight ok other than that no."
|
||||
- All new proprietary REAPER integration work happens in
|
||||
`extension-reaper-macos` instead.
|
||||
- Useful reference patterns inside ReaLearn: `main/src/domain/targets/*.rs`
|
||||
target-plugin pattern, `mouse_target.rs` OS-level mouse emulation via
|
||||
`enigo`, `reaper-high`/`reaper-medium` (reaper-rs) usage — studied, not
|
||||
depended on.
|
||||
|
||||
---
|
||||
|
||||
## Ongoing project context
|
||||
|
||||
### extension-reaper-macos / envelope automation pipeline
|
||||
Paul is building `extension-reaper-macos` as his own proprietary, owned
|
||||
compiled REAPER extension — not scoped to one feature.
|
||||
|
||||
- Language: C/C++ against the real Cockos SDK directly (Rust/reaper-rs was
|
||||
floated and explicitly dropped: "REAPER's actual native SDK... no wrapper
|
||||
layer, no crate dependency graph").
|
||||
- Layout: `vendor/reaper-sdk/sdk/` (unmodified headers from
|
||||
justinfrankel/reaper-sdk), `vendor/reaper-sdk/WDL/` (full justinfrankel/WDL
|
||||
checkout, header types only — nothing compiled/linked from it),
|
||||
`src/main.cpp` (entrypoint, owns `REAPERAPI_IMPLEMENT`), one
|
||||
subfolder per module (`hello/`, `tracking/`, `socket/`).
|
||||
- Confirmed working 2026-07-15: bare-minimum skeleton built, installed,
|
||||
loads in REAPER, prints console confirmation.
|
||||
- Since then: extended to read/write automation items (`D_BASELINE` nudge,
|
||||
`tracking.cpp`), and a UDP socket listener (`socket.cpp`) wired to the
|
||||
desktop app so fader moves nudge the selected automation item's baseline
|
||||
live.
|
||||
|
||||
**Origin feature:** click an automation item on a REAPER envelope lane to
|
||||
select it, then move a fader in the desktop app (eventually the tablet
|
||||
remote) to raise/lower ALL envelope points in that item together — maps to
|
||||
REAPER's own `D_BASELINE` property (same as ctrl-drag natively), not
|
||||
per-point editing.
|
||||
|
||||
**Design decisions:**
|
||||
- Mouse-emulation (replaying a physical Y-axis drag) was explicitly
|
||||
rejected in favor of calling REAPER's API directly.
|
||||
- This extension owns its own socket listener rather than going through
|
||||
REAPER's OSC-control-surface config or ReaScript/Action-List-learn.
|
||||
- Open question, unresolved: does this extension eventually *replace* the
|
||||
existing OSC in/out to REAPER (track/preset name, transport, position via
|
||||
`osc_sender.py`/`osc_receiver.py`), or live alongside it for things OSC
|
||||
can't reach?
|
||||
- Also undecided: relative (nudge) vs absolute fader mapping for baseline
|
||||
value.
|
||||
|
||||
**Cross-platform build (this is the live thread — Windows port in
|
||||
progress):**
|
||||
- macOS: `.dylib`, `clang++ -dynamiclib -arch arm64` via `build.sh`. Needs a
|
||||
real Mac — can't be reliably/legally cross-compiled from another host.
|
||||
Output now goes to `build/macos/`, copied to
|
||||
`~/Library/Application Support/REAPER/UserPlugins/`.
|
||||
- Windows: needs a `.dll`. `main.cpp`/`tracking.cpp` are portable as-is (pure
|
||||
REAPER SDK C++, and `REAPER_PLUGIN_DLL_EXPORT` is already `_WIN32`-aware in
|
||||
the vendored SDK header). `socket.cpp` is the one file that needs a real
|
||||
port — it uses raw POSIX sockets
|
||||
(`sys/socket.h`/`netinet/in.h`/`unistd.h`/`fcntl.h`,
|
||||
`recvfrom`/`fcntl`/`close`) which don't exist on Windows; needs a Winsock2
|
||||
path (`WSAStartup`/`WSACleanup`, `closesocket`, `ioctlsocket` instead of
|
||||
`fcntl`, link `ws2_32`). Also need a Windows-side build script (MSVC
|
||||
`cl.exe /LD` or MinGW `g++ -shared`) producing a `.dll`, copied to
|
||||
`%APPDATA%\REAPER\UserPlugins\`.
|
||||
- Dev environment: Paul is setting up a Windows ARM64 VM (Parallels, Apple
|
||||
Silicon host) rather than full x86 emulation (tried QEMU/UTM full x86
|
||||
emulation first — extremely slow, no hardware acceleration for a foreign
|
||||
instruction set). ARM64 Windows runs natively/fast on Apple Silicon;
|
||||
x64 compiler + x64 REAPER run inside it via Windows' own built-in
|
||||
x64-on-ARM app emulation, which is fast enough for real dev work. REAPER
|
||||
also ships a native ARM64 Windows build if x64 ends up unnecessary.
|
||||
- A self-hosted Gitea instance (separate box) was floated for CI: Linux
|
||||
`.so` natively there, Windows `.dll` via `x86_64-w64-mingw32-g++`
|
||||
cross-compile from that same Linux box — viable while the extension has
|
||||
no REAPER C++ virtual-class interfaces crossing the plugin boundary
|
||||
(re-check if things like `PCM_source`/`ProjectStateContext` get used,
|
||||
where the SDK's MSVC-ABI requirement actually bites).
|
||||
|
||||
### Hardware CC binding backlog
|
||||
Backlog for the hw_cc/hw_channel binding system in `main.py`,
|
||||
`faderwidget.py`, `focusfaderwidget.py`, `focustogglewidget.py`:
|
||||
|
||||
1. **Assignment collision detection** — no validation today for two widgets
|
||||
(faders/toggles/transports/preset_browsers/focus_faders/focus_toggles)
|
||||
being bound to the same hw_cc + hw_channel. Not yet designed — open
|
||||
question is where the check should live (Learn-time vs. preset-wide
|
||||
audit) and what UI signals it.
|
||||
|
||||
Raised 2026-07-06 after building JL Cooper touch-sense support (CH15/CH16
|
||||
same-CC convention) and Focus widget Hardware/Feedback checkboxes — those
|
||||
made binding conflicts more likely without adding detection.
|
||||
|
||||
### Planned features backlog (2026-06-29, ordered)
|
||||
1. Momentary on Toggle Widget — Mom/Tog mode on ToggleWidget, mirrors
|
||||
TransportWidget
|
||||
2. Add Rows — add new fader/toggle rows to layout at runtime
|
||||
3. Duplicate (Create New Gen ID) — duplicate a widget/row with a fresh
|
||||
generated ID
|
||||
4. Big Title — large display title element
|
||||
5. Enable/Disable Preset Browser Row
|
||||
6. Re-order Rows — drag or button-based row reordering
|
||||
|
||||
---
|
||||
|
||||
## iOS remote client (companion project, same session lineage)
|
||||
|
||||
These entries came out of sessions rooted in this project directory but
|
||||
concern `remote-client-ios` / `virtual-controller-ios-app`. Kept here since
|
||||
that's where they were captured.
|
||||
|
||||
### Tablet direction (confirmed)
|
||||
The WebSocket + vanilla-JS web surface is the **permanent** direction for
|
||||
performance control — not TouchOSC, not a native app. POC validated the
|
||||
workflow: one-time arrange per preset, auto-switches with preset, instant JS
|
||||
load, works on any device browser.
|
||||
|
||||
Stack: PyQt6 app owns all routing/presets/MIDI/OSC; `QWebSocketServer` on
|
||||
:8765; HTTP server on :8080 serving `/client`; vanilla JS client, no
|
||||
frameworks; layout saved per preset UUID in `presets.json` under
|
||||
`"tablet_layout"`. Native app (Android/iOS) explicitly off the table unless
|
||||
revisited.
|
||||
|
||||
### iOS FaderWidget architecture (settled, do not revisit)
|
||||
Rebuilt from scratch using raw `touchesBegan`/`touchesMoved` overrides
|
||||
instead of UISlider or UIScrollView — UISlider needs precise thumb
|
||||
targeting, UIScrollView's pan gesture recognizer fought a drag gesture
|
||||
recognizer (`touchesCancelled` froze the fader after one increment). Raw
|
||||
touch tracking fires at 120hz with zero latency and works anywhere on the
|
||||
widget surface.
|
||||
|
||||
Key decisions:
|
||||
- Value mapping: `value = (1 - pt.y / activeHeight) * 127`, normalized
|
||||
against `bounds.height`.
|
||||
- Dead zones: 90pt top/bottom (`padHeight = 90`).
|
||||
- REL mode (default): drag relative to touch start, no value jump. ABS
|
||||
mode: touch position maps directly to value.
|
||||
- Gradient fill: `CAGradientLayer`, transparent black (bottom) to color at
|
||||
45% alpha (top), grows upward with value.
|
||||
- Haptics: `UIImpactFeedbackGenerator(.medium)` on hitting 0 or 127
|
||||
(edge-detected via `lastHapticValue`).
|
||||
- Dedup via `lastSentValue`.
|
||||
- Gesture fix: `addDragGesture` sets `cancelsTouchesInView = false` so pan
|
||||
doesn't steal touches; `arrangeMode` flag guards touch handlers during
|
||||
drag; pinch-to-resize removed for faders.
|
||||
|
||||
### iOS remote backlog
|
||||
**Next up:** review pass on the `dest_id` system — Paul flagged things to
|
||||
go over, "kinda works, things to go over."
|
||||
|
||||
Shipped: `remote_checkbox`/`dest_spin` per widget (own `grp_remote` frame),
|
||||
Bonjour discovery (desktop advertises `_vcremote._tcp.local.` via
|
||||
`zeroconf`, iOS browses via `NetServiceBrowser`), Feedback widget (Channel
|
||||
Focus-scoped, not the old global one), `ToggleWidget` off-state now uses
|
||||
`color.darkened(by: 0.6)` instead of a hardcoded gray, pinch-to-resize +
|
||||
grid-snap in arrange mode.
|
||||
|
||||
Remaining:
|
||||
1. **Bonjour discovery bug** — desktop-side confirmed correct (registers,
|
||||
resolves, correct IP, Local Network permission on, same Wi-Fi), but never
|
||||
resolves from a *borrowed* test iPad — possibly MDM/managed-device or
|
||||
router client isolation. **Retest on Paul's own iPad before assuming the
|
||||
code is broken.**
|
||||
2. Review pass on dest_id system (see "Next up" above).
|
||||
3. All-edge snap — `snapFrame` written but unused; call sites still use
|
||||
`snapValue` on origin only.
|
||||
4. Preset save bug — first/last presets not persisting.
|
||||
5. Model all iOS JSON transactions (outer envelope still raw
|
||||
`JSONSerialization`).
|
||||
6. Model all Python outgoing data — dataclasses in `ws_server.py`
|
||||
broadcasts.
|
||||
7. Clean up `layout` arg — `broadcast_preset` takes it but doesn't send it.
|
||||
8. Initial sync/ack handshake on connect.
|
||||
9. Disallow presets without UUID (enforce server-side before broadcast).
|
||||
10. Desktop activity ring — flash on desktop widget when it receives a
|
||||
value from iPad.
|
||||
11. **No live color-sync broadcast** (deprioritized) — color only travels
|
||||
inside a full `"preset"` snapshot, never standalone like
|
||||
label/value/feedback. Regressed at some point per Paul; not worth
|
||||
chasing until other things are done.
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['src\\main.py'],
|
||||
pathex=[],
|
||||
binaries=[],
|
||||
datas=[('src/app-presets/presets.json', 'app-presets')],
|
||||
hiddenimports=[],
|
||||
hookspath=[],
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
noarchive=False,
|
||||
optimize=0,
|
||||
)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
[],
|
||||
exclude_binaries=True,
|
||||
name='VirtualController',
|
||||
debug=False,
|
||||
bootloader_ignore_signals=False,
|
||||
strip=False,
|
||||
upx=True,
|
||||
console=False,
|
||||
disable_windowed_traceback=False,
|
||||
argv_emulation=False,
|
||||
target_arch=None,
|
||||
codesign_identity=None,
|
||||
entitlements_file=None,
|
||||
)
|
||||
coll = COLLECT(
|
||||
exe,
|
||||
a.binaries,
|
||||
a.datas,
|
||||
strip=False,
|
||||
upx=True,
|
||||
upx_exclude=[],
|
||||
name='VirtualController',
|
||||
)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,47 @@
|
||||
import socket
|
||||
|
||||
_ext_socket = None
|
||||
_ext_ip = "127.0.0.1"
|
||||
_ext_port = 9124
|
||||
_ext_enabled = False
|
||||
|
||||
|
||||
def get_extension_socket():
|
||||
global _ext_socket
|
||||
|
||||
if _ext_socket is None:
|
||||
_ext_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
|
||||
return _ext_socket
|
||||
|
||||
|
||||
def set_extension_target(ip=None, port=None):
|
||||
global _ext_socket, _ext_ip, _ext_port
|
||||
|
||||
if ip is not None:
|
||||
_ext_ip = ip
|
||||
|
||||
if port is not None:
|
||||
_ext_port = port
|
||||
|
||||
_ext_socket = None
|
||||
|
||||
|
||||
def set_extension_enabled(enabled):
|
||||
global _ext_enabled
|
||||
_ext_enabled = enabled
|
||||
|
||||
|
||||
def is_extension_enabled():
|
||||
return _ext_enabled
|
||||
|
||||
|
||||
def send_to_extension(value):
|
||||
"""Sends a plain numeric delta to extension-reaper-macos over UDP.
|
||||
No-op (kill switch) unless set_extension_enabled(True) has been called."""
|
||||
if not _ext_enabled:
|
||||
return
|
||||
try:
|
||||
get_extension_socket().sendto(str(value).encode(), (_ext_ip, _ext_port))
|
||||
except Exception as e:
|
||||
print(f"[Extension send] Error: {e}")
|
||||
@@ -3,7 +3,7 @@ import uuid
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QSlider,
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy,
|
||||
QPushButton, QCheckBox
|
||||
QPushButton, QCheckBox, QComboBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
@@ -55,6 +55,15 @@ class FaderWidget(QWidget):
|
||||
self.hw_channel = None
|
||||
self.is_learning = False
|
||||
|
||||
# Touch sense: some motorized fader hardware (e.g. JL Cooper) sends
|
||||
# touch on/off using the SAME CC as the fader's value, just on
|
||||
# hw_channel - 1 (e.g. value on CH16, touch on CH15). "Off" (default)
|
||||
# ignores this distinction entirely, matching prior behavior.
|
||||
# is_touching is tracked either way but only acted on by hardware with
|
||||
# a real feedback path to gate (see FocusFaderWidget.receive_osc_value).
|
||||
self.touch_sense_mode = "off"
|
||||
self.is_touching = False
|
||||
|
||||
_grp_style = "QFrame { border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; }"
|
||||
|
||||
# Group 1: Learn + input readout (border always visible; learn_btn hideable)
|
||||
@@ -77,6 +86,12 @@ class FaderWidget(QWidget):
|
||||
self.cc_input_lbl.setFixedHeight(16)
|
||||
_grp1_layout.addWidget(self.cc_input_lbl)
|
||||
|
||||
self.touch_sense_combo = QComboBox()
|
||||
self.touch_sense_combo.addItems(["No Profile", "MIDI JL Cooper CC Mode"])
|
||||
self.touch_sense_combo.setStyleSheet("font-size: 10px;")
|
||||
self.touch_sense_combo.currentIndexChanged.connect(self._on_touch_sense_changed)
|
||||
_grp1_layout.addWidget(self.touch_sense_combo)
|
||||
|
||||
inner.addWidget(self.grp_learn)
|
||||
|
||||
_arrow_lbl = QLabel("▼")
|
||||
@@ -130,7 +145,25 @@ class FaderWidget(QWidget):
|
||||
|
||||
inner.addWidget(self.grp_cc)
|
||||
|
||||
# Group 3: Remote routing
|
||||
self.grp_remote = QFrame()
|
||||
self.grp_remote.setStyleSheet(_grp_style)
|
||||
_grp_remote_layout = QVBoxLayout(self.grp_remote)
|
||||
_grp_remote_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_remote_layout.setSpacing(3)
|
||||
|
||||
self.remote_checkbox = QCheckBox("App")
|
||||
self.remote_checkbox.setChecked(True)
|
||||
self.remote_checkbox.stateChanged.connect(lambda _: self.dest_spin.setEnabled(self.remote_checkbox.isChecked()))
|
||||
_grp_remote_layout.addWidget(self.remote_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.dest_spin = QSpinBox()
|
||||
self.dest_spin.setRange(1, 9)
|
||||
self.dest_spin.setValue(1)
|
||||
self.dest_spin.setFixedWidth(52)
|
||||
_grp_remote_layout.addWidget(self.dest_spin, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
inner.addWidget(self.grp_remote)
|
||||
|
||||
# Fader
|
||||
self.fader = QSlider(Qt.Orientation.Vertical)
|
||||
@@ -423,6 +456,10 @@ class FaderWidget(QWidget):
|
||||
self.hw_channel = channel
|
||||
self.stop_learn()
|
||||
|
||||
def _on_touch_sense_changed(self, index):
|
||||
self.touch_sense_mode = "jl_cooper" if index == 1 else "off"
|
||||
self.is_touching = False
|
||||
|
||||
def get_state(self):
|
||||
return {
|
||||
"uid": self.uid,
|
||||
@@ -435,9 +472,12 @@ class FaderWidget(QWidget):
|
||||
"last_sent": self.last_sent,
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"touch_sense_mode": self.touch_sense_mode,
|
||||
"zone": self.zone,
|
||||
"center_index": self.center_index,
|
||||
"zone_index": self.zone_index
|
||||
"zone_index": self.zone_index,
|
||||
"remote": self.remote_checkbox.isChecked(),
|
||||
"dest_id": self.dest_spin.value(),
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
@@ -464,6 +504,22 @@ class FaderWidget(QWidget):
|
||||
self.zone_btn.set_state(self.zone == "left", self.zone == "right")
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
# jl_cooper_off/jl_cooper_on were a short-lived three-state naming;
|
||||
# both collapse to the single jl_cooper mode.
|
||||
saved_touch_mode = state.get("touch_sense_mode", "off")
|
||||
self.touch_sense_mode = "jl_cooper" if saved_touch_mode in ("jl_cooper", "jl_cooper_off", "jl_cooper_on") else "off"
|
||||
self.touch_sense_combo.blockSignals(True)
|
||||
self.touch_sense_combo.setCurrentIndex(1 if self.touch_sense_mode == "jl_cooper" else 0)
|
||||
self.touch_sense_combo.blockSignals(False)
|
||||
self.is_touching = False
|
||||
remote = state.get("remote", True)
|
||||
self.remote_checkbox.blockSignals(True)
|
||||
self.remote_checkbox.setChecked(remote)
|
||||
self.remote_checkbox.blockSignals(False)
|
||||
self.dest_spin.blockSignals(True)
|
||||
self.dest_spin.setValue(state.get("dest_id", 1))
|
||||
self.dest_spin.blockSignals(False)
|
||||
self.dest_spin.setEnabled(remote)
|
||||
if self.hw_cc is not None:
|
||||
self.learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.cc_input_lbl.setText(self._cc_input_label())
|
||||
@@ -0,0 +1,663 @@
|
||||
import uuid
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QSlider,
|
||||
QLabel, QLineEdit, QFrame, QSizePolicy,
|
||||
QPushButton, QCheckBox, QSpinBox, QComboBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
from palette import (
|
||||
PALETTE_NAMES,
|
||||
PALETTE_HEX,
|
||||
PALETTE_VIVID,
|
||||
lighten_hex,
|
||||
darken_hex,
|
||||
)
|
||||
from styles import FADER_STYLE, TITLE_STYLE
|
||||
from zonebutton import ZoneButton
|
||||
from colorswatchpopup import ColorSwatchPopup
|
||||
from osc_sender import send_osc_message
|
||||
from midi_sender import send_cc
|
||||
from extension_sender import send_to_extension
|
||||
|
||||
|
||||
class FocusFaderWidget(QWidget):
|
||||
"""Direct DAW-focus fader — OSC-only output, shown on any iPad in Channel Focus mode, no hardware learn input."""
|
||||
def __init__(self, label):
|
||||
|
||||
super().__init__()
|
||||
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Maximum)
|
||||
self.setFixedWidth(115)
|
||||
self.current_color = PALETTE_HEX.get("Gray", "#263238")
|
||||
|
||||
self.uid = str(uuid.uuid4())
|
||||
|
||||
# Pickup mode state
|
||||
self.pickup_mode = False
|
||||
self.last_sent = 64
|
||||
self.prev_position = 64
|
||||
self.hunting = False
|
||||
|
||||
self.is_learning = False
|
||||
self.is_learning_name = False
|
||||
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(0, 0, 0, 0)
|
||||
outer.setSpacing(2)
|
||||
|
||||
self.container = QFrame()
|
||||
self.container.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.apply_color(self.current_color)
|
||||
|
||||
inner = QVBoxLayout(self.container)
|
||||
inner.setContentsMargins(4, 6, 4, 6)
|
||||
inner.setSpacing(4)
|
||||
inner.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
_grp_style = "QFrame { border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; }"
|
||||
|
||||
# Group: OSC input (feedback from the DAW)
|
||||
self.grp_input = QFrame()
|
||||
self.grp_input.setStyleSheet(_grp_style)
|
||||
_grp_input_layout = QVBoxLayout(self.grp_input)
|
||||
_grp_input_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_input_layout.setSpacing(3)
|
||||
|
||||
self.learn_btn = QPushButton("Learn")
|
||||
self.learn_btn.setFixedHeight(20)
|
||||
self.learn_btn.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.learn_btn)
|
||||
|
||||
self.osc_addr_in_edit = QLineEdit("/track/volume")
|
||||
self.osc_addr_in_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_in_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_in_edit.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.osc_addr_in_edit)
|
||||
|
||||
self.cc_input_lbl = QLabel("")
|
||||
self.cc_input_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_input_lbl.setFixedHeight(16)
|
||||
_grp_input_layout.addWidget(self.cc_input_lbl)
|
||||
|
||||
inner.addWidget(self.grp_input)
|
||||
|
||||
self.arrow_lbl = QLabel("▼")
|
||||
self.arrow_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.arrow_lbl.setStyleSheet("color: rgba(255,255,255,0.3); font-size: 9px; background: transparent;")
|
||||
self.arrow_lbl.setFixedHeight(12)
|
||||
inner.addWidget(self.arrow_lbl)
|
||||
|
||||
# Group: OSC output controls + readout (sent to the DAW on fader change)
|
||||
self.grp_cc = QFrame()
|
||||
self.grp_cc.setStyleSheet(_grp_style)
|
||||
_grp2_layout = QVBoxLayout(self.grp_cc)
|
||||
_grp2_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp2_layout.setSpacing(3)
|
||||
|
||||
# OSC / Ext mode selector
|
||||
mode_row = QHBoxLayout()
|
||||
mode_row.setContentsMargins(0, 0, 0, 0)
|
||||
mode_row.setSpacing(2)
|
||||
self.osc_mode_btn = QPushButton("OSC")
|
||||
self.osc_mode_btn.setFixedHeight(18)
|
||||
self.osc_mode_btn.setCheckable(True)
|
||||
self.osc_mode_btn.setChecked(True)
|
||||
self.osc_mode_btn.setStyleSheet("border: none;")
|
||||
self.ext_mode_btn = QPushButton("Ext")
|
||||
self.ext_mode_btn.setFixedHeight(18)
|
||||
self.ext_mode_btn.setCheckable(True)
|
||||
self.ext_mode_btn.setChecked(False)
|
||||
self.ext_mode_btn.setStyleSheet("border: none;")
|
||||
self.osc_mode_btn.clicked.connect(lambda: self.set_output_mode("osc"))
|
||||
self.ext_mode_btn.clicked.connect(lambda: self.set_output_mode("ext"))
|
||||
mode_row.addWidget(self.osc_mode_btn)
|
||||
mode_row.addWidget(self.ext_mode_btn)
|
||||
_grp2_layout.addLayout(mode_row)
|
||||
|
||||
self.output_mode = "osc"
|
||||
self._last_ext_value = None # tracks previous fader value, for delta-on-move in Ext mode
|
||||
|
||||
# OSC address field
|
||||
self.osc_addr_out_edit = QLineEdit("/track/volume")
|
||||
self.osc_addr_out_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_out_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_out_edit.setStyleSheet("border: none;")
|
||||
_grp2_layout.addWidget(self.osc_addr_out_edit)
|
||||
|
||||
# Output readout
|
||||
self.cc_output_lbl = QLabel(self._osc_output_label(64))
|
||||
self.cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_output_lbl.setFixedHeight(16)
|
||||
_grp2_layout.addWidget(self.cc_output_lbl)
|
||||
|
||||
inner.addWidget(self.grp_cc)
|
||||
|
||||
# Group: Hardware — optional physical MIDI CC control alongside the
|
||||
# app/OSC control (e.g. a JL Cooper motorized fader), positioned like
|
||||
# grp_remote on the regular widgets rather than up with the OSC learn
|
||||
# section. "Hardware" binds a hw CC that can move this fader; independently,
|
||||
# "Feedback" sends a translated CC back out on every value change (from
|
||||
# hardware, OSC feedback, or the app) so a motorized fader's position stays
|
||||
# in sync — separate switches since you may want feedback without hardware
|
||||
# input, or vice versa.
|
||||
self.hw_cc = None
|
||||
self.hw_channel = None
|
||||
self.is_learning_hw = False
|
||||
|
||||
# Touch sense: some motorized fader hardware (e.g. JL Cooper) sends
|
||||
# touch on/off using the SAME CC as the fader's value, just on
|
||||
# hw_channel - 1 (e.g. value on CH16, touch on CH15). "Off" (default)
|
||||
# ignores this distinction entirely. While touching, incoming OSC/DAW
|
||||
# feedback is ignored (see receive_osc_value) so the motor doesn't
|
||||
# fight your hand.
|
||||
self.touch_sense_mode = "off"
|
||||
self.is_touching = False
|
||||
|
||||
self.grp_hardware = QFrame()
|
||||
self.grp_hardware.setStyleSheet(_grp_style)
|
||||
_grp_hw_layout = QVBoxLayout(self.grp_hardware)
|
||||
_grp_hw_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_hw_layout.setSpacing(3)
|
||||
|
||||
self.hardware_checkbox = QCheckBox("Hardware")
|
||||
self.hardware_checkbox.setChecked(False)
|
||||
self.hardware_checkbox.stateChanged.connect(lambda _: self._update_hw_visibility())
|
||||
_grp_hw_layout.addWidget(self.hardware_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.hw_learn_btn = QPushButton("Learn")
|
||||
self.hw_learn_btn.setFixedHeight(20)
|
||||
self.hw_learn_btn.setStyleSheet("border: none;")
|
||||
_grp_hw_layout.addWidget(self.hw_learn_btn)
|
||||
|
||||
self.hw_cc_input_lbl = QLabel("")
|
||||
self.hw_cc_input_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px; border: none;")
|
||||
self.hw_cc_input_lbl.setFixedHeight(16)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_input_lbl)
|
||||
|
||||
self.touch_sense_combo = QComboBox()
|
||||
self.touch_sense_combo.addItems(["No Profile", "MIDI JL Cooper CC Mode"])
|
||||
self.touch_sense_combo.setStyleSheet("font-size: 10px;")
|
||||
self.touch_sense_combo.currentIndexChanged.connect(self._on_touch_sense_changed)
|
||||
_grp_hw_layout.addWidget(self.touch_sense_combo)
|
||||
|
||||
self.feedback_checkbox = QCheckBox("Feedback")
|
||||
self.feedback_checkbox.setChecked(False)
|
||||
self.feedback_checkbox.stateChanged.connect(lambda _: self._update_hw_visibility())
|
||||
_grp_hw_layout.addWidget(self.feedback_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.hw_cc_spin = QSpinBox()
|
||||
self.hw_cc_spin.setMinimum(0)
|
||||
self.hw_cc_spin.setMaximum(127)
|
||||
self.hw_cc_spin.setValue(7)
|
||||
self.hw_cc_spin.setFixedWidth(55)
|
||||
_hw_cc_lbl = QLabel("CC")
|
||||
_hw_cc_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.hw_cc_spin_row = QWidget()
|
||||
_hbox_hw_cc = QHBoxLayout(self.hw_cc_spin_row)
|
||||
_hbox_hw_cc.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_hw_cc.setSpacing(4)
|
||||
_hbox_hw_cc.addWidget(_hw_cc_lbl)
|
||||
_hbox_hw_cc.addWidget(self.hw_cc_spin)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_spin_row)
|
||||
|
||||
self.hw_ch_spin = QSpinBox()
|
||||
self.hw_ch_spin.setMinimum(1)
|
||||
self.hw_ch_spin.setMaximum(16)
|
||||
self.hw_ch_spin.setValue(1)
|
||||
self.hw_ch_spin.setFixedWidth(55)
|
||||
_hw_ch_lbl = QLabel("CH")
|
||||
_hw_ch_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.hw_ch_spin_row = QWidget()
|
||||
_hbox_hw_ch = QHBoxLayout(self.hw_ch_spin_row)
|
||||
_hbox_hw_ch.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_hw_ch.setSpacing(4)
|
||||
_hbox_hw_ch.addWidget(_hw_ch_lbl)
|
||||
_hbox_hw_ch.addWidget(self.hw_ch_spin)
|
||||
_grp_hw_layout.addWidget(self.hw_ch_spin_row)
|
||||
|
||||
self.hw_cc_output_lbl = QLabel(f"CC{self.hw_cc_spin.value()} CH{self.hw_ch_spin.value()} [--]")
|
||||
self.hw_cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.hw_cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.hw_cc_output_lbl.setFixedHeight(16)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_output_lbl)
|
||||
|
||||
inner.addWidget(self.grp_hardware)
|
||||
|
||||
# Fader
|
||||
self.fader = QSlider(Qt.Orientation.Vertical)
|
||||
self.fader.setMinimum(0)
|
||||
self.fader.setMaximum(127)
|
||||
self.fader.setValue(64)
|
||||
self.fader.setMinimumHeight(200)
|
||||
self.fader.setFixedWidth(40)
|
||||
self.fader.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Expanding)
|
||||
self.fader.setStyleSheet(FADER_STYLE)
|
||||
inner.addWidget(self.fader, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
# Readout kept for pickup blink logic but not shown
|
||||
self.readout = QLabel(f"{self.osc_addr_out_edit.text()} → {self.fader.value()}")
|
||||
self.readout.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.readout.setVisible(False)
|
||||
|
||||
# Pickup checkbox
|
||||
self.pickup_checkbox = QCheckBox("Pickup")
|
||||
self.pickup_checkbox.setChecked(False)
|
||||
self.pickup_checkbox.stateChanged.connect(self.on_pickup_changed)
|
||||
inner.addWidget(self.pickup_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
# Track name learn (binds the scribble strip to live OSC track-name feedback)
|
||||
self.name_learn_btn = QPushButton("Learn Name")
|
||||
self.name_learn_btn.setFixedHeight(18)
|
||||
self.name_learn_btn.setStyleSheet("border: none;")
|
||||
inner.addWidget(self.name_learn_btn)
|
||||
|
||||
self.osc_addr_name_edit = QLineEdit("/track/name")
|
||||
self.osc_addr_name_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_name_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_name_edit.setStyleSheet("border: none;")
|
||||
inner.addWidget(self.osc_addr_name_edit)
|
||||
|
||||
# Scribble strip
|
||||
self.title_edit = QLineEdit(label)
|
||||
self.title_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.title_edit.setStyleSheet(TITLE_STYLE)
|
||||
inner.addWidget(self.title_edit)
|
||||
|
||||
# Color swatch button
|
||||
self.color_name = "Gray"
|
||||
self.color_swatch_btn = QPushButton()
|
||||
self.color_swatch_btn.setFixedHeight(10)
|
||||
self.color_swatch_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID['Gray']}; border-radius: 2px; border: none;")
|
||||
self.color_swatch_btn.clicked.connect(self.show_color_popup)
|
||||
inner.addWidget(self.color_swatch_btn)
|
||||
|
||||
# + / - buttons
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.setContentsMargins(0, 0, 0, 0)
|
||||
btn_row.setSpacing(2)
|
||||
self.minus_btn = QPushButton("-")
|
||||
self.minus_btn.setFixedSize(30, 20)
|
||||
self.plus_btn = QPushButton("+")
|
||||
self.plus_btn.setFixedSize(30, 20)
|
||||
btn_row.addWidget(self.minus_btn)
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(self.plus_btn)
|
||||
inner.addLayout(btn_row)
|
||||
|
||||
# Zone button
|
||||
self.zone_btn = ZoneButton()
|
||||
self.zone_btn.left_callback = lambda active: self.on_zone_checked("left", active)
|
||||
self.zone_btn.right_callback = lambda active: self.on_zone_checked("right", active)
|
||||
inner.addWidget(self.zone_btn)
|
||||
|
||||
# Separator before ID label
|
||||
id_sep = QWidget()
|
||||
id_sep.setFixedHeight(1)
|
||||
id_sep.setStyleSheet("background-color: #3a3a3a;")
|
||||
inner.addWidget(id_sep)
|
||||
|
||||
# Channel ID label
|
||||
self.id_label = QLabel("")
|
||||
self.id_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.id_label.setStyleSheet("color: #ffffff; font-size: 13px; font-weight: bold; background: transparent;")
|
||||
inner.addWidget(self.id_label)
|
||||
|
||||
outer.addWidget(self.container)
|
||||
|
||||
self.fader.valueChanged.connect(self.on_fader_moved)
|
||||
self.on_select_callback = None
|
||||
self.on_context_menu_callback = None
|
||||
self.zone = None # None, "left", or "right"
|
||||
self.center_index = None # saved position in center row before zoning
|
||||
self.zone_index = None # position within zone slot
|
||||
self.on_zone_change_callback = None
|
||||
|
||||
self._update_hw_visibility()
|
||||
|
||||
# Install event filter on all children to forward clicks to select
|
||||
for child in self.findChildren(QWidget):
|
||||
child.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() == QEvent.Type.MouseButtonPress:
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
elif event.type() == QEvent.Type.ContextMenu:
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
return True
|
||||
return False
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
|
||||
def start_learn(self):
|
||||
self.is_learning = True
|
||||
self.learn_btn.setText("Listening...")
|
||||
self.learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_learn(self):
|
||||
self.is_learning = False
|
||||
self.learn_btn.setText("Learn")
|
||||
self.learn_btn.setStyleSheet("")
|
||||
|
||||
def bind_osc_addr(self, address):
|
||||
self.osc_addr_in_edit.setText(address)
|
||||
self.stop_learn()
|
||||
|
||||
def _update_hw_visibility(self):
|
||||
hw_on = self.hardware_checkbox.isChecked()
|
||||
self.hw_learn_btn.setVisible(hw_on)
|
||||
self.hw_cc_input_lbl.setVisible(hw_on)
|
||||
fb_on = self.feedback_checkbox.isChecked()
|
||||
self.hw_cc_spin_row.setVisible(fb_on)
|
||||
self.hw_ch_spin_row.setVisible(fb_on)
|
||||
self.hw_cc_output_lbl.setVisible(fb_on)
|
||||
|
||||
def start_hw_learn(self):
|
||||
self.is_learning_hw = True
|
||||
self.hw_learn_btn.setText("Listening...")
|
||||
self.hw_learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_hw_learn(self):
|
||||
self.is_learning_hw = False
|
||||
self.hw_learn_btn.setStyleSheet("")
|
||||
if self.hw_cc is not None:
|
||||
self.hw_learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.hw_cc_input_lbl.setText(self._hw_cc_input_label())
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
else:
|
||||
self.hw_learn_btn.setText("Learn")
|
||||
self.hw_cc_input_lbl.setText("")
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px;")
|
||||
|
||||
def bind_hw_cc(self, cc_number, channel=None):
|
||||
self.hw_cc = cc_number
|
||||
self.hw_channel = channel
|
||||
self.stop_hw_learn()
|
||||
|
||||
def _hw_cc_input_label(self, vel="--"):
|
||||
if self.hw_cc is None:
|
||||
return ""
|
||||
ch = f" CH{self.hw_channel}" if self.hw_channel is not None else ""
|
||||
return f"CC{self.hw_cc}{ch} [{vel}]"
|
||||
|
||||
def _on_touch_sense_changed(self, index):
|
||||
self.touch_sense_mode = "jl_cooper" if index == 1 else "off"
|
||||
self.is_touching = False
|
||||
|
||||
def set_output_mode(self, mode):
|
||||
self.output_mode = mode
|
||||
if mode == "osc":
|
||||
self.osc_mode_btn.setChecked(True)
|
||||
self.ext_mode_btn.setChecked(False)
|
||||
self.osc_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.ext_mode_btn.setStyleSheet("")
|
||||
self.osc_addr_out_edit.setVisible(True)
|
||||
self.cc_output_lbl.setText(self._osc_output_label(self.fader.value()))
|
||||
else:
|
||||
self.ext_mode_btn.setChecked(True)
|
||||
self.osc_mode_btn.setChecked(False)
|
||||
self.ext_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.osc_mode_btn.setStyleSheet("")
|
||||
self.osc_addr_out_edit.setVisible(False)
|
||||
self.cc_output_lbl.setText("Ext [--]")
|
||||
self._last_ext_value = None # reset delta tracking on mode switch
|
||||
|
||||
def receive_osc_value(self, value):
|
||||
"""Update the fader to reflect feedback from the DAW, without echoing it back out."""
|
||||
if self.is_touching:
|
||||
# Hardware currently owns the value — don't let DAW/OSC feedback
|
||||
# fight the motor while a hand is physically on the fader.
|
||||
return
|
||||
fader_value = max(0, min(127, round(value * 127)))
|
||||
self.cc_input_lbl.setText(f"[{value:.2f}]")
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.fader.blockSignals(True)
|
||||
self.fader.setValue(fader_value)
|
||||
self.fader.blockSignals(False)
|
||||
|
||||
def start_learn_name(self):
|
||||
self.is_learning_name = True
|
||||
self.name_learn_btn.setText("Listening...")
|
||||
self.name_learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_learn_name(self):
|
||||
self.is_learning_name = False
|
||||
self.name_learn_btn.setText("Learn Name")
|
||||
self.name_learn_btn.setStyleSheet("")
|
||||
|
||||
def bind_osc_name_addr(self, address):
|
||||
self.osc_addr_name_edit.setText(address)
|
||||
self.stop_learn_name()
|
||||
|
||||
def receive_osc_name(self, name):
|
||||
"""Update the scribble strip to reflect the live track name from the DAW."""
|
||||
self.title_edit.setText(name)
|
||||
|
||||
def on_pickup_changed(self, state):
|
||||
self.pickup_mode = bool(state)
|
||||
if self.pickup_mode:
|
||||
self.hunting = True
|
||||
self.prev_position = self.fader.value()
|
||||
self.pickup_checkbox.setStyleSheet("color: orange; font-weight: bold;")
|
||||
self._start_blink()
|
||||
else:
|
||||
self.hunting = False
|
||||
self.pickup_checkbox.setStyleSheet("color: black;")
|
||||
self._stop_blink()
|
||||
self._set_readout_style("normal")
|
||||
|
||||
def _set_readout_style(self, mode):
|
||||
if mode == "normal":
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
self.pickup_checkbox.setStyleSheet("color: black;") if not self.pickup_mode else None
|
||||
elif mode == "orange":
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: orange; font-size: 10px; font-weight: bold; padding: 1px;")
|
||||
elif mode == "clear":
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: transparent; font-size: 10px; padding: 1px;")
|
||||
|
||||
def _start_blink(self):
|
||||
if not hasattr(self, '_blink_timer'):
|
||||
self._blink_timer = QTimer()
|
||||
self._blink_timer.timeout.connect(self._do_blink)
|
||||
self._blink_state = False
|
||||
self._blink_timer.start(400)
|
||||
|
||||
def _stop_blink(self):
|
||||
if hasattr(self, '_blink_timer'):
|
||||
self._blink_timer.stop()
|
||||
self._set_readout_style("normal")
|
||||
|
||||
def _do_blink(self):
|
||||
self._blink_state = not self._blink_state
|
||||
if self._blink_state:
|
||||
self._set_readout_style("orange")
|
||||
else:
|
||||
self._set_readout_style("clear")
|
||||
|
||||
def on_fader_moved(self, value):
|
||||
if self.output_mode == "ext":
|
||||
self.cc_output_lbl.setText(f"Ext [{value}]")
|
||||
else:
|
||||
self.cc_output_lbl.setText(self._osc_output_label(value))
|
||||
|
||||
if self.pickup_mode and self.hunting:
|
||||
crossed = (
|
||||
(self.prev_position < self.last_sent <= value) or
|
||||
(self.prev_position > self.last_sent >= value)
|
||||
)
|
||||
self.prev_position = value
|
||||
if crossed:
|
||||
self.hunting = False
|
||||
self._stop_blink()
|
||||
self._set_readout_style("orange")
|
||||
self.last_sent = value
|
||||
self._emit_value(value)
|
||||
else:
|
||||
self.readout.setText(f"[{value}]")
|
||||
return
|
||||
else:
|
||||
self.last_sent = value
|
||||
self.prev_position = value
|
||||
self._emit_value(value)
|
||||
self.readout.setText(f"[{value}]")
|
||||
|
||||
def _emit_value(self, value):
|
||||
"""Sends the current fader value out via whichever protocol is
|
||||
selected. Ext mode sends a delta (change since last move), not the
|
||||
absolute position — the extension's UDP protocol only understands
|
||||
deltas added to the current baseline, not absolute targets."""
|
||||
if self.output_mode == "ext":
|
||||
if self._last_ext_value is None:
|
||||
self._last_ext_value = value
|
||||
delta = (value - self._last_ext_value) / 127.0
|
||||
self._last_ext_value = value
|
||||
if delta != 0:
|
||||
send_to_extension(delta)
|
||||
else:
|
||||
addr = self.osc_addr_out_edit.text().strip() or "/track/volume"
|
||||
send_osc_message(addr, value / 127.0)
|
||||
self._send_hw_feedback(value)
|
||||
|
||||
def _send_hw_feedback(self, value):
|
||||
"""Sends a translated MIDI CC out (e.g. so a motorized fader's position
|
||||
tracks this channel's value), independent of whether hardware CC input
|
||||
is also enabled — you may want feedback without hardware input, or
|
||||
vice versa."""
|
||||
if not self.feedback_checkbox.isChecked():
|
||||
return
|
||||
out_cc = self.hw_cc_spin.value()
|
||||
out_ch = self.hw_ch_spin.value()
|
||||
send_cc(out_cc, value, channel=out_ch)
|
||||
self.hw_cc_output_lbl.setText(f"CC{out_cc} CH{out_ch} [{value}]")
|
||||
|
||||
def apply_color(self, hex_color, selected=False):
|
||||
self.current_color = hex_color
|
||||
border = "#00ff88" if selected else "rgba(0,0,0,0.3)"
|
||||
border_width = "3px" if selected else "2px"
|
||||
self.container.setObjectName("stripContainer")
|
||||
self.container.setStyleSheet(f"""
|
||||
QFrame#stripContainer {{
|
||||
background-color: {hex_color};
|
||||
border-radius: 6px;
|
||||
border: {border_width} solid {border};
|
||||
}}
|
||||
""")
|
||||
|
||||
def set_selected(self, selected):
|
||||
self.apply_color(self.current_color, selected=selected)
|
||||
|
||||
def update_id_label(self, zone, index):
|
||||
if zone == "left":
|
||||
self.id_label.setText(f"L{index + 1}")
|
||||
elif zone == "right":
|
||||
self.id_label.setText(f"R{index + 1}")
|
||||
else:
|
||||
self.id_label.setText(f"{index + 1}")
|
||||
|
||||
def on_zone_checked(self, zone, active):
|
||||
if active:
|
||||
self.zone = zone
|
||||
else:
|
||||
self.zone = None
|
||||
if self.on_zone_change_callback:
|
||||
self.on_zone_change_callback(self)
|
||||
|
||||
def show_color_popup(self):
|
||||
popup = ColorSwatchPopup(self.on_color_change, self)
|
||||
btn_pos = self.color_swatch_btn.mapToGlobal(self.color_swatch_btn.rect().bottomLeft())
|
||||
popup.move(btn_pos)
|
||||
popup.show()
|
||||
|
||||
def on_color_change(self, name, hex_color=None):
|
||||
if hex_color is None:
|
||||
hex_color = PALETTE_HEX.get(name, self.current_color)
|
||||
self.color_name = name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
|
||||
def _osc_output_label(self, value="--"):
|
||||
addr = self.osc_addr_out_edit.text().strip() or "/track/volume"
|
||||
if isinstance(value, (int, float)):
|
||||
return f"{addr} [{value / 127.0:.2f}]"
|
||||
return f"{addr} [{value}]"
|
||||
|
||||
def get_state(self):
|
||||
return {
|
||||
"uid": self.uid,
|
||||
"label": self.title_edit.text(),
|
||||
"value": self.fader.value(),
|
||||
"color": self.color_name,
|
||||
"pickup": self.pickup_checkbox.isChecked(),
|
||||
"last_sent": self.last_sent,
|
||||
"osc_addr_in": self.osc_addr_in_edit.text(),
|
||||
"osc_addr_out": self.osc_addr_out_edit.text(),
|
||||
"osc_addr_name": self.osc_addr_name_edit.text(),
|
||||
"zone": self.zone,
|
||||
"center_index": self.center_index,
|
||||
"zone_index": self.zone_index,
|
||||
"hardware_enabled": self.hardware_checkbox.isChecked(),
|
||||
"feedback_enabled": self.feedback_checkbox.isChecked(),
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"hw_out_cc": self.hw_cc_spin.value(),
|
||||
"hw_out_ch": self.hw_ch_spin.value(),
|
||||
"touch_sense_mode": self.touch_sense_mode,
|
||||
"output_mode": self.output_mode,
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
if "uid" in state:
|
||||
self.uid = state["uid"]
|
||||
self.title_edit.setText(state.get("label", ""))
|
||||
self.last_sent = state.get("last_sent", 64)
|
||||
self.fader.setValue(state.get("value", 64))
|
||||
color_name = state.get("color", "Gray")
|
||||
hex_color = PALETTE_HEX.get(color_name, "transparent")
|
||||
self.color_name = color_name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
pickup = state.get("pickup", False)
|
||||
self.pickup_checkbox.setChecked(pickup)
|
||||
if pickup:
|
||||
self.hunting = True
|
||||
self._start_blink()
|
||||
self.zone = state.get("zone", None)
|
||||
self.center_index = state.get("center_index", None)
|
||||
self.zone_index = state.get("zone_index", None)
|
||||
self.zone_btn.set_state(self.zone == "left", self.zone == "right")
|
||||
self.osc_addr_in_edit.setText(state.get("osc_addr_in", "/track/volume"))
|
||||
self.osc_addr_out_edit.setText(state.get("osc_addr_out", "/track/volume"))
|
||||
self.osc_addr_name_edit.setText(state.get("osc_addr_name", "/track/name"))
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
self.hw_cc_spin.setValue(state.get("hw_out_cc", 7))
|
||||
self.hw_ch_spin.setValue(state.get("hw_out_ch", 1))
|
||||
self.stop_hw_learn()
|
||||
self.hardware_checkbox.setChecked(state.get("hardware_enabled", False))
|
||||
self.feedback_checkbox.setChecked(state.get("feedback_enabled", False))
|
||||
# jl_cooper_off/jl_cooper_on were a short-lived three-state naming;
|
||||
# both collapse to the single jl_cooper mode.
|
||||
saved_touch_mode = state.get("touch_sense_mode", "off")
|
||||
self.touch_sense_mode = "jl_cooper" if saved_touch_mode in ("jl_cooper", "jl_cooper_off", "jl_cooper_on") else "off"
|
||||
self.touch_sense_combo.blockSignals(True)
|
||||
self.touch_sense_combo.setCurrentIndex(1 if self.touch_sense_mode == "jl_cooper" else 0)
|
||||
self.touch_sense_combo.blockSignals(False)
|
||||
self.is_touching = False
|
||||
self._update_hw_visibility()
|
||||
self.set_output_mode(state.get("output_mode", "osc"))
|
||||
@@ -0,0 +1,226 @@
|
||||
import uuid
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QLineEdit, QFrame, QSizePolicy, QCheckBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent
|
||||
|
||||
from palette import PALETTE_HEX, PALETTE_VIVID
|
||||
from styles import TITLE_STYLE
|
||||
from colorswatchpopup import ColorSwatchPopup
|
||||
|
||||
|
||||
class FocusFeedbackWidget(QWidget):
|
||||
"""Read-only OSC feedback display for Channel Focus — Learn an address, show whatever value arrives. No output, no interaction."""
|
||||
def __init__(self, label):
|
||||
super().__init__()
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
self.setFixedWidth(115)
|
||||
self.current_color = PALETTE_HEX.get("Gray", "#263238")
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.is_learning = False
|
||||
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(0, 0, 0, 0)
|
||||
outer.setSpacing(2)
|
||||
|
||||
self.container = QFrame()
|
||||
self.container.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.apply_color(self.current_color)
|
||||
|
||||
inner = QVBoxLayout(self.container)
|
||||
inner.setContentsMargins(4, 6, 4, 6)
|
||||
inner.setSpacing(4)
|
||||
inner.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
_grp_style = "QFrame { border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; }"
|
||||
|
||||
# Group: OSC input (feedback from the DAW)
|
||||
self.grp_input = QFrame()
|
||||
self.grp_input.setStyleSheet(_grp_style)
|
||||
_grp_input_layout = QVBoxLayout(self.grp_input)
|
||||
_grp_input_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_input_layout.setSpacing(3)
|
||||
|
||||
self.learn_btn = QPushButton("Learn")
|
||||
self.learn_btn.setFixedHeight(20)
|
||||
self.learn_btn.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.learn_btn)
|
||||
|
||||
self.osc_addr_in_edit = QLineEdit("/3/trackname")
|
||||
self.osc_addr_in_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_in_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_in_edit.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.osc_addr_in_edit)
|
||||
|
||||
# Custom: shows a fixed, manually-typed string instead of live OSC
|
||||
# feedback — disables Learn and the OSC address input while checked.
|
||||
self.custom_checkbox = QCheckBox("Custom")
|
||||
self.custom_checkbox.setChecked(False)
|
||||
self.custom_checkbox.stateChanged.connect(self._on_custom_changed)
|
||||
_grp_input_layout.addWidget(self.custom_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.custom_text_edit = QLineEdit("")
|
||||
self.custom_text_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.custom_text_edit.setPlaceholderText("Custom text")
|
||||
self.custom_text_edit.setStyleSheet("border: none;")
|
||||
self.custom_text_edit.textChanged.connect(self._on_custom_text_changed)
|
||||
self.custom_text_edit.setVisible(False)
|
||||
_grp_input_layout.addWidget(self.custom_text_edit)
|
||||
|
||||
inner.addWidget(self.grp_input)
|
||||
|
||||
# Readout — the live feedback value
|
||||
self.readout_lbl = QLabel("—")
|
||||
self.readout_lbl.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.readout_lbl.setWordWrap(True)
|
||||
self.readout_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 14px; font-weight: bold; padding: 6px 2px; border-radius: 4px;")
|
||||
self.readout_lbl.setMinimumHeight(48)
|
||||
inner.addWidget(self.readout_lbl)
|
||||
|
||||
# Scribble strip (caption — what this feedback represents, e.g. "Track Name")
|
||||
self.title_edit = QLineEdit(label)
|
||||
self.title_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.title_edit.setStyleSheet(TITLE_STYLE)
|
||||
inner.addWidget(self.title_edit)
|
||||
|
||||
# Color swatch button
|
||||
self.color_name = "Gray"
|
||||
self.color_swatch_btn = QPushButton()
|
||||
self.color_swatch_btn.setFixedHeight(10)
|
||||
self.color_swatch_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID['Gray']}; border-radius: 2px; border: none;")
|
||||
self.color_swatch_btn.clicked.connect(self.show_color_popup)
|
||||
inner.addWidget(self.color_swatch_btn)
|
||||
|
||||
# + / - buttons
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.setContentsMargins(0, 0, 0, 0)
|
||||
btn_row.setSpacing(2)
|
||||
self.minus_btn = QPushButton("-")
|
||||
self.minus_btn.setFixedSize(30, 20)
|
||||
self.plus_btn = QPushButton("+")
|
||||
self.plus_btn.setFixedSize(30, 20)
|
||||
btn_row.addWidget(self.minus_btn)
|
||||
btn_row.addStretch()
|
||||
btn_row.addWidget(self.plus_btn)
|
||||
inner.addLayout(btn_row)
|
||||
|
||||
outer.addWidget(self.container)
|
||||
self.on_select_callback = None
|
||||
self.on_context_menu_callback = None
|
||||
|
||||
for child in self.findChildren(QWidget):
|
||||
child.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() == QEvent.Type.MouseButtonPress:
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
elif event.type() == QEvent.Type.ContextMenu:
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
return True
|
||||
return False
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
|
||||
def start_learn(self):
|
||||
self.is_learning = True
|
||||
self.learn_btn.setText("Listening...")
|
||||
self.learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_learn(self):
|
||||
self.is_learning = False
|
||||
self.learn_btn.setText("Learn")
|
||||
self.learn_btn.setStyleSheet("")
|
||||
|
||||
def bind_osc_addr(self, address):
|
||||
self.osc_addr_in_edit.setText(address)
|
||||
self.stop_learn()
|
||||
|
||||
def _update_custom_visibility(self):
|
||||
is_custom = self.custom_checkbox.isChecked()
|
||||
self.learn_btn.setVisible(not is_custom)
|
||||
self.osc_addr_in_edit.setVisible(not is_custom)
|
||||
self.custom_text_edit.setVisible(is_custom)
|
||||
|
||||
def _on_custom_changed(self, _state):
|
||||
self._update_custom_visibility()
|
||||
if self.custom_checkbox.isChecked():
|
||||
self.readout_lbl.setText(self.custom_text_edit.text())
|
||||
|
||||
def _on_custom_text_changed(self, text):
|
||||
if self.custom_checkbox.isChecked():
|
||||
self.readout_lbl.setText(text)
|
||||
|
||||
def receive_osc_value(self, value):
|
||||
"""Display whatever feedback arrives on the bound address, verbatim."""
|
||||
if self.custom_checkbox.isChecked():
|
||||
return
|
||||
self.readout_lbl.setText(value)
|
||||
|
||||
def apply_color(self, hex_color, selected=False):
|
||||
self.current_color = hex_color
|
||||
border = "#00ff88" if selected else "rgba(0,0,0,0.3)"
|
||||
border_width = "3px" if selected else "2px"
|
||||
self.container.setObjectName("stripContainer")
|
||||
self.container.setStyleSheet(f"""
|
||||
QFrame#stripContainer {{
|
||||
background-color: {hex_color};
|
||||
border-radius: 6px;
|
||||
border: {border_width} solid {border};
|
||||
}}
|
||||
""")
|
||||
|
||||
def set_selected(self, selected):
|
||||
self.apply_color(self.current_color, selected=selected)
|
||||
|
||||
def show_color_popup(self):
|
||||
popup = ColorSwatchPopup(self.on_color_change, self)
|
||||
btn_pos = self.color_swatch_btn.mapToGlobal(self.color_swatch_btn.rect().bottomLeft())
|
||||
popup.move(btn_pos)
|
||||
popup.show()
|
||||
|
||||
def on_color_change(self, name, hex_color=None):
|
||||
if hex_color is None:
|
||||
hex_color = PALETTE_HEX.get(name, self.current_color)
|
||||
self.color_name = name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
|
||||
def get_state(self):
|
||||
return {
|
||||
"uid": self.uid,
|
||||
"label": self.title_edit.text(),
|
||||
"text": self.readout_lbl.text(),
|
||||
"color": self.color_name,
|
||||
"osc_addr_in": self.osc_addr_in_edit.text(),
|
||||
"custom_enabled": self.custom_checkbox.isChecked(),
|
||||
"custom_text": self.custom_text_edit.text(),
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
if "uid" in state:
|
||||
self.uid = state["uid"]
|
||||
self.title_edit.setText(state.get("label", ""))
|
||||
self.readout_lbl.setText(state.get("text", "—"))
|
||||
color_name = state.get("color", "Gray")
|
||||
hex_color = PALETTE_HEX.get(color_name, "transparent")
|
||||
self.color_name = color_name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
self.osc_addr_in_edit.setText(state.get("osc_addr_in", "/3/trackname"))
|
||||
self.custom_text_edit.setText(state.get("custom_text", ""))
|
||||
self.custom_checkbox.setChecked(state.get("custom_enabled", False))
|
||||
self._update_custom_visibility()
|
||||
@@ -0,0 +1,491 @@
|
||||
import uuid
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QLineEdit, QFrame, QSizePolicy, QCheckBox, QSpinBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
from palette import PALETTE_HEX, PALETTE_VIVID
|
||||
from styles import TITLE_STYLE
|
||||
from zonebutton import ZoneButton
|
||||
from colorswatchpopup import ColorSwatchPopup
|
||||
from osc_sender import send_osc_message
|
||||
from midi_sender import send_cc
|
||||
|
||||
|
||||
class FocusToggleWidget(QWidget):
|
||||
"""Direct DAW-focus toggle — OSC-only output, shown on any iPad in Channel Focus mode, no hardware learn input."""
|
||||
def __init__(self, label):
|
||||
super().__init__()
|
||||
self.setSizePolicy(QSizePolicy.Policy.Fixed, QSizePolicy.Policy.Preferred)
|
||||
self.setFixedWidth(115)
|
||||
self.current_color = PALETTE_HEX.get("Gray", "#263238")
|
||||
self.toggle_state = False
|
||||
self.trigger_mode = "toggle"
|
||||
self.uid = str(uuid.uuid4())
|
||||
self.zone = None
|
||||
self.center_index = None
|
||||
self.zone_index = None
|
||||
self.on_zone_change_callback = None
|
||||
|
||||
self.is_learning = False
|
||||
|
||||
outer = QVBoxLayout(self)
|
||||
outer.setContentsMargins(0, 0, 0, 0)
|
||||
outer.setSpacing(2)
|
||||
|
||||
self.container = QFrame()
|
||||
self.container.setFrameShape(QFrame.Shape.StyledPanel)
|
||||
self.apply_color(self.current_color)
|
||||
|
||||
inner = QVBoxLayout(self.container)
|
||||
inner.setContentsMargins(4, 6, 4, 6)
|
||||
inner.setSpacing(4)
|
||||
inner.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
_grp_style = "QFrame { border: 1px solid rgba(255,255,255,0.2); border-radius: 4px; }"
|
||||
|
||||
# Group: OSC input (feedback from the DAW)
|
||||
self.grp_input = QFrame()
|
||||
self.grp_input.setStyleSheet(_grp_style)
|
||||
_grp_input_layout = QVBoxLayout(self.grp_input)
|
||||
_grp_input_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_input_layout.setSpacing(3)
|
||||
|
||||
self.learn_btn = QPushButton("Learn")
|
||||
self.learn_btn.setFixedHeight(20)
|
||||
self.learn_btn.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.learn_btn)
|
||||
|
||||
self.osc_addr_in_edit = QLineEdit("/track/mute")
|
||||
self.osc_addr_in_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_in_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_in_edit.setStyleSheet("border: none;")
|
||||
_grp_input_layout.addWidget(self.osc_addr_in_edit)
|
||||
|
||||
self.cc_input_lbl = QLabel("")
|
||||
self.cc_input_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_input_lbl.setFixedHeight(16)
|
||||
_grp_input_layout.addWidget(self.cc_input_lbl)
|
||||
|
||||
inner.addWidget(self.grp_input)
|
||||
|
||||
self.arrow_lbl = QLabel("▼")
|
||||
self.arrow_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.arrow_lbl.setStyleSheet("color: rgba(255,255,255,0.3); font-size: 9px; background: transparent;")
|
||||
self.arrow_lbl.setFixedHeight(12)
|
||||
inner.addWidget(self.arrow_lbl)
|
||||
|
||||
# Group: OSC output controls + readout (sent to the DAW on click)
|
||||
self.grp_cc = QFrame()
|
||||
self.grp_cc.setStyleSheet(_grp_style)
|
||||
_grp2_layout = QVBoxLayout(self.grp_cc)
|
||||
_grp2_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp2_layout.setSpacing(3)
|
||||
|
||||
# Momentary / Toggle mode selector
|
||||
trigger_row = QHBoxLayout()
|
||||
trigger_row.setContentsMargins(0, 0, 0, 0)
|
||||
trigger_row.setSpacing(2)
|
||||
self.momentary_btn = QPushButton("Mom.")
|
||||
self.momentary_btn.setFixedHeight(18)
|
||||
self.momentary_btn.setCheckable(True)
|
||||
self.momentary_btn.setChecked(False)
|
||||
self.momentary_btn.setStyleSheet("border: none;")
|
||||
self.toggle_mode_btn = QPushButton("Tog.")
|
||||
self.toggle_mode_btn.setFixedHeight(18)
|
||||
self.toggle_mode_btn.setCheckable(True)
|
||||
self.toggle_mode_btn.setChecked(True)
|
||||
self.toggle_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.momentary_btn.clicked.connect(lambda: self.set_trigger_mode("momentary"))
|
||||
self.toggle_mode_btn.clicked.connect(lambda: self.set_trigger_mode("toggle"))
|
||||
trigger_row.addWidget(self.momentary_btn)
|
||||
trigger_row.addWidget(self.toggle_mode_btn)
|
||||
_grp2_layout.addLayout(trigger_row)
|
||||
|
||||
self.osc_addr_out_edit = QLineEdit("/track/mute")
|
||||
self.osc_addr_out_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.osc_addr_out_edit.setPlaceholderText("/address")
|
||||
self.osc_addr_out_edit.setStyleSheet("border: none;")
|
||||
_grp2_layout.addWidget(self.osc_addr_out_edit)
|
||||
|
||||
self.cc_output_lbl = QLabel(self._osc_output_label("--"))
|
||||
self.cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.cc_output_lbl.setFixedHeight(16)
|
||||
_grp2_layout.addWidget(self.cc_output_lbl)
|
||||
|
||||
inner.addWidget(self.grp_cc)
|
||||
|
||||
# Group: Hardware — optional physical MIDI CC control alongside the
|
||||
# app/OSC control (e.g. a JL Cooper button), positioned like grp_remote
|
||||
# on the regular widgets rather than up with the OSC learn section.
|
||||
# "Hardware" binds a hw CC/note that can trigger this toggle; independently,
|
||||
# "Feedback" sends a translated CC back out on every state change (from
|
||||
# hardware, OSC feedback, or the app) so hardware LEDs/motors stay in
|
||||
# sync — separate switches since you may want one without the other.
|
||||
self.hw_cc = None
|
||||
self.hw_channel = None
|
||||
self.is_learning_hw = False
|
||||
|
||||
self.grp_hardware = QFrame()
|
||||
self.grp_hardware.setStyleSheet(_grp_style)
|
||||
_grp_hw_layout = QVBoxLayout(self.grp_hardware)
|
||||
_grp_hw_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_hw_layout.setSpacing(3)
|
||||
|
||||
self.hardware_checkbox = QCheckBox("Hardware")
|
||||
self.hardware_checkbox.setChecked(False)
|
||||
self.hardware_checkbox.stateChanged.connect(lambda _: self._update_hw_visibility())
|
||||
_grp_hw_layout.addWidget(self.hardware_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.hw_learn_btn = QPushButton("Learn")
|
||||
self.hw_learn_btn.setFixedHeight(20)
|
||||
self.hw_learn_btn.setStyleSheet("border: none;")
|
||||
_grp_hw_layout.addWidget(self.hw_learn_btn)
|
||||
|
||||
self.hw_cc_input_lbl = QLabel("")
|
||||
self.hw_cc_input_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px; border: none;")
|
||||
self.hw_cc_input_lbl.setFixedHeight(16)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_input_lbl)
|
||||
|
||||
self.feedback_checkbox = QCheckBox("Feedback")
|
||||
self.feedback_checkbox.setChecked(False)
|
||||
self.feedback_checkbox.stateChanged.connect(lambda _: self._update_hw_visibility())
|
||||
_grp_hw_layout.addWidget(self.feedback_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.hw_cc_spin = QSpinBox()
|
||||
self.hw_cc_spin.setMinimum(0)
|
||||
self.hw_cc_spin.setMaximum(127)
|
||||
self.hw_cc_spin.setValue(7)
|
||||
self.hw_cc_spin.setFixedWidth(55)
|
||||
_hw_cc_lbl = QLabel("CC")
|
||||
_hw_cc_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.hw_cc_spin_row = QWidget()
|
||||
_hbox_hw_cc = QHBoxLayout(self.hw_cc_spin_row)
|
||||
_hbox_hw_cc.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_hw_cc.setSpacing(4)
|
||||
_hbox_hw_cc.addWidget(_hw_cc_lbl)
|
||||
_hbox_hw_cc.addWidget(self.hw_cc_spin)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_spin_row)
|
||||
|
||||
self.hw_ch_spin = QSpinBox()
|
||||
self.hw_ch_spin.setMinimum(1)
|
||||
self.hw_ch_spin.setMaximum(16)
|
||||
self.hw_ch_spin.setValue(1)
|
||||
self.hw_ch_spin.setFixedWidth(55)
|
||||
_hw_ch_lbl = QLabel("CH")
|
||||
_hw_ch_lbl.setStyleSheet("color: #fff; font-size: 10px; border: none;")
|
||||
self.hw_ch_spin_row = QWidget()
|
||||
_hbox_hw_ch = QHBoxLayout(self.hw_ch_spin_row)
|
||||
_hbox_hw_ch.setContentsMargins(0, 0, 0, 0)
|
||||
_hbox_hw_ch.setSpacing(4)
|
||||
_hbox_hw_ch.addWidget(_hw_ch_lbl)
|
||||
_hbox_hw_ch.addWidget(self.hw_ch_spin)
|
||||
_grp_hw_layout.addWidget(self.hw_ch_spin_row)
|
||||
|
||||
self.hw_cc_output_lbl = QLabel(f"CC{self.hw_cc_spin.value()} CH{self.hw_ch_spin.value()} [--]")
|
||||
self.hw_cc_output_lbl.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.hw_cc_output_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self.hw_cc_output_lbl.setFixedHeight(16)
|
||||
_grp_hw_layout.addWidget(self.hw_cc_output_lbl)
|
||||
|
||||
inner.addWidget(self.grp_hardware)
|
||||
|
||||
# Toggle button with LED dot overlay
|
||||
btn_container = QWidget()
|
||||
btn_container.setFixedSize(60, 60)
|
||||
|
||||
self.btn = QPushButton("", btn_container)
|
||||
self.btn.setFixedSize(60, 60)
|
||||
self.btn.clicked.connect(self.on_click)
|
||||
|
||||
self.led_dot = QLabel(btn_container)
|
||||
self.led_dot.setFixedSize(6, 6)
|
||||
self.led_dot.move(5, 49)
|
||||
self.led_dot.setStyleSheet("background-color: #3c3c3c; border-radius: 3px;")
|
||||
|
||||
inner.addWidget(btn_container, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
# Scribble strip
|
||||
self.title_edit = QLineEdit(label)
|
||||
self.title_edit.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.title_edit.setStyleSheet(TITLE_STYLE)
|
||||
inner.addWidget(self.title_edit)
|
||||
|
||||
# Color swatch button
|
||||
self.color_name = "Gray"
|
||||
self.color_swatch_btn = QPushButton()
|
||||
self.color_swatch_btn.setFixedHeight(10)
|
||||
self.color_swatch_btn.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed)
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID['Gray']}; border-radius: 2px; border: none;")
|
||||
self.color_swatch_btn.clicked.connect(self.show_color_popup)
|
||||
inner.addWidget(self.color_swatch_btn)
|
||||
|
||||
# + / - buttons
|
||||
toggle_btn_row = QHBoxLayout()
|
||||
toggle_btn_row.setContentsMargins(0, 0, 0, 0)
|
||||
toggle_btn_row.setSpacing(2)
|
||||
self.minus_btn = QPushButton("-")
|
||||
self.minus_btn.setFixedSize(30, 20)
|
||||
self.plus_btn = QPushButton("+")
|
||||
self.plus_btn.setFixedSize(30, 20)
|
||||
toggle_btn_row.addWidget(self.minus_btn)
|
||||
toggle_btn_row.addStretch()
|
||||
toggle_btn_row.addWidget(self.plus_btn)
|
||||
inner.addLayout(toggle_btn_row)
|
||||
|
||||
# Zone button
|
||||
self.zone_btn = ZoneButton()
|
||||
self.zone_btn.left_callback = lambda active: self.on_zone_checked("left", active)
|
||||
self.zone_btn.right_callback = lambda active: self.on_zone_checked("right", active)
|
||||
inner.addWidget(self.zone_btn)
|
||||
|
||||
# Separator before ID label
|
||||
id_sep = QWidget()
|
||||
id_sep.setFixedHeight(1)
|
||||
id_sep.setStyleSheet("background-color: #3a3a3a;")
|
||||
inner.addWidget(id_sep)
|
||||
|
||||
# Channel ID label
|
||||
self.id_label = QLabel("")
|
||||
self.id_label.setAlignment(Qt.AlignmentFlag.AlignHCenter)
|
||||
self.id_label.setStyleSheet("color: #ffffff; font-size: 13px; font-weight: bold; background: transparent;")
|
||||
inner.addWidget(self.id_label)
|
||||
|
||||
outer.addWidget(self.container)
|
||||
self.on_select_callback = None
|
||||
self.on_context_menu_callback = None
|
||||
|
||||
self._update_hw_visibility()
|
||||
|
||||
for child in self.findChildren(QWidget):
|
||||
child.installEventFilter(self)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() == QEvent.Type.MouseButtonPress:
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
elif event.type() == QEvent.Type.ContextMenu:
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
return True
|
||||
return False
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() != Qt.MouseButton.RightButton:
|
||||
if self.on_select_callback:
|
||||
self.on_select_callback(self)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def contextMenuEvent(self, event):
|
||||
if self.on_context_menu_callback:
|
||||
self.on_context_menu_callback(self, event.globalPos())
|
||||
|
||||
def start_learn(self):
|
||||
self.is_learning = True
|
||||
self.learn_btn.setText("Listening...")
|
||||
self.learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_learn(self):
|
||||
self.is_learning = False
|
||||
self.learn_btn.setText("Learn")
|
||||
self.learn_btn.setStyleSheet("")
|
||||
|
||||
def bind_osc_addr(self, address):
|
||||
self.osc_addr_in_edit.setText(address)
|
||||
self.stop_learn()
|
||||
|
||||
def _update_hw_visibility(self):
|
||||
hw_on = self.hardware_checkbox.isChecked()
|
||||
self.hw_learn_btn.setVisible(hw_on)
|
||||
self.hw_cc_input_lbl.setVisible(hw_on)
|
||||
fb_on = self.feedback_checkbox.isChecked()
|
||||
self.hw_cc_spin_row.setVisible(fb_on)
|
||||
self.hw_ch_spin_row.setVisible(fb_on)
|
||||
self.hw_cc_output_lbl.setVisible(fb_on)
|
||||
|
||||
def start_hw_learn(self):
|
||||
self.is_learning_hw = True
|
||||
self.hw_learn_btn.setText("Listening...")
|
||||
self.hw_learn_btn.setStyleSheet("color: orange; font-weight: bold;")
|
||||
|
||||
def stop_hw_learn(self):
|
||||
self.is_learning_hw = False
|
||||
self.hw_learn_btn.setStyleSheet("")
|
||||
if self.hw_cc is not None:
|
||||
self.hw_learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.hw_cc_input_lbl.setText(self._hw_cc_input_label())
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px;")
|
||||
else:
|
||||
self.hw_learn_btn.setText("Learn")
|
||||
self.hw_cc_input_lbl.setText("")
|
||||
self.hw_cc_input_lbl.setStyleSheet("background-color: #000000; color: #888888; font-size: 10px; padding: 1px;")
|
||||
|
||||
def bind_hw_cc(self, cc_number, channel=None):
|
||||
self.hw_cc = cc_number
|
||||
self.hw_channel = channel
|
||||
self.stop_hw_learn()
|
||||
|
||||
def _hw_cc_input_label(self, vel="--"):
|
||||
if self.hw_cc is None:
|
||||
return ""
|
||||
ch = f" CH{self.hw_channel}" if self.hw_channel is not None else ""
|
||||
return f"CC{self.hw_cc}{ch} [{vel}]"
|
||||
|
||||
def _send_hw_feedback(self, value):
|
||||
"""Sends a translated MIDI CC out (e.g. so a hardware button's LED
|
||||
tracks this toggle's state), independent of whether hardware CC input
|
||||
is also enabled."""
|
||||
if not self.feedback_checkbox.isChecked():
|
||||
return
|
||||
out_cc = self.hw_cc_spin.value()
|
||||
out_ch = self.hw_ch_spin.value()
|
||||
send_cc(out_cc, value, channel=out_ch)
|
||||
self.hw_cc_output_lbl.setText(f"CC{out_cc} CH{out_ch} [{value}]")
|
||||
|
||||
def receive_osc_value(self, value):
|
||||
"""Update the toggle to reflect feedback from the DAW, without echoing it back out."""
|
||||
self.toggle_state = value >= 0.5
|
||||
self.cc_input_lbl.setText(f"[{value:.2f}]")
|
||||
self.cc_input_lbl.setStyleSheet("background-color: #000000; color: #00e676; font-size: 10px; padding: 1px; border: none;")
|
||||
self._update_btn_style()
|
||||
|
||||
def apply_color(self, hex_color, selected=False):
|
||||
self.current_color = hex_color
|
||||
border = "#00ff88" if selected else "rgba(0,0,0,0.3)"
|
||||
border_width = "3px" if selected else "2px"
|
||||
self.container.setObjectName("stripContainer")
|
||||
self.container.setStyleSheet(f"""
|
||||
QFrame#stripContainer {{
|
||||
background-color: {hex_color};
|
||||
border-radius: 6px;
|
||||
border: {border_width} solid {border};
|
||||
}}
|
||||
""")
|
||||
|
||||
def set_selected(self, selected):
|
||||
self.apply_color(self.current_color, selected=selected)
|
||||
|
||||
def update_id_label(self, zone, index):
|
||||
if zone == "left":
|
||||
self.id_label.setText(f"L{index + 1}")
|
||||
elif zone == "right":
|
||||
self.id_label.setText(f"R{index + 1}")
|
||||
else:
|
||||
self.id_label.setText(f"{index + 1}")
|
||||
|
||||
def on_zone_checked(self, zone, active):
|
||||
if active:
|
||||
self.zone = zone
|
||||
else:
|
||||
self.zone = None
|
||||
if self.on_zone_change_callback:
|
||||
self.on_zone_change_callback(self)
|
||||
|
||||
def show_color_popup(self):
|
||||
popup = ColorSwatchPopup(self.on_color_change, self)
|
||||
btn_pos = self.color_swatch_btn.mapToGlobal(self.color_swatch_btn.rect().bottomLeft())
|
||||
popup.move(btn_pos)
|
||||
popup.show()
|
||||
|
||||
def on_color_change(self, name, hex_color=None):
|
||||
if hex_color is None:
|
||||
hex_color = PALETTE_HEX.get(name, self.current_color)
|
||||
self.color_name = name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
self._update_btn_style()
|
||||
|
||||
def set_trigger_mode(self, mode):
|
||||
self.trigger_mode = mode
|
||||
if mode == "momentary":
|
||||
self.momentary_btn.setChecked(True)
|
||||
self.toggle_mode_btn.setChecked(False)
|
||||
self.momentary_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.toggle_mode_btn.setStyleSheet("")
|
||||
else:
|
||||
self.toggle_mode_btn.setChecked(True)
|
||||
self.momentary_btn.setChecked(False)
|
||||
self.toggle_mode_btn.setStyleSheet("font-weight: bold; color: #00e676;")
|
||||
self.momentary_btn.setStyleSheet("")
|
||||
|
||||
def on_click(self):
|
||||
addr = self.osc_addr_out_edit.text().strip() or "/track/mute"
|
||||
if self.trigger_mode == "momentary":
|
||||
send_osc_message(addr, 1.0)
|
||||
self.cc_output_lbl.setText(self._osc_output_label(1.0))
|
||||
self.led_dot.setStyleSheet("background-color: #00e676; border-radius: 3px;")
|
||||
QTimer.singleShot(150, lambda: self.led_dot.setStyleSheet("background-color: #3c3c3c; border-radius: 3px;"))
|
||||
self._send_hw_feedback(127)
|
||||
else:
|
||||
self.toggle_state = not self.toggle_state
|
||||
value = 1.0 if self.toggle_state else 0.0
|
||||
send_osc_message(addr, value)
|
||||
self.cc_output_lbl.setText(self._osc_output_label(value))
|
||||
self._update_btn_style()
|
||||
self._send_hw_feedback(127 if self.toggle_state else 0)
|
||||
|
||||
def _update_btn_style(self):
|
||||
if self.toggle_state:
|
||||
self.led_dot.setStyleSheet("background-color: #00e676; border-radius: 3px;")
|
||||
else:
|
||||
self.led_dot.setStyleSheet("background-color: #3c3c3c; border-radius: 3px;")
|
||||
|
||||
def _osc_output_label(self, value="--"):
|
||||
addr = self.osc_addr_out_edit.text().strip() or "/track/mute"
|
||||
if isinstance(value, (int, float)):
|
||||
return f"{addr} [{value:.1f}]"
|
||||
return f"{addr} [{value}]"
|
||||
|
||||
def get_state(self):
|
||||
return {
|
||||
"uid": self.uid,
|
||||
"label": self.title_edit.text(),
|
||||
"state": self.toggle_state,
|
||||
"color": self.color_name,
|
||||
"zone": self.zone,
|
||||
"center_index": self.center_index,
|
||||
"zone_index": self.zone_index,
|
||||
"trigger_mode": self.trigger_mode,
|
||||
"osc_addr_in": self.osc_addr_in_edit.text(),
|
||||
"osc_addr_out": self.osc_addr_out_edit.text(),
|
||||
"hardware_enabled": self.hardware_checkbox.isChecked(),
|
||||
"feedback_enabled": self.feedback_checkbox.isChecked(),
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"hw_out_cc": self.hw_cc_spin.value(),
|
||||
"hw_out_ch": self.hw_ch_spin.value(),
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
if "uid" in state:
|
||||
self.uid = state["uid"]
|
||||
self.title_edit.setText(state.get("label", ""))
|
||||
color_name = state.get("color", "Gray")
|
||||
hex_color = PALETTE_HEX.get(color_name, "transparent")
|
||||
self.color_name = color_name
|
||||
self.color_swatch_btn.setStyleSheet(f"background-color: {PALETTE_VIVID.get(self.color_name, hex_color)}; border-radius: 2px; border: none;")
|
||||
self.apply_color(hex_color)
|
||||
self.toggle_state = state.get("state", False)
|
||||
self._update_btn_style()
|
||||
self.cc_output_lbl.setText(self._osc_output_label(1.0 if self.toggle_state else 0.0))
|
||||
self.zone = state.get("zone", None)
|
||||
self.center_index = state.get("center_index", None)
|
||||
self.zone_index = state.get("zone_index", None)
|
||||
self.zone_btn.set_state(self.zone == "left", self.zone == "right")
|
||||
self.set_trigger_mode(state.get("trigger_mode", "toggle"))
|
||||
self.osc_addr_in_edit.setText(state.get("osc_addr_in", "/track/mute"))
|
||||
self.osc_addr_out_edit.setText(state.get("osc_addr_out", "/track/mute"))
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
self.hw_cc_spin.setValue(state.get("hw_out_cc", 7))
|
||||
self.hw_ch_spin.setValue(state.get("hw_out_ch", 1))
|
||||
self.stop_hw_learn()
|
||||
self.hardware_checkbox.setChecked(state.get("hardware_enabled", False))
|
||||
self.feedback_checkbox.setChecked(state.get("feedback_enabled", False))
|
||||
self._update_hw_visibility()
|
||||
+1398
-32
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ class MidiReceiver(QObject):
|
||||
midi_out_signal = pyqtSignal(list)
|
||||
transport_out_signal = pyqtSignal()
|
||||
hw_cc_signal = pyqtSignal(str, int)
|
||||
controller_in_signal = pyqtSignal(list)
|
||||
|
||||
midi_receiver = MidiReceiver()
|
||||
|
||||
@@ -32,6 +32,6 @@ def send_osc_message(address, value=1.0):
|
||||
try:
|
||||
get_osc_client().send_message(address, float(value))
|
||||
osc_receiver.osc_out_signal.emit(address, str(value))
|
||||
print(f"[OSC] → {address} {value}")
|
||||
print(f"[OSC] -> {address} {value}")
|
||||
except Exception as e:
|
||||
print(f"[OSC send] Error: {e}")
|
||||
@@ -9,10 +9,29 @@ from PyQt6.QtWidgets import (
|
||||
)
|
||||
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
PRESETS_DIR = os.path.join(BASE_DIR, "presets")
|
||||
PRESETS_FILE = os.path.join(PRESETS_DIR, "presets.json")
|
||||
os.makedirs(PRESETS_DIR, exist_ok=True)
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
if getattr(sys, "frozen", False):
|
||||
# Packaged exe: Program Files (or wherever it's installed) is not
|
||||
# writable by a normal user, so presets live in the per-user AppData
|
||||
# folder instead — standard Windows convention for app-written data.
|
||||
PRESETS_DIR = os.path.join(os.environ["APPDATA"], "VirtualController")
|
||||
PRESETS_FILE = os.path.join(PRESETS_DIR, "presets.json")
|
||||
os.makedirs(PRESETS_DIR, exist_ok=True)
|
||||
if not os.path.exists(PRESETS_FILE):
|
||||
# First launch on this machine: seed from the presets.json bundled
|
||||
# into the exe (sys._MEIPASS), so a fresh install starts with the
|
||||
# real presets instead of empty. Only happens once — after this,
|
||||
# AppData is the only copy that's ever read or written.
|
||||
_bundled = os.path.join(sys._MEIPASS, "app-presets", "presets.json")
|
||||
if os.path.exists(_bundled):
|
||||
shutil.copyfile(_bundled, PRESETS_FILE)
|
||||
else:
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
PRESETS_DIR = os.path.join(BASE_DIR, "app-presets")
|
||||
PRESETS_FILE = os.path.join(PRESETS_DIR, "presets.json")
|
||||
os.makedirs(PRESETS_DIR, exist_ok=True)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import os
|
||||
import runpy
|
||||
|
||||
_here = os.path.dirname(os.path.abspath(__file__))
|
||||
runpy.run_path(os.path.join(_here, "main.py"), run_name="__main__")
|
||||
@@ -2,7 +2,7 @@ import uuid
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy, QCheckBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
@@ -137,6 +137,26 @@ class ToggleWidget(QWidget):
|
||||
|
||||
inner.addWidget(self.grp_cc)
|
||||
|
||||
# Group 3: Remote routing
|
||||
self.grp_remote = QFrame()
|
||||
self.grp_remote.setStyleSheet(_grp_style)
|
||||
_grp_remote_layout = QVBoxLayout(self.grp_remote)
|
||||
_grp_remote_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_remote_layout.setSpacing(3)
|
||||
|
||||
self.remote_checkbox = QCheckBox("App")
|
||||
self.remote_checkbox.setChecked(True)
|
||||
self.remote_checkbox.stateChanged.connect(lambda _: self.dest_spin.setEnabled(self.remote_checkbox.isChecked()))
|
||||
_grp_remote_layout.addWidget(self.remote_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.dest_spin = QSpinBox()
|
||||
self.dest_spin.setRange(1, 9)
|
||||
self.dest_spin.setValue(1)
|
||||
self.dest_spin.setFixedWidth(52)
|
||||
_grp_remote_layout.addWidget(self.dest_spin, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
inner.addWidget(self.grp_remote)
|
||||
|
||||
# Toggle button with LED dot overlay
|
||||
btn_container = QWidget()
|
||||
btn_container.setFixedSize(60, 60)
|
||||
@@ -370,6 +390,8 @@ class ToggleWidget(QWidget):
|
||||
"hw_cc": self.hw_cc,
|
||||
"hw_channel": self.hw_channel,
|
||||
"trigger_mode": self.trigger_mode,
|
||||
"remote": self.remote_checkbox.isChecked(),
|
||||
"dest_id": self.dest_spin.value(),
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
@@ -394,6 +416,14 @@ class ToggleWidget(QWidget):
|
||||
self.hw_cc = state.get("hw_cc", None)
|
||||
self.hw_channel = state.get("hw_channel", None)
|
||||
self.set_trigger_mode(state.get("trigger_mode", "toggle"))
|
||||
remote = state.get("remote", True)
|
||||
self.remote_checkbox.blockSignals(True)
|
||||
self.remote_checkbox.setChecked(remote)
|
||||
self.remote_checkbox.blockSignals(False)
|
||||
self.dest_spin.blockSignals(True)
|
||||
self.dest_spin.setValue(state.get("dest_id", 1))
|
||||
self.dest_spin.blockSignals(False)
|
||||
self.dest_spin.setEnabled(remote)
|
||||
if self.hw_cc is not None:
|
||||
self.learn_btn.setText(f"HW: CC{self.hw_cc}")
|
||||
self.cc_input_lbl.setText(self._cc_input_label())
|
||||
@@ -2,7 +2,7 @@ import uuid
|
||||
|
||||
from PyQt6.QtWidgets import (
|
||||
QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy
|
||||
QLabel, QSpinBox, QLineEdit, QFrame, QSizePolicy, QCheckBox
|
||||
)
|
||||
from PyQt6.QtCore import Qt, QEvent, QTimer
|
||||
|
||||
@@ -152,6 +152,26 @@ class TransportWidget(QWidget):
|
||||
|
||||
self.output_mode = "midi"
|
||||
|
||||
# Group 3: Remote routing
|
||||
self.grp_remote = QFrame()
|
||||
self.grp_remote.setStyleSheet(_grp_style)
|
||||
_grp_remote_layout = QVBoxLayout(self.grp_remote)
|
||||
_grp_remote_layout.setContentsMargins(4, 4, 4, 4)
|
||||
_grp_remote_layout.setSpacing(3)
|
||||
|
||||
self.remote_checkbox = QCheckBox("App")
|
||||
self.remote_checkbox.setChecked(True)
|
||||
self.remote_checkbox.stateChanged.connect(lambda _: self.dest_spin.setEnabled(self.remote_checkbox.isChecked()))
|
||||
_grp_remote_layout.addWidget(self.remote_checkbox, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
self.dest_spin = QSpinBox()
|
||||
self.dest_spin.setRange(1, 9)
|
||||
self.dest_spin.setValue(1)
|
||||
self.dest_spin.setFixedWidth(52)
|
||||
_grp_remote_layout.addWidget(self.dest_spin, alignment=Qt.AlignmentFlag.AlignHCenter)
|
||||
|
||||
inner.addWidget(self.grp_remote)
|
||||
|
||||
# Transport button with LED dot
|
||||
btn_container = QWidget()
|
||||
btn_container.setFixedSize(60, 60)
|
||||
@@ -378,6 +398,8 @@ class TransportWidget(QWidget):
|
||||
"output_mode": self.output_mode,
|
||||
"osc_addr": self.osc_addr_edit.text(),
|
||||
"trigger_mode": self.trigger_mode,
|
||||
"remote": self.remote_checkbox.isChecked(),
|
||||
"dest_id": self.dest_spin.value(),
|
||||
}
|
||||
|
||||
def set_state(self, state):
|
||||
@@ -404,3 +426,11 @@ class TransportWidget(QWidget):
|
||||
self.osc_addr_edit.setText(state.get("osc_addr", "/play"))
|
||||
self.set_output_mode(state.get("output_mode", "midi"))
|
||||
self.set_trigger_mode(state.get("trigger_mode", "momentary"))
|
||||
remote = state.get("remote", True)
|
||||
self.remote_checkbox.blockSignals(True)
|
||||
self.remote_checkbox.setChecked(remote)
|
||||
self.remote_checkbox.blockSignals(False)
|
||||
self.dest_spin.blockSignals(True)
|
||||
self.dest_spin.setValue(state.get("dest_id", 1))
|
||||
self.dest_spin.blockSignals(False)
|
||||
self.dest_spin.setEnabled(remote)
|
||||
@@ -0,0 +1,108 @@
|
||||
import json
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
from PyQt6.QtWebSockets import QWebSocketServer
|
||||
from PyQt6.QtNetwork import QHostAddress
|
||||
|
||||
|
||||
class WSServer(QObject):
|
||||
control_received = pyqtSignal(str, int)
|
||||
control_f_received = pyqtSignal(str, float)
|
||||
layout_saved = pyqtSignal(str, object)
|
||||
widget_visibility_received = pyqtSignal(str, int)
|
||||
client_connected = pyqtSignal()
|
||||
log_signal = pyqtSignal(str)
|
||||
raw_in_signal = pyqtSignal(str)
|
||||
|
||||
def __init__(self, port=8765, parent=None):
|
||||
super().__init__(parent)
|
||||
self._clients = []
|
||||
self._port = port
|
||||
self._server = QWebSocketServer("VC", QWebSocketServer.SslMode.NonSecureMode, self)
|
||||
if self._server.listen(QHostAddress.SpecialAddress.AnyIPv4, port):
|
||||
self.log_signal.emit(f"WS listening on :{port}")
|
||||
else:
|
||||
self.log_signal.emit(f"WS failed to bind :{port}")
|
||||
self._server.newConnection.connect(self._on_new_connection)
|
||||
|
||||
def _on_new_connection(self):
|
||||
client = self._server.nextPendingConnection()
|
||||
peer = client.peerAddress().toString()
|
||||
self._clients.append(client)
|
||||
client.textMessageReceived.connect(self._on_message)
|
||||
client.disconnected.connect(lambda c=client, p=peer: self._on_disconnect(c, p))
|
||||
self.log_signal.emit(f"client connected {peer} ({len(self._clients)} total)")
|
||||
self.client_connected.emit()
|
||||
|
||||
def _on_disconnect(self, client, peer):
|
||||
if client in self._clients:
|
||||
self._clients.remove(client)
|
||||
client.deleteLater()
|
||||
self.log_signal.emit(f"client disconnected {peer} ({len(self._clients)} remaining)")
|
||||
|
||||
def _on_message(self, message):
|
||||
self.raw_in_signal.emit(message)
|
||||
try:
|
||||
data = json.loads(message)
|
||||
ev = data.get("event")
|
||||
if ev == "control":
|
||||
uid = data["uid"]
|
||||
val = int(data["value"])
|
||||
self.log_signal.emit(f"control uid={uid[:8]}… val={val}")
|
||||
self.control_received.emit(uid, val)
|
||||
elif ev == "control_f":
|
||||
uid = data["uid"]
|
||||
val = float(data["value"])
|
||||
self.log_signal.emit(f"control_f uid={uid[:8]}… val={val:.4f}")
|
||||
self.control_f_received.emit(uid, val)
|
||||
elif ev == "save_layout":
|
||||
n = len(data.get("layout", {}))
|
||||
self.log_signal.emit(f"layout saved preset={data['preset_uuid'][:8]}… {n} widgets")
|
||||
self.layout_saved.emit(data["preset_uuid"], data["layout"])
|
||||
elif ev == "widget_visibility":
|
||||
uid = data["uid"]
|
||||
dest_id = int(data.get("dest_id", 0))
|
||||
self.log_signal.emit(f"widget visibility uid={uid[:8]}… dest_id={dest_id}")
|
||||
self.widget_visibility_received.emit(uid, dest_id)
|
||||
else:
|
||||
self.log_signal.emit(f"unknown event: {ev}")
|
||||
except Exception as e:
|
||||
self.log_signal.emit(f"bad message: {e}")
|
||||
|
||||
@property
|
||||
def has_clients(self) -> bool:
|
||||
return len(self._clients) > 0
|
||||
|
||||
def broadcast(self, data: dict):
|
||||
if not self._clients:
|
||||
return
|
||||
msg = json.dumps(data)
|
||||
for client in list(self._clients):
|
||||
client.sendTextMessage(msg)
|
||||
|
||||
def broadcast_preset(self, snapshot: dict, layout: dict):
|
||||
name = snapshot.get("preset_name", "")
|
||||
self.log_signal.emit(f"broadcast preset '{name}' → {len(self._clients)} client(s)")
|
||||
# layout omitted — owned and persisted client-side, keyed by preset_uuid
|
||||
self.broadcast({"event": "preset", "data": snapshot})
|
||||
|
||||
def broadcast_widget_update(self, uid: str, value: int):
|
||||
self.broadcast({"event": "widget_update", "uid": uid, "value": value})
|
||||
|
||||
def broadcast_widget_update_f(self, uid: str, value: float):
|
||||
self.broadcast({"event": "widget_update_f", "uid": uid, "value": value})
|
||||
|
||||
def broadcast_widget_label(self, uid: str, label: str):
|
||||
self.broadcast({"event": "widget_label", "uid": uid, "label": label})
|
||||
|
||||
def broadcast_widget_feedback(self, uid: str, text: str):
|
||||
self.broadcast({"event": "widget_feedback", "uid": uid, "text": text})
|
||||
|
||||
def broadcast_daw_state(self, **kwargs):
|
||||
self.broadcast({"event": "daw_state", **kwargs})
|
||||
|
||||
def stop(self):
|
||||
self._server.close()
|
||||
for client in list(self._clients):
|
||||
client.close()
|
||||
self._clients.clear()
|
||||
self.log_signal.emit("WS server stopped")
|
||||
@@ -0,0 +1,6 @@
|
||||
build-mac/output/
|
||||
build-win/output/
|
||||
*.obj
|
||||
*.exp
|
||||
*.lib
|
||||
*.pdb
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
# Builds extension-reaper as a REAPER extension .dylib.
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
NAME="reaper_extension-reaper.dylib"
|
||||
OUT_DIR="output"
|
||||
OUT="$OUT_DIR/$NAME"
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
clang++ \
|
||||
-std=c++17 \
|
||||
-fvisibility=hidden \
|
||||
-dynamiclib \
|
||||
-arch arm64 \
|
||||
-I../vendor/reaper-sdk/sdk \
|
||||
-I../vendor/reaper-sdk/WDL \
|
||||
-I../src \
|
||||
-o "$OUT" \
|
||||
../src/main.cpp \
|
||||
../src/hello/hello.cpp \
|
||||
../src/tracking/tracking.cpp \
|
||||
../src/socket/socket.cpp
|
||||
|
||||
echo "Built $OUT"
|
||||
|
||||
DEST="$HOME/Library/Application Support/REAPER/UserPlugins/$NAME"
|
||||
cp "$OUT" "$DEST"
|
||||
echo "Copied to $DEST (restart REAPER to reload)"
|
||||
@@ -0,0 +1,50 @@
|
||||
# Builds extension-reaper as a REAPER extension .dll.
|
||||
$ErrorActionPreference = "Stop"
|
||||
Set-Location $PSScriptRoot
|
||||
|
||||
$Name = "reaper_extension-reaper.dll"
|
||||
$OutDir = "output"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $OutDir | Out-Null
|
||||
|
||||
# Locate the MSVC toolchain and import its dev environment, so this script
|
||||
# runs from a plain PowerShell session instead of requiring a "Developer
|
||||
# PowerShell for VS" shell.
|
||||
$vswhere = "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe"
|
||||
if (-not (Test-Path $vswhere)) {
|
||||
throw "vswhere.exe not found - install Visual Studio Build Tools (C++ workload) first."
|
||||
}
|
||||
|
||||
$installPath = & $vswhere -latest -products "*" -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath
|
||||
if (-not $installPath) {
|
||||
throw "No MSVC C++ toolchain found via vswhere - install Visual Studio Build Tools (C++ workload) first."
|
||||
}
|
||||
|
||||
$vcvarsall = Join-Path $installPath "VC\Auxiliary\Build\vcvarsall.bat"
|
||||
$envDump = cmd /c "`"$vcvarsall`" x64 && set"
|
||||
foreach ($line in $envDump) {
|
||||
if ($line -match "^([^=]+)=(.*)$") {
|
||||
Set-Item -Path "Env:$($Matches[1])" -Value $Matches[2]
|
||||
}
|
||||
}
|
||||
|
||||
cl.exe `
|
||||
/std:c++17 `
|
||||
/EHsc `
|
||||
/LD `
|
||||
/I ..\vendor\reaper-sdk\sdk `
|
||||
/I ..\vendor\reaper-sdk\WDL `
|
||||
/I ..\src `
|
||||
/Fo"$OutDir\" `
|
||||
/Fe"$OutDir\$Name" `
|
||||
..\src\main.cpp `
|
||||
..\src\hello\hello.cpp `
|
||||
..\src\tracking\tracking.cpp `
|
||||
..\src\socket\socket.cpp `
|
||||
/link ws2_32.lib
|
||||
|
||||
Write-Output "Built $OutDir\$Name"
|
||||
|
||||
$Dest = Join-Path $env:APPDATA "REAPER\UserPlugins\$Name"
|
||||
Copy-Item -Path "$OutDir\$Name" -Destination $Dest -Force
|
||||
Write-Output "Copied to $Dest (restart REAPER to reload)"
|
||||
@@ -0,0 +1,70 @@
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
#include "hello/hello.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
static bool isOurAction;
|
||||
static bool handled;
|
||||
|
||||
static int helloActionId = 0;
|
||||
static const char *kHelloActionIdStr = "EXTENSION_REAPER_HELLO";
|
||||
static const char *kHelloActionName = "Hello Action List Display Name";
|
||||
|
||||
static bool HelloAction(KbdSectionInfo *sec, int command, int val, int val2, int relmode, HWND hwnd);
|
||||
static void HandleHelloAction(int command);
|
||||
static void RunHelloAction();
|
||||
|
||||
//step 1 register
|
||||
void RegisterHello(reaper_plugin_info_t *rec)
|
||||
{
|
||||
custom_action_register_t actionDescription = {
|
||||
0,
|
||||
kHelloActionIdStr,
|
||||
kHelloActionName,
|
||||
NULL,
|
||||
};
|
||||
helloActionId = rec->Register("custom_action", &actionDescription);
|
||||
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "[extension-reaper] helloActionId = %d\n", helloActionId);
|
||||
ShowConsoleMsg(buf);
|
||||
|
||||
rec->Register("hookcommand2", (void *)HelloAction);
|
||||
}
|
||||
|
||||
//step 2 callback from REAPER
|
||||
static bool HelloAction(KbdSectionInfo *sec, int command, int val, int val2, int relmode, HWND hwnd)
|
||||
{
|
||||
HandleHelloAction(command);
|
||||
return handled;
|
||||
}
|
||||
//step 3 check whether the ID REAPER gave us is our registered action, and dispatch to step 4 if so
|
||||
static void HandleHelloAction(int command)
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "[extension-reaper] HelloAction called: command=%d helloActionId=%d\n", command, helloActionId);
|
||||
ShowConsoleMsg(buf);
|
||||
|
||||
isOurAction = (command == helloActionId);
|
||||
|
||||
if (isOurAction == true)
|
||||
{
|
||||
RunHelloAction();
|
||||
handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ShowConsoleMsg("[extension-reaper] ignored — not ours\n");
|
||||
handled = false;
|
||||
}
|
||||
//step 4 perform action
|
||||
static void RunHelloAction()
|
||||
{
|
||||
ShowConsoleMsg("[extension-reaper] Hello action triggered\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
// The "Hello" test action — the first thing built in this extension, to
|
||||
// prove custom_action registration + hookcommand2 callbacks work end to
|
||||
// end. Kept around as a working reference, not load-bearing for the real
|
||||
// feature.
|
||||
|
||||
#ifndef EXTENSION_REAPER_HELLO_H
|
||||
#define EXTENSION_REAPER_HELLO_H
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
|
||||
// Call once from the entrypoint, after REAPERAPI_LoadAPI has succeeded.
|
||||
void RegisterHello(reaper_plugin_info_t *rec);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
// extension-reaper — REAPER extension entry point.
|
||||
//
|
||||
// This file's only job: establish REAPER_PLUGIN_ENTRYPOINT (the one
|
||||
// function name REAPER looks for), do the required load/unload/version
|
||||
// checks, then delegate everything else to its own module — hello/,
|
||||
// tracking/, socket/.
|
||||
//
|
||||
// Top-level story:
|
||||
// - We tell the compiler which REAPER functions we need a box for. This
|
||||
// file has to cover everything used anywhere in the project, since it's
|
||||
// the one with REAPERAPI_IMPLEMENT — the one that owns the real storage.
|
||||
// - REAPER loads our .dylib and calls our one required function, once.
|
||||
// - We check we're actually loading, not unloading, and the version matches.
|
||||
// - We fill our function box(es) with their real address.
|
||||
// - We delegate to each module's own Register___(rec) function.
|
||||
// - We print "loaded successfully" — proof of everything up to that point,
|
||||
// but NOT proof any individual module actually works.
|
||||
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
#define REAPERAPI_WANT_CountAutomationItems
|
||||
#define REAPERAPI_WANT_GetSetAutomationItemInfo
|
||||
#define REAPERAPI_WANT_CountTracks
|
||||
#define REAPERAPI_WANT_GetTrack
|
||||
#define REAPERAPI_WANT_CountTrackEnvelopes
|
||||
#define REAPERAPI_WANT_GetTrackEnvelope
|
||||
#define REAPERAPI_IMPLEMENT
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
#include "hello/hello.h"
|
||||
#include "tracking/tracking.h"
|
||||
#include "socket/socket.h"
|
||||
|
||||
// Return values REAPER expects back from us.
|
||||
const int kUnloadOrIncompatible = 0;
|
||||
const int kLoadedSuccessfully = 1;
|
||||
|
||||
static bool CheckerExtLoadingOrUnloading(reaper_plugin_info_t *rec);
|
||||
static bool CheckerExtVersionMatches(reaper_plugin_info_t *rec);
|
||||
static bool CheckerExtFunctionsLoaded(reaper_plugin_info_t *rec);
|
||||
static void RegisterActions(reaper_plugin_info_t *rec);
|
||||
|
||||
// REAPER loads our .dylib and calls this once.
|
||||
extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(REAPER_PLUGIN_HINSTANCE hInstance, reaper_plugin_info_t *rec)
|
||||
{
|
||||
if (CheckerExtLoadingOrUnloading(rec) == true)
|
||||
{
|
||||
return kUnloadOrIncompatible;
|
||||
}
|
||||
|
||||
if (CheckerExtVersionMatches(rec) == false)
|
||||
{
|
||||
return kUnloadOrIncompatible;
|
||||
}
|
||||
|
||||
if (CheckerExtFunctionsLoaded(rec) == false)
|
||||
{
|
||||
return kUnloadOrIncompatible;
|
||||
}
|
||||
|
||||
RegisterActions(rec);
|
||||
|
||||
// Prove we got this far — but NOT proof any individual module works.
|
||||
ShowConsoleMsg("[extension-reaper] loaded successfully\n");
|
||||
|
||||
return kLoadedSuccessfully;
|
||||
}
|
||||
|
||||
// Checks whether REAPER is unloading us (rec is NULL) rather than loading
|
||||
// us. Safe to call no matter what — this IS the check for whether rec is
|
||||
// even safe to use for anything else.
|
||||
static bool CheckerExtLoadingOrUnloading(reaper_plugin_info_t *rec)
|
||||
{
|
||||
bool isUnloading = (rec == NULL);
|
||||
return isUnloading;
|
||||
}
|
||||
|
||||
// Checks whether this REAPER's plugin format matches what we expect. Only
|
||||
// call after CheckerExtLoadingOrUnloading has confirmed rec is real.
|
||||
static bool CheckerExtVersionMatches(reaper_plugin_info_t *rec)
|
||||
{
|
||||
bool versionMatches = (rec->caller_version == REAPER_PLUGIN_VERSION);
|
||||
return versionMatches;
|
||||
}
|
||||
|
||||
// Fetches every REAPER function we WANT'd above, and checks whether all of
|
||||
// them were found. Only call once we know rec is real.
|
||||
static bool CheckerExtFunctionsLoaded(reaper_plugin_info_t *rec)
|
||||
{
|
||||
int missingFunctionCount = REAPERAPI_LoadAPI(rec->GetFunc);
|
||||
bool allFunctionsLoaded = (missingFunctionCount == 0);
|
||||
return allFunctionsLoaded;
|
||||
}
|
||||
|
||||
// Delegates to each module's own Register___(rec) function — one place
|
||||
// that lists every module this extension is made of.
|
||||
static void RegisterActions(reaper_plugin_info_t *rec)
|
||||
{
|
||||
RegisterHello(rec);
|
||||
RegisterTracking(rec);
|
||||
RegisterSocket(rec);
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
// A basic UDP socket listener. Opens a local port and polls it
|
||||
// non-blockingly via REAPER's "timer" callback (same mechanism
|
||||
// tracking.cpp uses — each Register("timer", ...) call adds its own
|
||||
// independent subscriber, so this runs alongside tracking.cpp's OnTimer,
|
||||
// not instead of it). Non-blocking means this never stalls REAPER's main
|
||||
// thread waiting for network data that may never arrive.
|
||||
//
|
||||
// Incoming messages are parsed as a plain number (e.g. "0.05" or "-0.05")
|
||||
// and applied as a delta to the currently-selected automation item's
|
||||
// baseline, via the same NudgeSelectedBaseline tracking.cpp's manual
|
||||
// actions already use.
|
||||
|
||||
// Winsock2 must be included before windows.h (pulled in transitively by
|
||||
// reaper_plugin.h below) — windows.h drags in the legacy winsock.h unless
|
||||
// winsock2.h has already set its include guard, and the two can't coexist
|
||||
// in one translation unit.
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
typedef SOCKET socket_t;
|
||||
static const socket_t kInvalidSocket = INVALID_SOCKET;
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
typedef int socket_t;
|
||||
static const socket_t kInvalidSocket = -1;
|
||||
#endif
|
||||
|
||||
// No REAPERAPI_IMPLEMENT here — main.cpp owns the real storage.
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
#include "socket/socket.h"
|
||||
#include "tracking/tracking.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
static const int kListenPort = 9124;
|
||||
static socket_t listen_fd = kInvalidSocket;
|
||||
|
||||
static void OnSocketTimer()
|
||||
{
|
||||
if (listen_fd == kInvalidSocket)
|
||||
return;
|
||||
|
||||
// Drain every pending packet each tick, instead of one recvfrom() per
|
||||
// tick. REAPER's timer only fires ~30x/second-ish, so if messages arrive
|
||||
// faster than that (a fast fader drag easily does), a one-shot read
|
||||
// falls further and further behind as packets queue up in the OS buffer.
|
||||
// Since our protocol is deltas, summing everything pending into one net
|
||||
// value and applying it once is equivalent to applying each
|
||||
// individually — just one REAPER API call instead of many.
|
||||
double total_delta = 0.0;
|
||||
char data[256];
|
||||
#ifdef _WIN32
|
||||
int n;
|
||||
#else
|
||||
ssize_t n;
|
||||
#endif
|
||||
while ((n = recvfrom(listen_fd, data, sizeof(data) - 1, 0, NULL, NULL)) > 0)
|
||||
{
|
||||
data[n] = '\0';
|
||||
|
||||
// Note: atof() returns 0.0 both for "actually parsed as zero" and for
|
||||
// "couldn't parse this at all" — can't tell those apart from the
|
||||
// return value alone. Fine for now since we control what sends here.
|
||||
total_delta += atof(data);
|
||||
}
|
||||
|
||||
if (total_delta != 0.0)
|
||||
NudgeSelectedBaseline(total_delta);
|
||||
}
|
||||
|
||||
void RegisterSocket(reaper_plugin_info_t *rec)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
WSADATA wsaData;
|
||||
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
|
||||
{
|
||||
ShowConsoleMsg("[extension-reaper] WSAStartup() failed\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
listen_fd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (listen_fd == kInvalidSocket)
|
||||
{
|
||||
ShowConsoleMsg("[extension-reaper] socket() failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Non-blocking — recvfrom() returns immediately if nothing's arrived,
|
||||
// instead of stalling REAPER's main thread waiting for data.
|
||||
#ifdef _WIN32
|
||||
u_long mode = 1;
|
||||
ioctlsocket(listen_fd, FIONBIO, &mode);
|
||||
#else
|
||||
fcntl(listen_fd, F_SETFL, O_NONBLOCK);
|
||||
#endif
|
||||
|
||||
sockaddr_in addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // localhost only, for now
|
||||
addr.sin_port = htons(kListenPort);
|
||||
|
||||
if (bind(listen_fd, (sockaddr *)&addr, sizeof(addr)) < 0)
|
||||
{
|
||||
ShowConsoleMsg("[extension-reaper] socket bind() failed\n");
|
||||
#ifdef _WIN32
|
||||
closesocket(listen_fd);
|
||||
#else
|
||||
close(listen_fd);
|
||||
#endif
|
||||
listen_fd = kInvalidSocket;
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "[extension-reaper] socket listening on 127.0.0.1:%d\n", kListenPort);
|
||||
ShowConsoleMsg(buf);
|
||||
|
||||
rec->Register("timer", (void *)OnSocketTimer);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// A basic UDP socket listener for extension-reaper — the eventual
|
||||
// entry point for the desktop app to spray fader/control data at REAPER.
|
||||
|
||||
#ifndef EXTENSION_REAPER_SOCKET_H
|
||||
#define EXTENSION_REAPER_SOCKET_H
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
|
||||
// Opens a UDP socket and registers a REAPER timer to poll it
|
||||
// non-blockingly. Call once from the entrypoint, after REAPERAPI_LoadAPI
|
||||
// has succeeded.
|
||||
void RegisterSocket(reaper_plugin_info_t *rec);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
// Tracks REAPER's currently selected automation item(s), purely by
|
||||
// polling. REAPER doesn't tell us when selection changes, so we check
|
||||
// repeatedly via a "timer" callback and only print when something's new.
|
||||
//
|
||||
// Also registers two test actions (nudge baseline up/down) to prove we can
|
||||
// WRITE to an automation item, not just read it — same pattern as Hello in
|
||||
// main.cpp: custom_action to register, hookcommand2 to react when it runs.
|
||||
|
||||
// No REAPERAPI_IMPLEMENT here — main.cpp owns the real storage for these
|
||||
// function boxes (it's the one .cpp file that defines IMPLEMENT). This file
|
||||
// just borrows them via extern declarations, same names, same addresses.
|
||||
#define REAPERAPI_MINIMAL
|
||||
#define REAPERAPI_WANT_ShowConsoleMsg
|
||||
#define REAPERAPI_WANT_CountAutomationItems
|
||||
#define REAPERAPI_WANT_GetSetAutomationItemInfo
|
||||
#define REAPERAPI_WANT_CountTracks
|
||||
#define REAPERAPI_WANT_GetTrack
|
||||
#define REAPERAPI_WANT_CountTrackEnvelopes
|
||||
#define REAPERAPI_WANT_GetTrackEnvelope
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
#include "reaper_plugin_functions.h"
|
||||
#include "tracking/tracking.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
static int nudge_up_id = 0;
|
||||
static int nudge_down_id = 0;
|
||||
static const double kNudgeAmount = 0.05;
|
||||
|
||||
struct SelectedItem
|
||||
{
|
||||
TrackEnvelope *env;
|
||||
int idx;
|
||||
bool operator==(const SelectedItem &other) const
|
||||
{
|
||||
return env == other.env && idx == other.idx;
|
||||
}
|
||||
};
|
||||
|
||||
// Scans every envelope on every track in the project, collecting every
|
||||
// automation item currently marked selected (D_UISEL) — regardless of
|
||||
// which track/envelope currently has UI focus. That's what lets this work
|
||||
// across multiple tracks at once, and also what makes a clip keep being
|
||||
// targeted after you've clicked onto a different track: we're not asking
|
||||
// REAPER "what's focused right now," we're checking the actual selection
|
||||
// flag on every item, everywhere, every time.
|
||||
static std::vector<SelectedItem> FindAllSelectedItems()
|
||||
{
|
||||
std::vector<SelectedItem> result;
|
||||
|
||||
int track_count = CountTracks(NULL);
|
||||
for (int t = 0; t < track_count; t++)
|
||||
{
|
||||
MediaTrack *track = GetTrack(NULL, t);
|
||||
if (!track)
|
||||
continue;
|
||||
|
||||
int env_count = CountTrackEnvelopes(track);
|
||||
for (int e = 0; e < env_count; e++)
|
||||
{
|
||||
TrackEnvelope *env = GetTrackEnvelope(track, e);
|
||||
if (!env)
|
||||
continue;
|
||||
|
||||
int item_count = CountAutomationItems(env);
|
||||
for (int i = 0; i < item_count; i++)
|
||||
{
|
||||
if (GetSetAutomationItemInfo(env, i, "D_UISEL", 0, false) != 0)
|
||||
result.push_back({env, i});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::vector<SelectedItem> last_printed_items;
|
||||
|
||||
// REAPER calls this automatically, many times per second, no matter what
|
||||
// the user is doing. Prints only when the selected set actually changes.
|
||||
static void OnTimer()
|
||||
{
|
||||
std::vector<SelectedItem> items = FindAllSelectedItems();
|
||||
|
||||
if (items != last_printed_items)
|
||||
{
|
||||
last_printed_items = items;
|
||||
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "[extension-reaper] %zu item(s) selected\n", items.size());
|
||||
ShowConsoleMsg(buf);
|
||||
}
|
||||
}
|
||||
|
||||
// Shared by the manual nudge actions (below) and socket.cpp's listener —
|
||||
// one path, two ways to trigger it. Applies the same delta to every
|
||||
// currently-selected item, anywhere in the project, each clamped
|
||||
// independently.
|
||||
void NudgeSelectedBaseline(double delta)
|
||||
{
|
||||
std::vector<SelectedItem> items = FindAllSelectedItems();
|
||||
if (items.empty())
|
||||
{
|
||||
ShowConsoleMsg("[extension-reaper] nudge: nothing selected\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const SelectedItem &item : items)
|
||||
{
|
||||
double baseline = GetSetAutomationItemInfo(item.env, item.idx, "D_BASELINE", 0, false);
|
||||
double newBaseline = baseline + delta;
|
||||
if (newBaseline < 0.0) newBaseline = 0.0; // D_BASELINE's valid range is [0,1]
|
||||
if (newBaseline > 1.0) newBaseline = 1.0;
|
||||
GetSetAutomationItemInfo(item.env, item.idx, "D_BASELINE", newBaseline, true);
|
||||
}
|
||||
// No console log here — this runs on every fader move, and a GUI
|
||||
// text-widget update per call was itself a real latency cost.
|
||||
}
|
||||
|
||||
// Called for every action, any trigger source. Checks if it was one of our
|
||||
// two nudge actions; if so, calls the shared NudgeSelectedBaseline above.
|
||||
static bool NudgeAction(KbdSectionInfo *sec, int command, int val, int val2, int relmode, HWND hwnd)
|
||||
{
|
||||
if (command == nudge_up_id)
|
||||
NudgeSelectedBaseline(kNudgeAmount);
|
||||
else if (command == nudge_down_id)
|
||||
NudgeSelectedBaseline(-kNudgeAmount);
|
||||
else
|
||||
return false; // not ours
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void RegisterTracking(reaper_plugin_info_t *rec)
|
||||
{
|
||||
rec->Register("timer", (void *)OnTimer);
|
||||
|
||||
custom_action_register_t nudge_up_desc = {
|
||||
0,
|
||||
"EXTENSION_REAPER_NUDGE_UP",
|
||||
"extension-reaper: Nudge baseline up",
|
||||
NULL,
|
||||
};
|
||||
nudge_up_id = rec->Register("custom_action", &nudge_up_desc);
|
||||
|
||||
custom_action_register_t nudge_down_desc = {
|
||||
0,
|
||||
"EXTENSION_REAPER_NUDGE_DOWN",
|
||||
"extension-reaper: Nudge baseline down",
|
||||
NULL,
|
||||
};
|
||||
nudge_down_id = rec->Register("custom_action", &nudge_down_desc);
|
||||
|
||||
rec->Register("hookcommand2", (void *)NudgeAction);
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Polls REAPER for the currently selected envelope + automation item, since
|
||||
// REAPER has no "selection changed" notification to hook directly.
|
||||
|
||||
#ifndef EXTENSION_REAPER_TRACKING_H
|
||||
#define EXTENSION_REAPER_TRACKING_H
|
||||
|
||||
#include "reaper_plugin.h"
|
||||
|
||||
// Call once from the entrypoint, after REAPERAPI_LoadAPI has succeeded.
|
||||
// Registers our polling function with REAPER's "timer" callback.
|
||||
void RegisterTracking(reaper_plugin_info_t *rec);
|
||||
|
||||
// Applies delta to D_BASELINE on the currently-selected automation item (if
|
||||
// any). Shared by the manual nudge actions and socket.cpp's listener.
|
||||
void NudgeSelectedBaseline(double delta);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,26 @@
|
||||
* -text
|
||||
*.c text=auto
|
||||
*.cpp text=auto
|
||||
*.cc text=auto
|
||||
*.h text=auto
|
||||
*.hpp text=auto
|
||||
*.m text=auto
|
||||
*.mm text=auto
|
||||
|
||||
*.eel text=auto
|
||||
*.php text=auto
|
||||
*.txt text=auto
|
||||
|
||||
*.bat text eol=crlf
|
||||
*.cmd text eol=crlf
|
||||
|
||||
*.rc text eol=crlf
|
||||
*.dsp text eol=crlf
|
||||
*.dsw text eol=crlf
|
||||
*.sln text eol=crlf
|
||||
*.vcxproj text eol=crlf
|
||||
*.vcxproj.filters text eol=crlf
|
||||
|
||||
*.sh text eol=lf
|
||||
*.pbxproj text eol=lf
|
||||
Makefile text eol=lf
|
||||
@@ -0,0 +1,413 @@
|
||||
// Taken from http://www-personal.engin.umich.edu/~wagnerr/MersenneTwister.html
|
||||
|
||||
|
||||
// MersenneTwister.h
|
||||
// Mersenne Twister random number generator -- a C++ class MTRand
|
||||
// Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
|
||||
// Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
|
||||
|
||||
// The Mersenne Twister is an algorithm for generating random numbers. It
|
||||
// was designed with consideration of the flaws in various other generators.
|
||||
// The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
|
||||
// are far greater. The generator is also fast; it avoids multiplication and
|
||||
// division, and it benefits from caches and pipelines. For more information
|
||||
// see the inventors' web page at http://www.math.keio.ac.jp/~matumoto/emt.html
|
||||
|
||||
// Reference
|
||||
// M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
|
||||
// Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
|
||||
// Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
|
||||
|
||||
// Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
// Copyright (C) 2000 - 2003, Richard J. Wagner
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. The names of its contributors may not be used to endorse or promote
|
||||
// products derived from this software without specific prior written
|
||||
// permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// The original code included the following notice:
|
||||
//
|
||||
// When you use this, send an email to: matumoto@math.keio.ac.jp
|
||||
// with an appropriate reference to your work.
|
||||
//
|
||||
// It would be nice to CC: rjwagner@writeme.com and Cokus@math.washington.edu
|
||||
// when you write.
|
||||
|
||||
#ifndef _MERSENNETWISTER_H_
|
||||
#define _MERSENNETWISTER_H_
|
||||
|
||||
// Not thread safe (unless auto-initialization is avoided and each thread has
|
||||
// its own MTRand object)
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <math.h>
|
||||
|
||||
class MTRand {
|
||||
// Data
|
||||
public:
|
||||
typedef unsigned int uint32; // unsigned integer type, at least 32 bits
|
||||
|
||||
enum { N = 624 }; // length of state vector
|
||||
enum { SAVE = N + 1 }; // length of array for save()
|
||||
|
||||
protected:
|
||||
enum { M = 397 }; // period parameter
|
||||
|
||||
uint32 state[N]; // internal state
|
||||
uint32 *pNext; // next value to get from state
|
||||
int left; // number of values left before reload needed
|
||||
|
||||
|
||||
//Methods
|
||||
public:
|
||||
MTRand( const uint32& oneSeed ); // initialize with a simple uint32
|
||||
MTRand( uint32 *const bigSeed, uint32 const seedLength = N ); // or an array
|
||||
MTRand(); // auto-initialize with /dev/urandom or time() and clock()
|
||||
|
||||
// Do NOT use for CRYPTOGRAPHY without securely hashing several returned
|
||||
// values together, otherwise the generator state can be learned after
|
||||
// reading 624 consecutive values.
|
||||
|
||||
// Access to 32-bit random numbers
|
||||
double rand(); // real number in [0,1]
|
||||
double rand( const double& n ); // real number in [0,n]
|
||||
double randExc(); // real number in [0,1)
|
||||
double randExc( const double& n ); // real number in [0,n)
|
||||
double randDblExc(); // real number in (0,1)
|
||||
double randDblExc( const double& n ); // real number in (0,n)
|
||||
uint32 randInt(); // integer in [0,2^32-1]
|
||||
uint32 randInt( const uint32& n ); // integer in [0,n] for n < 2^32
|
||||
double operator()() { return rand(); } // same as rand()
|
||||
|
||||
// Access to 53-bit random numbers (capacity of IEEE double precision)
|
||||
double rand53(); // real number in [0,1)
|
||||
|
||||
// Access to nonuniform random number distributions
|
||||
double randNorm( const double& mean = 0.0, const double& variance = 0.0 );
|
||||
|
||||
// Re-seeding functions with same behavior as initializers
|
||||
void seed( const uint32 oneSeed );
|
||||
void seed( uint32 *const bigSeed, const uint32 seedLength = N );
|
||||
void seed();
|
||||
|
||||
// Saving and loading generator state
|
||||
void save( uint32* saveArray ) const; // to array of size SAVE
|
||||
void load( uint32 *const loadArray ); // from such array
|
||||
|
||||
protected:
|
||||
void initialize( const uint32 oneSeed );
|
||||
void reload();
|
||||
uint32 hiBit( const uint32& u ) const { return u & 0x80000000UL; }
|
||||
uint32 loBit( const uint32& u ) const { return u & 0x00000001UL; }
|
||||
uint32 loBits( const uint32& u ) const { return u & 0x7fffffffUL; }
|
||||
uint32 mixBits( const uint32& u, const uint32& v ) const
|
||||
{ return hiBit(u) | loBits(v); }
|
||||
uint32 twist( const uint32& m, const uint32& s0, const uint32& s1 ) const
|
||||
{ return m ^ (mixBits(s0,s1)>>1) ^ (-((int)loBit(s1)) & 0x9908b0dfUL); }
|
||||
|
||||
public:
|
||||
// This was protected, but we need it exposed so FIRan1.h can use it for seeding
|
||||
static uint32 hash( time_t t, clock_t c );
|
||||
};
|
||||
|
||||
|
||||
inline MTRand::MTRand( const uint32& oneSeed )
|
||||
{ seed(oneSeed); }
|
||||
|
||||
inline MTRand::MTRand( uint32 *const bigSeed, const uint32 seedLength )
|
||||
{ seed(bigSeed,seedLength); }
|
||||
|
||||
inline MTRand::MTRand()
|
||||
{ seed(); }
|
||||
|
||||
inline double MTRand::rand()
|
||||
{ return double(randInt()) * (1.0/4294967295.0); }
|
||||
|
||||
inline double MTRand::rand( const double& n )
|
||||
{ return rand() * n; }
|
||||
|
||||
inline double MTRand::randExc()
|
||||
{ return double(randInt()) * (1.0/4294967296.0); }
|
||||
|
||||
inline double MTRand::randExc( const double& n )
|
||||
{ return randExc() * n; }
|
||||
|
||||
inline double MTRand::randDblExc()
|
||||
{ return ( double(randInt()) + 0.5 ) * (1.0/4294967296.0); }
|
||||
|
||||
inline double MTRand::randDblExc( const double& n )
|
||||
{ return randDblExc() * n; }
|
||||
|
||||
inline double MTRand::rand53()
|
||||
{
|
||||
uint32 a = randInt() >> 5, b = randInt() >> 6;
|
||||
return ( a * 67108864.0 + b ) * (1.0/9007199254740992.0); // by Isaku Wada
|
||||
}
|
||||
|
||||
inline double MTRand::randNorm( const double& mean, const double& variance )
|
||||
{
|
||||
// Return a real number from a normal (Gaussian) distribution with given
|
||||
// mean and variance by Box-Muller method
|
||||
double r = sqrt( -2.0 * log( 1.0-randDblExc()) ) * variance;
|
||||
double phi = 2.0 * 3.14159265358979323846264338328 * randExc();
|
||||
return mean + r * cos(phi);
|
||||
}
|
||||
|
||||
inline MTRand::uint32 MTRand::randInt()
|
||||
{
|
||||
// Pull a 32-bit integer from the generator state
|
||||
// Every other access function simply transforms the numbers extracted here
|
||||
|
||||
if( left == 0 ) reload();
|
||||
--left;
|
||||
|
||||
uint32 s1;
|
||||
s1 = *pNext++;
|
||||
s1 ^= (s1 >> 11);
|
||||
s1 ^= (s1 << 7) & 0x9d2c5680UL;
|
||||
s1 ^= (s1 << 15) & 0xefc60000UL;
|
||||
return ( s1 ^ (s1 >> 18) );
|
||||
}
|
||||
|
||||
inline MTRand::uint32 MTRand::randInt( const uint32& n )
|
||||
{
|
||||
// Find which bits are used in n
|
||||
// Optimized by Magnus Jonsson (magnus@smartelectronix.com)
|
||||
uint32 used = n;
|
||||
used |= used >> 1;
|
||||
used |= used >> 2;
|
||||
used |= used >> 4;
|
||||
used |= used >> 8;
|
||||
used |= used >> 16;
|
||||
|
||||
// Draw numbers until one is found in [0,n]
|
||||
uint32 i;
|
||||
do
|
||||
i = randInt() & used; // toss unused bits to shorten search
|
||||
while( i > n );
|
||||
return i;
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::seed( const uint32 oneSeed )
|
||||
{
|
||||
// Seed the generator with a simple uint32
|
||||
initialize(oneSeed);
|
||||
reload();
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::seed( uint32 *const bigSeed, const uint32 seedLength )
|
||||
{
|
||||
// Seed the generator with an array of uint32's
|
||||
// There are 2^19937-1 possible initial states. This function allows
|
||||
// all of those to be accessed by providing at least 19937 bits (with a
|
||||
// default seed length of N = 624 uint32's). Any bits above the lower 32
|
||||
// in each element are discarded.
|
||||
// Just call seed() if you want to get array from /dev/urandom
|
||||
initialize(19650218UL);
|
||||
int i = 1;
|
||||
uint32 j = 0;
|
||||
int k = ( N > seedLength ? N : seedLength );
|
||||
for( ; k; --k )
|
||||
{
|
||||
state[i] =
|
||||
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1664525UL );
|
||||
state[i] += ( bigSeed[j] & 0xffffffffUL ) + j;
|
||||
state[i] &= 0xffffffffUL;
|
||||
++i; ++j;
|
||||
if( i >= N ) { state[0] = state[N-1]; i = 1; }
|
||||
if( j >= seedLength ) j = 0;
|
||||
}
|
||||
for( k = N - 1; k; --k )
|
||||
{
|
||||
state[i] =
|
||||
state[i] ^ ( (state[i-1] ^ (state[i-1] >> 30)) * 1566083941UL );
|
||||
state[i] -= i;
|
||||
state[i] &= 0xffffffffUL;
|
||||
++i;
|
||||
if( i >= N ) { state[0] = state[N-1]; i = 1; }
|
||||
}
|
||||
state[0] = 0x80000000UL; // MSB is 1, assuring non-zero initial array
|
||||
reload();
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::seed()
|
||||
{
|
||||
// Seed the generator with an array from /dev/urandom if available
|
||||
// Otherwise use a hash of time() and clock() values
|
||||
|
||||
// No point in trying this on Windows machines - won't work, so it just slows things down
|
||||
#ifndef WIN32
|
||||
#ifndef WDL_MTRAND_FASTSEED
|
||||
// First try getting an array from /dev/urandom
|
||||
FILE* urandom = fopen( "/dev/urandom", "rb" );
|
||||
if( urandom )
|
||||
{
|
||||
uint32 bigSeed[N];
|
||||
uint32 *s = bigSeed;
|
||||
int i = N;
|
||||
bool success = true;
|
||||
while( success && i-- )
|
||||
success = fread( s++, sizeof(uint32), 1, urandom );
|
||||
fclose(urandom);
|
||||
if( success ) { seed( bigSeed, N ); return; }
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Was not successful, so use time() and clock() instead
|
||||
seed( hash( time(NULL), clock() ) );
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::initialize( const uint32 seedv )
|
||||
{
|
||||
// Initialize generator state with seed
|
||||
// See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
|
||||
// In previous versions, most significant bits (MSBs) of the seed affect
|
||||
// only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto.
|
||||
uint32 *s = state;
|
||||
uint32 *r = state;
|
||||
int i = 1;
|
||||
*s++ = seedv & 0xffffffffUL;
|
||||
for( ; i < N; ++i )
|
||||
{
|
||||
*s++ = ( 1812433253UL * ( *r ^ (*r >> 30) ) + i ) & 0xffffffffUL;
|
||||
r++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::reload()
|
||||
{
|
||||
// Generate N new values in state
|
||||
// Made clearer and faster by Matthew Bellew (matthew.bellew@home.com)
|
||||
uint32 *p = state;
|
||||
int i;
|
||||
for( i = int(N) - int(M); i--; ++p )
|
||||
*p = twist( p[M], p[0], p[1] );
|
||||
for( i = M; --i; ++p )
|
||||
*p = twist( p[int(M)-int(N)], p[0], p[1] );
|
||||
*p = twist( p[int(M)-int(N)], p[0], state[0] );
|
||||
|
||||
left = N, pNext = state;
|
||||
}
|
||||
|
||||
|
||||
inline MTRand::uint32 MTRand::hash( time_t t, clock_t c )
|
||||
{
|
||||
// Get a uint32 from t and c
|
||||
// Better than uint32(x) in case x is floating point in [0,1]
|
||||
// Based on code by Lawrence Kirby (fred@genesis.demon.co.uk)
|
||||
|
||||
static uint32 differ = 0; // guarantee time-based seeds will change
|
||||
|
||||
uint32 h1 = 0;
|
||||
unsigned char *p = (unsigned char *) &t;
|
||||
for( size_t i = 0; i < sizeof(t); ++i )
|
||||
{
|
||||
h1 *= UCHAR_MAX + 2U;
|
||||
h1 += p[i];
|
||||
}
|
||||
uint32 h2 = 0;
|
||||
p = (unsigned char *) &c;
|
||||
for( size_t j = 0; j < sizeof(c); ++j )
|
||||
{
|
||||
h2 *= UCHAR_MAX + 2U;
|
||||
h2 += p[j];
|
||||
}
|
||||
return ( h1 + differ++ ) ^ h2;
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::save( uint32* saveArray ) const
|
||||
{
|
||||
uint32 *sa = saveArray;
|
||||
const uint32 *s = state;
|
||||
int i = N;
|
||||
for( ; i--; *sa++ = *s++ ) {}
|
||||
*sa = left;
|
||||
}
|
||||
|
||||
|
||||
inline void MTRand::load( uint32 *const loadArray )
|
||||
{
|
||||
uint32 *s = state;
|
||||
uint32 *la = loadArray;
|
||||
int i = N;
|
||||
for( ; i--; *s++ = *la++ ) {}
|
||||
left = *la;
|
||||
pNext = &state[N-left];
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // MERSENNETWISTER_H
|
||||
|
||||
// Change log:
|
||||
//
|
||||
// v0.1 - First release on 15 May 2000
|
||||
// - Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
|
||||
// - Translated from C to C++
|
||||
// - Made completely ANSI compliant
|
||||
// - Designed convenient interface for initialization, seeding, and
|
||||
// obtaining numbers in default or user-defined ranges
|
||||
// - Added automatic seeding from /dev/urandom or time() and clock()
|
||||
// - Provided functions for saving and loading generator state
|
||||
//
|
||||
// v0.2 - Fixed bug which reloaded generator one step too late
|
||||
//
|
||||
// v0.3 - Switched to clearer, faster reload() code from Matthew Bellew
|
||||
//
|
||||
// v0.4 - Removed trailing newline in saved generator format to be consistent
|
||||
// with output format of built-in types
|
||||
//
|
||||
// v0.5 - Improved portability by replacing static const int's with enum's and
|
||||
// clarifying return values in seed(); suggested by Eric Heimburg
|
||||
// - Removed MAXINT constant; use 0xffffffffUL instead
|
||||
//
|
||||
// v0.6 - Eliminated seed overflow when uint32 is larger than 32 bits
|
||||
// - Changed integer [0,n] generator to give better uniformity
|
||||
//
|
||||
// v0.7 - Fixed operator precedence ambiguity in reload()
|
||||
// - Added access for real numbers in (0,1) and (0,n)
|
||||
//
|
||||
// v0.8 - Included time.h header to properly support time_t and clock_t
|
||||
//
|
||||
// v1.0 - Revised seeding to match 26 Jan 2002 update of Nishimura and Matsumoto
|
||||
// - Allowed for seeding with arrays of any length
|
||||
// - Added access for real numbers in [0,1) with 53-bit resolution
|
||||
// - Added access for real numbers from normal (Gaussian) distributions
|
||||
// - Increased overall speed by optimizing twist()
|
||||
// - Doubled speed of integer [0,n] generation
|
||||
// - Fixed out-of-range number generation on 64-bit machines
|
||||
// - Improved portability by substituting literal constants for long enum's
|
||||
// - Changed license from GNU LGPL to BSD
|
||||
@@ -0,0 +1,319 @@
|
||||
#ifndef _WDL_ADPCM_DECODE_H_
|
||||
#define _WDL_ADPCM_DECODE_H_
|
||||
|
||||
#include "queue.h"
|
||||
|
||||
#define MSADPCM_TYPE 2
|
||||
#define IMAADPCM_TYPE 0x11
|
||||
#define CADPCM2_TYPE 0xac0c
|
||||
|
||||
class WDL_adpcm_decoder
|
||||
{
|
||||
typedef struct
|
||||
{
|
||||
int cf1,cf2,deltas,spl1,spl2;
|
||||
} WDL_adpcm_decode_chanctx;
|
||||
|
||||
public:
|
||||
enum { MSADPCM_PREAMBLELEN=7, IMA_PREAMBLELEN=4 };
|
||||
static INT64 sampleLengthFromBytes(INT64 nbytes, int blockalign, int nch, int type, int bps)
|
||||
{
|
||||
if (!bps||type!=CADPCM2_TYPE) bps=4;
|
||||
// remove overhead of headers
|
||||
INT64 nblocks=((nbytes+blockalign-1)/blockalign);
|
||||
|
||||
// remove preambles
|
||||
if (type==IMAADPCM_TYPE||type==CADPCM2_TYPE) nbytes -= nblocks*IMA_PREAMBLELEN*nch;
|
||||
else nbytes -= nblocks*MSADPCM_PREAMBLELEN*nch;
|
||||
|
||||
// scale from bytes to samples
|
||||
nbytes = (nbytes*8)/(nch*bps);
|
||||
|
||||
if (type==IMAADPCM_TYPE||type==CADPCM2_TYPE) nbytes++; // IMA has just one initial sample
|
||||
else nbytes+=2; // msadpcm has 2 initial sample values
|
||||
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
WDL_adpcm_decoder(int blockalign,int nch, int type, int bps)
|
||||
{
|
||||
m_bps=0;
|
||||
m_type=0;
|
||||
m_nch=0;
|
||||
m_blockalign=0;
|
||||
m_srcbuf=0;
|
||||
m_chans=0;
|
||||
setParameters(blockalign,nch,type,bps);
|
||||
}
|
||||
~WDL_adpcm_decoder()
|
||||
{
|
||||
free(m_srcbuf);
|
||||
free(m_chans);
|
||||
}
|
||||
|
||||
|
||||
void resetState()
|
||||
{
|
||||
if (m_chans) memset(m_chans,0,m_nch*sizeof(WDL_adpcm_decode_chanctx));
|
||||
m_srcbuf_valid=0;
|
||||
samplesOut.Clear();
|
||||
samplesOut.Compact();
|
||||
}
|
||||
|
||||
void setParameters(int ba, int nch, int type, int bps)
|
||||
{
|
||||
if (m_blockalign != ba||nch != m_nch||type!=m_type||bps != m_bps)
|
||||
{
|
||||
free(m_srcbuf);
|
||||
free(m_chans);
|
||||
m_bps=bps;
|
||||
m_blockalign=ba;
|
||||
m_nch=nch;
|
||||
m_srcbuf_valid=0;
|
||||
m_srcbuf=(unsigned char*)malloc(ba);
|
||||
m_chans=(WDL_adpcm_decode_chanctx*)malloc(sizeof(WDL_adpcm_decode_chanctx)*nch);
|
||||
m_type=type;
|
||||
resetState();
|
||||
}
|
||||
}
|
||||
|
||||
int blockAlign() { return m_blockalign; }
|
||||
|
||||
int samplesPerBlock()
|
||||
{
|
||||
if (m_type==IMAADPCM_TYPE||m_type==CADPCM2_TYPE)
|
||||
{
|
||||
if (m_bps == 2) return (m_blockalign/m_nch - IMA_PREAMBLELEN)*4 + 1;
|
||||
return (m_blockalign/m_nch - IMA_PREAMBLELEN)*2 + 1; //4 bit
|
||||
}
|
||||
return (m_blockalign/m_nch - MSADPCM_PREAMBLELEN)*2 + 2; // 4 bit
|
||||
}
|
||||
|
||||
INT64 samplesToSourceBytes(INT64 outlen_samples) // length in samplepairs
|
||||
{
|
||||
outlen_samples -= samplesOut.Available()/m_nch;
|
||||
if (outlen_samples<1) return 0; // no data required
|
||||
|
||||
int spls_block = samplesPerBlock();
|
||||
if (spls_block<1) return 0;
|
||||
INT64 nblocks = (outlen_samples+spls_block-1)/spls_block;
|
||||
INT64 v=nblocks * m_blockalign;
|
||||
|
||||
v -= m_srcbuf_valid;
|
||||
|
||||
return wdl_max(v,0);
|
||||
}
|
||||
|
||||
void AddInput(void *buf, int len, short *parm_cotab=NULL)
|
||||
{
|
||||
unsigned char *rdbuf = (unsigned char *)buf;
|
||||
if (m_srcbuf_valid)
|
||||
{
|
||||
int v=m_blockalign-m_srcbuf_valid;
|
||||
if (v>len) v=len;
|
||||
|
||||
memcpy(m_srcbuf+m_srcbuf_valid,rdbuf,v);
|
||||
len-=v;
|
||||
rdbuf+=v;
|
||||
if ((m_srcbuf_valid+=v)>=m_blockalign)
|
||||
{
|
||||
DecodeBlock(m_srcbuf,parm_cotab);
|
||||
m_srcbuf_valid=0;
|
||||
}
|
||||
}
|
||||
|
||||
while (len >= m_blockalign)
|
||||
{
|
||||
DecodeBlock(rdbuf,parm_cotab);
|
||||
rdbuf+=m_blockalign;
|
||||
len-=m_blockalign;
|
||||
}
|
||||
if (len>0) memcpy(m_srcbuf,rdbuf,m_srcbuf_valid=len);
|
||||
}
|
||||
|
||||
|
||||
int sourceBytesQueued() { return m_srcbuf_valid; }
|
||||
WDL_TypedQueue<short> samplesOut;
|
||||
|
||||
private:
|
||||
static int getwordsigned(unsigned char **rdptr)
|
||||
{
|
||||
int s = (*rdptr)[0] + ((*rdptr)[1]<<8);
|
||||
(*rdptr)+=2;
|
||||
if (s & 0x8000) s -= 0x10000;
|
||||
return s;
|
||||
}
|
||||
|
||||
bool DecodeBlockIMA(unsigned char *buf)
|
||||
{
|
||||
int samples_block = samplesPerBlock();
|
||||
|
||||
int nch=m_nch;
|
||||
int ch;
|
||||
short *outptr = samplesOut.Add(NULL,samples_block * nch);
|
||||
|
||||
for (ch=0;ch<nch;ch++)
|
||||
{
|
||||
m_chans[ch].spl1 = getwordsigned(&buf);
|
||||
m_chans[ch].cf1 = buf[0] | (buf[1]<<8);
|
||||
buf+=2;
|
||||
}
|
||||
|
||||
for (ch=0;ch<nch;ch++) *outptr++ = m_chans[ch].spl1;
|
||||
|
||||
char bstate=0;
|
||||
unsigned char lastbyte=0;
|
||||
int x;
|
||||
|
||||
static signed char index_table[8] = { -1, -1, -1, -1, 2, 4, 6, 8 };
|
||||
static short step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
|
||||
int splcnt = (samples_block-1)*nch;
|
||||
int cnt=0;
|
||||
ch=0;
|
||||
int wrpos = 0;
|
||||
|
||||
int bps=m_bps;
|
||||
const int chunksize = bps == 2 ? 16 : 8;
|
||||
|
||||
for (x=0; x < splcnt; x++)
|
||||
{
|
||||
int nib;
|
||||
|
||||
if (bps==2)
|
||||
{
|
||||
switch (bstate++)
|
||||
{
|
||||
case 0: nib=(lastbyte=*buf++)<<2; break;
|
||||
case 1: nib=lastbyte; break;
|
||||
case 2: nib=lastbyte>>2; break;
|
||||
default: nib=lastbyte>>4; bstate=0; break;
|
||||
}
|
||||
nib &= 8|4;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((bstate^=1)) nib=(lastbyte=*buf++)&0xf;
|
||||
else nib=lastbyte>>4;
|
||||
}
|
||||
|
||||
int step_index=m_chans[ch].cf1;
|
||||
if (step_index<0)step_index=0;
|
||||
else if (step_index>88)step_index=88;
|
||||
|
||||
int step=step_table[step_index];
|
||||
|
||||
int diff = ((nib&7)*step)/4 + step/8;
|
||||
|
||||
int v=m_chans[ch].spl1 + ((nib&8) ? -diff : diff);
|
||||
|
||||
if (v<-32768)v=-32768;
|
||||
else if (v>32767)v=32767;
|
||||
|
||||
outptr[wrpos]=(short)v;
|
||||
wrpos+=nch;
|
||||
|
||||
m_chans[ch].spl1=v;
|
||||
|
||||
m_chans[ch].cf1=step_index + index_table[nib&7];
|
||||
|
||||
|
||||
// advance channelcounts
|
||||
if (++cnt==chunksize)
|
||||
{
|
||||
if (++ch>=nch)
|
||||
{
|
||||
ch=0;
|
||||
outptr += chunksize*nch;
|
||||
}
|
||||
wrpos = ch;
|
||||
cnt=0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DecodeBlock(unsigned char *buf, short *parm_cotab=NULL)
|
||||
{
|
||||
if (m_type==IMAADPCM_TYPE||m_type==CADPCM2_TYPE) return DecodeBlockIMA(buf);
|
||||
static short cotab[14] = { 256,0, 512,-256, 0,0, 192,64, 240, 0,460, -208, 392, -232 };
|
||||
static short adtab[16] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 };
|
||||
|
||||
short *use_cotab = parm_cotab ? parm_cotab : cotab;
|
||||
int nch = m_nch;
|
||||
int ch;
|
||||
for(ch=0;ch<nch;ch++)
|
||||
{
|
||||
unsigned char c=*buf++;
|
||||
if (c > 6) return false;
|
||||
c*=2;
|
||||
m_chans[ch].cf1 = use_cotab[c];
|
||||
m_chans[ch].cf2 = use_cotab[c+1];
|
||||
}
|
||||
for(ch=0;ch<nch;ch++) m_chans[ch].deltas = getwordsigned(&buf);
|
||||
for(ch=0;ch<nch;ch++) m_chans[ch].spl1 = getwordsigned(&buf);
|
||||
for(ch=0;ch<nch;ch++) m_chans[ch].spl2 = getwordsigned(&buf);
|
||||
|
||||
int samples_block = samplesPerBlock();
|
||||
|
||||
short *outptr = samplesOut.Add(NULL,samples_block * nch);
|
||||
|
||||
for(ch=0;ch<nch;ch++) *outptr++ = m_chans[ch].spl2;
|
||||
for(ch=0;ch<nch;ch++) *outptr++ = m_chans[ch].spl1;
|
||||
|
||||
int x;
|
||||
char bstate=0;
|
||||
unsigned char lastbyte;
|
||||
for (x=2; x < samples_block; x++)
|
||||
{
|
||||
for(ch=0;ch<nch;ch++)
|
||||
{
|
||||
int nib;
|
||||
if ((bstate^=1)) nib=(lastbyte=*buf++)>>4;
|
||||
else nib=lastbyte&0xf;
|
||||
|
||||
int sn=nib;
|
||||
if (sn & 8) sn -= 16;
|
||||
|
||||
int pred = ( ((m_chans[ch].spl1 * m_chans[ch].cf1) +
|
||||
(m_chans[ch].spl2 * m_chans[ch].cf2)) / 256) +
|
||||
(sn * m_chans[ch].deltas);
|
||||
|
||||
m_chans[ch].spl2 = m_chans[ch].spl1;
|
||||
|
||||
if (pred < -32768) pred=-32768;
|
||||
else if (pred > 32767) pred=32767;
|
||||
|
||||
*outptr++ = m_chans[ch].spl1 = pred;
|
||||
|
||||
int i= (adtab[nib] * m_chans[ch].deltas) / 256;
|
||||
if (i <= 16) m_chans[ch].deltas=16;
|
||||
else m_chans[ch].deltas = i;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
WDL_adpcm_decode_chanctx *m_chans;
|
||||
unsigned char *m_srcbuf;
|
||||
int m_srcbuf_valid;
|
||||
|
||||
int m_blockalign,m_nch,m_type,m_bps;
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,137 @@
|
||||
#ifndef _WDL_ADPCM_ENCODE_H_
|
||||
#define _WDL_ADPCM_ENCODE_H_
|
||||
|
||||
|
||||
#include "pcmfmtcvt.h"
|
||||
|
||||
void WDL_adpcm_encode_IMA(PCMFMTCVT_DBL_TYPE *samples, int numsamples, int nch, int bps,
|
||||
unsigned char *bufout, int *bufout_used, short **predState);
|
||||
|
||||
#define WDL_adpcm_encode_IMA_samplesneededbytes(bytes,bps) ((((bytes)-4)*8)/(bps)+1)
|
||||
|
||||
|
||||
// untested. also probably slow.
|
||||
#ifdef WDL_ADPCM_ENCODE_IMPL
|
||||
|
||||
|
||||
static signed char ima_adpcm_index_table[8] = { -1, -1, -1, -1, 2, 4, 6, 8, };
|
||||
static short ima_adpcm_step_table[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14, 16, 17,
|
||||
19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
|
||||
50, 55, 60, 66, 73, 80, 88, 97, 107, 118,
|
||||
130, 143, 157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658, 724, 796,
|
||||
876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066,
|
||||
2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358,
|
||||
5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899,
|
||||
15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
|
||||
};
|
||||
|
||||
static char calcBestNibble(int *initial_step_index, int lastSpl, int thisSpl, short *lastsplout, int bps)
|
||||
{
|
||||
|
||||
int step=ima_adpcm_step_table[*initial_step_index];
|
||||
|
||||
char sign=0;
|
||||
int adiff = thisSpl - lastSpl;
|
||||
if (adiff<0) { adiff=-adiff; sign=8; }
|
||||
|
||||
// adiff == (nib*step)/4 + step/8
|
||||
// adiff - step/8 = nib*step/4
|
||||
// nib = 4*(adiff-step/8)/step = 4*adiff/step - 0.5
|
||||
int nib = step ? ((4 * adiff - step/2) / step) : 0;
|
||||
if (nib<0) nib=0;
|
||||
else if(nib>7) nib=7;
|
||||
|
||||
if (bps==2) nib&=4;
|
||||
|
||||
int diff = (nib*step)/4 + step/8;
|
||||
*lastsplout = lastSpl + (sign?-diff:diff);
|
||||
|
||||
|
||||
*initial_step_index += ima_adpcm_index_table[nib];
|
||||
|
||||
if (*initial_step_index<0)*initial_step_index=0;
|
||||
else if (*initial_step_index>88)*initial_step_index=88;
|
||||
|
||||
return (char)nib|sign;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WDL_adpcm_encode_IMA(PCMFMTCVT_DBL_TYPE *samples, int numsamples, int nch, int bps,
|
||||
unsigned char *bufout, int *bufout_used, short **predState)
|
||||
{
|
||||
int x;
|
||||
if (!*predState) *predState=(short *)calloc(nch,sizeof(short));
|
||||
|
||||
short *pstate = *predState;
|
||||
for(x=0;x<nch;x++)
|
||||
{
|
||||
int left=numsamples;
|
||||
PCMFMTCVT_DBL_TYPE *spl = samples+x;
|
||||
unsigned char *wrptr = bufout + x*4;
|
||||
|
||||
int step_index=pstate[x];
|
||||
|
||||
short last_out;
|
||||
double_TO_INT16(last_out,*spl);
|
||||
left--;
|
||||
|
||||
*wrptr++ = last_out&0xff; *wrptr++ = last_out>>8;
|
||||
spl+=nch;
|
||||
|
||||
*wrptr++ = step_index&0xff; *wrptr++ = step_index>>8;
|
||||
|
||||
wrptr += (nch-1)*4;
|
||||
|
||||
|
||||
int outpos=0;
|
||||
const int outblocklen = bps == 2 ? 16 : 8;
|
||||
unsigned char buildchar=0;
|
||||
|
||||
while (left-->0)
|
||||
{
|
||||
short this_spl;
|
||||
double_TO_INT16(this_spl,*spl);
|
||||
char nib = calcBestNibble(&step_index,last_out,this_spl,&last_out,bps);
|
||||
|
||||
spl+=nch;
|
||||
|
||||
// update output
|
||||
if (bps == 2)
|
||||
{
|
||||
nib>>=2;
|
||||
switch (outpos&3)
|
||||
{
|
||||
case 0: buildchar = nib; break;
|
||||
case 1: buildchar |= nib<<2; break;
|
||||
case 2: buildchar |= nib<<4; break;
|
||||
case 3: *wrptr++ = buildchar | (nib<<6); break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(outpos&1)) buildchar = nib;
|
||||
else *wrptr++ = buildchar | (nib<<4);
|
||||
}
|
||||
|
||||
// skip other channels
|
||||
if (++outpos == outblocklen)
|
||||
{
|
||||
wrptr += ((nch-1)*outblocklen*bps)/8;
|
||||
outpos=0;
|
||||
}
|
||||
}
|
||||
pstate[x] = step_index;
|
||||
|
||||
}
|
||||
*bufout_used = (((numsamples-1)*bps)/8 + 4)*nch;
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif//_WDL_ADPCM_ENCODE_H_
|
||||
+531
@@ -0,0 +1,531 @@
|
||||
#ifndef _WDL_ASSOCARRAY_H_
|
||||
#define _WDL_ASSOCARRAY_H_
|
||||
|
||||
#include "heapbuf.h"
|
||||
#include "mergesort.h"
|
||||
#include "wdlcstring.h"
|
||||
|
||||
template<class T> static int WDL_assocarray_cmp(const T *a, const T *b) { return *a > *b ? 1 : *a < *b ? -1 : 0; }
|
||||
template<class T> static int WDL_assocarray_cmpmem(const T *a, const T *b) { return memcmp(a,b,sizeof(*a)); }
|
||||
template<class T> static int WDL_assocarray_cmpstr(T * const *a, T * const *b) { return strcmp(*a,*b); }
|
||||
template<class T> static int WDL_assocarray_cmpistr(T * const *a, T * const *b) { return stricmp(*a,*b); }
|
||||
|
||||
// on all of these, if valdispose is set, the array will dispose of values as needed.
|
||||
// if keydup/keydispose are set, copies of (any) key data will be made/destroyed as necessary
|
||||
|
||||
|
||||
// WDL_AssocArrayImpl can be used on its own, and can contain structs for keys or values
|
||||
template <class KEY, class VAL> class WDL_AssocArrayImpl
|
||||
{
|
||||
WDL_AssocArrayImpl(const WDL_AssocArrayImpl &cp) { CopyContents(cp); }
|
||||
|
||||
WDL_AssocArrayImpl &operator=(const WDL_AssocArrayImpl &cp) { CopyContents(cp); return *this; }
|
||||
|
||||
public:
|
||||
|
||||
explicit WDL_AssocArrayImpl(int (*keycmp)(const KEY *k1, const KEY *k2),
|
||||
KEY (*keydup)(KEY)=NULL,
|
||||
void (*keydispose)(KEY)=NULL,
|
||||
void (*valdispose)(VAL)=NULL)
|
||||
{
|
||||
m_keycmp = keycmp;
|
||||
m_keydup = keydup;
|
||||
m_keydispose = keydispose;
|
||||
m_valdispose = valdispose;
|
||||
}
|
||||
|
||||
~WDL_AssocArrayImpl()
|
||||
{
|
||||
DeleteAll();
|
||||
}
|
||||
|
||||
void Prealloc(int sz) { m_data.Prealloc(sz*sizeof(KeyVal)); }
|
||||
|
||||
VAL* GetPtr(KEY key, KEY *keyPtrOut=NULL) const
|
||||
{
|
||||
bool ismatch = false;
|
||||
int i = LowerBound(key, &ismatch);
|
||||
if (ismatch)
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (keyPtrOut) *keyPtrOut = kv->key;
|
||||
return &(kv->val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Exists(KEY key) const
|
||||
{
|
||||
bool ismatch = false;
|
||||
LowerBound(key, &ismatch);
|
||||
return ismatch;
|
||||
}
|
||||
|
||||
int Insert(KEY key, VAL val)
|
||||
{
|
||||
bool ismatch = false;
|
||||
int i = LowerBound(key, &ismatch);
|
||||
if (ismatch)
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (m_valdispose) m_valdispose(kv->val);
|
||||
kv->val = val;
|
||||
}
|
||||
else
|
||||
{
|
||||
KeyVal *kv = m_data.ResizeOK(m_data.GetSize()+1);
|
||||
if (WDL_NORMALLY(kv != NULL))
|
||||
{
|
||||
memmove(kv+i+1, kv+i, (m_data.GetSize()-i-1)*sizeof(KeyVal));
|
||||
if (m_keydup) key = m_keydup(key);
|
||||
kv[i].key = key;
|
||||
kv[i].val = val;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
void Delete(KEY key)
|
||||
{
|
||||
bool ismatch = false;
|
||||
int i = LowerBound(key, &ismatch);
|
||||
if (ismatch)
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (m_keydispose) m_keydispose(kv->key);
|
||||
if (m_valdispose) m_valdispose(kv->val);
|
||||
m_data.Delete(i);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteByIndex(int idx)
|
||||
{
|
||||
if (idx >= 0 && idx < m_data.GetSize())
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+idx;
|
||||
if (m_keydispose) m_keydispose(kv->key);
|
||||
if (m_valdispose) m_valdispose(kv->val);
|
||||
m_data.Delete(idx);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteAll(bool resizedown=false)
|
||||
{
|
||||
if (m_keydispose || m_valdispose)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < m_data.GetSize(); ++i)
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (m_keydispose) m_keydispose(kv->key);
|
||||
if (m_valdispose) m_valdispose(kv->val);
|
||||
}
|
||||
}
|
||||
m_data.Resize(0, resizedown);
|
||||
}
|
||||
|
||||
int GetSize() const
|
||||
{
|
||||
return m_data.GetSize();
|
||||
}
|
||||
|
||||
VAL* EnumeratePtr(int i, KEY* key=NULL) const
|
||||
{
|
||||
if (i >= 0 && i < m_data.GetSize())
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (key) *key = kv->key;
|
||||
return &(kv->val);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
KEY* ReverseLookupPtr(VAL val) const
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < m_data.GetSize(); ++i)
|
||||
{
|
||||
KeyVal* kv = m_data.Get()+i;
|
||||
if (kv->val == val) return &kv->key;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ChangeKey(KEY oldkey, KEY newkey)
|
||||
{
|
||||
bool ismatch=false;
|
||||
int i=LowerBound(oldkey, &ismatch);
|
||||
if (ismatch) ChangeKeyByIndex(i, newkey, true);
|
||||
}
|
||||
|
||||
void ChangeKeyByIndex(int idx, KEY newkey, bool needsort)
|
||||
{
|
||||
if (idx >= 0 && idx < m_data.GetSize())
|
||||
{
|
||||
KeyVal* kv=m_data.Get()+idx;
|
||||
if (!needsort)
|
||||
{
|
||||
if (m_keydispose) m_keydispose(kv->key);
|
||||
if (m_keydup) newkey=m_keydup(newkey);
|
||||
kv->key=newkey;
|
||||
}
|
||||
else
|
||||
{
|
||||
VAL val=kv->val;
|
||||
m_data.Delete(idx);
|
||||
Insert(newkey, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fast add-block mode
|
||||
void AddUnsorted(KEY key, VAL val)
|
||||
{
|
||||
int i=m_data.GetSize();
|
||||
KeyVal *kv = m_data.ResizeOK(i+1);
|
||||
if (WDL_NORMALLY(kv != NULL))
|
||||
{
|
||||
if (m_keydup) key = m_keydup(key);
|
||||
kv[i].key = key;
|
||||
kv[i].val = val;
|
||||
}
|
||||
}
|
||||
|
||||
void Resort(int (*new_keycmp)(const KEY *k1, const KEY *k2)=NULL)
|
||||
{
|
||||
if (new_keycmp) m_keycmp = new_keycmp;
|
||||
if (m_data.GetSize() > 1 && m_keycmp)
|
||||
{
|
||||
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
|
||||
(int(*)(const void*, const void*))m_keycmp);
|
||||
if (!new_keycmp)
|
||||
RemoveDuplicateKeys();
|
||||
}
|
||||
}
|
||||
|
||||
void ResortStable()
|
||||
{
|
||||
if (m_data.GetSize() > 1 && m_keycmp)
|
||||
{
|
||||
char *tmp=(char*)malloc(m_data.GetSize()*sizeof(KeyVal));
|
||||
if (WDL_NORMALLY(tmp))
|
||||
{
|
||||
WDL_mergesort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
|
||||
(int(*)(const void*, const void*))m_keycmp, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
qsort(m_data.Get(), m_data.GetSize(), sizeof(KeyVal),
|
||||
(int(*)(const void*, const void*))m_keycmp);
|
||||
}
|
||||
|
||||
RemoveDuplicateKeys();
|
||||
}
|
||||
}
|
||||
|
||||
int LowerBound(KEY key, bool* ismatch) const
|
||||
{
|
||||
int a = 0;
|
||||
int c = m_data.GetSize();
|
||||
while (a != c)
|
||||
{
|
||||
int b = (a+c)/2;
|
||||
KeyVal* kv=m_data.Get()+b;
|
||||
int cmp = m_keycmp(&key, &kv->key);
|
||||
if (cmp > 0) a = b+1;
|
||||
else if (cmp < 0) c = b;
|
||||
else
|
||||
{
|
||||
*ismatch = true;
|
||||
return b;
|
||||
}
|
||||
}
|
||||
*ismatch = false;
|
||||
return a;
|
||||
}
|
||||
|
||||
int GetIdx(KEY key) const
|
||||
{
|
||||
bool ismatch=false;
|
||||
int i = LowerBound(key, &ismatch);
|
||||
if (ismatch) return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
void SetGranul(int gran)
|
||||
{
|
||||
m_data.SetGranul(gran);
|
||||
}
|
||||
|
||||
void CopyContents(const WDL_AssocArrayImpl &cp)
|
||||
{
|
||||
m_data=cp.m_data;
|
||||
m_keycmp = cp.m_keycmp;
|
||||
m_keydup = cp.m_keydup;
|
||||
m_keydispose = m_keydup ? cp.m_keydispose : NULL;
|
||||
m_valdispose = NULL; // avoid disposing of values twice, since we don't have a valdup, we can't have a fully valid copy
|
||||
if (m_keydup)
|
||||
{
|
||||
const int n=m_data.GetSize();
|
||||
for (int x=0;x<n;x++)
|
||||
{
|
||||
KeyVal *kv=m_data.Get()+x;
|
||||
kv->key = m_keydup(kv->key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CopyContentsAsReference(const WDL_AssocArrayImpl &cp)
|
||||
{
|
||||
DeleteAll(true);
|
||||
m_keycmp = cp.m_keycmp;
|
||||
m_keydup = NULL; // this no longer can own any data
|
||||
m_keydispose = NULL;
|
||||
m_valdispose = NULL;
|
||||
|
||||
m_data=cp.m_data;
|
||||
}
|
||||
|
||||
|
||||
// private data, but exposed in case the caller wants to manipulate at its own risk
|
||||
struct KeyVal
|
||||
{
|
||||
KEY key;
|
||||
VAL val;
|
||||
};
|
||||
WDL_TypedBuf<KeyVal> m_data;
|
||||
|
||||
// for (const auto &a : list) { a.key, a.val }
|
||||
const KeyVal *begin() const { return m_data.begin(); }
|
||||
const KeyVal *end() const { return m_data.end(); }
|
||||
|
||||
// should be careful if modifying keys, and Resort() after
|
||||
KeyVal *begin() { return m_data.begin(); }
|
||||
KeyVal *end() { return m_data.end(); }
|
||||
|
||||
protected:
|
||||
|
||||
int (*m_keycmp)(const KEY *k1, const KEY *k2);
|
||||
KEY (*m_keydup)(KEY);
|
||||
void (*m_keydispose)(KEY);
|
||||
void (*m_valdispose)(VAL);
|
||||
|
||||
private:
|
||||
|
||||
void RemoveDuplicateKeys() // after resorting
|
||||
{
|
||||
const int sz = m_data.GetSize();
|
||||
|
||||
int cnt = 1;
|
||||
KeyVal *rd = m_data.Get() + 1, *wr = rd;
|
||||
for (int x = 1; x < sz; x ++)
|
||||
{
|
||||
if (m_keycmp(&rd->key, &wr[-1].key))
|
||||
{
|
||||
if (rd != wr) *wr=*rd;
|
||||
wr++;
|
||||
cnt++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_keydispose) m_keydispose(rd->key);
|
||||
if (m_valdispose) m_valdispose(rd->val);
|
||||
}
|
||||
rd++;
|
||||
}
|
||||
if (cnt < sz) m_data.Resize(cnt,false);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// WDL_AssocArray adds useful functions but requires assignment operator for keys and values
|
||||
template <class KEY, class VAL> class WDL_AssocArray : public WDL_AssocArrayImpl<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit WDL_AssocArray(int (*keycmp)(const KEY *k1, const KEY *k2),
|
||||
KEY (*keydup)(KEY)=NULL,
|
||||
void (*keydispose)(KEY)=NULL, void (*valdispose)(VAL)=NULL)
|
||||
: WDL_AssocArrayImpl<KEY, VAL>(keycmp, keydup, keydispose, valdispose)
|
||||
{
|
||||
}
|
||||
|
||||
VAL Get(KEY key, VAL notfound=0) const
|
||||
{
|
||||
VAL* p = this->GetPtr(key);
|
||||
if (p) return *p;
|
||||
return notfound;
|
||||
}
|
||||
|
||||
VAL Enumerate(int i, KEY* key=NULL, VAL notfound=0) const
|
||||
{
|
||||
VAL* p = this->EnumeratePtr(i, key);
|
||||
if (p) return *p;
|
||||
return notfound;
|
||||
}
|
||||
|
||||
KEY ReverseLookup(VAL val, KEY notfound=0) const
|
||||
{
|
||||
KEY* p=this->ReverseLookupPtr(val);
|
||||
if (p) return *p;
|
||||
return notfound;
|
||||
}
|
||||
};
|
||||
|
||||
template <class KEY, class VAL> class WDL_KeyedArray : public WDL_AssocArray<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_KeyedArray(void (*valdispose)(VAL)=NULL)
|
||||
: WDL_AssocArray<KEY, VAL>(WDL_assocarray_cmp<KEY>, NULL, NULL, valdispose)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class KEY, class VAL> class WDL_KeyedArrayImpl : public WDL_AssocArrayImpl<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_KeyedArrayImpl(void (*valdispose)(VAL)=NULL)
|
||||
: WDL_AssocArrayImpl<KEY, VAL>(WDL_assocarray_cmp<KEY>, NULL, NULL, valdispose)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class KEY, class VAL> class WDL_MemKeyedArray : public WDL_AssocArray<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_MemKeyedArray(void (*valdispose)(VAL)=NULL)
|
||||
: WDL_AssocArray<KEY, VAL>(WDL_assocarray_cmpmem<KEY>, NULL, NULL, valdispose)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class KEY, class VAL> class WDL_MemKeyedArrayImpl : public WDL_AssocArrayImpl<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_MemKeyedArrayImpl(void (*valdispose)(VAL)=NULL)
|
||||
: WDL_AssocArrayImpl<KEY, VAL>(WDL_assocarray_cmpmem<KEY>, NULL, NULL, valdispose)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class VAL> class WDL_IntKeyedArray : public WDL_KeyedArray<int, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_IntKeyedArray(void (*valdispose)(VAL)=NULL) : WDL_KeyedArray<int, VAL>(valdispose) {}
|
||||
};
|
||||
|
||||
template <class VAL> class WDL_IntKeyedArray2 : public WDL_KeyedArrayImpl<int, VAL>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit WDL_IntKeyedArray2(void (*valdispose)(VAL)=NULL) : WDL_KeyedArrayImpl<int, VAL>(valdispose) {}
|
||||
};
|
||||
|
||||
template <class VAL> class WDL_StringKeyedArray : public WDL_AssocArray<const char *, VAL>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit WDL_StringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=NULL, bool copyKeys=true)
|
||||
: WDL_AssocArray<const char*, VAL>(caseSensitive?WDL_assocarray_cmpstr<const char>:WDL_assocarray_cmpistr<const char>, copyKeys?dupstr:NULL, copyKeys?wdl_freefunc<const char *>:NULL, valdispose) {}
|
||||
|
||||
static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe...
|
||||
static void freecharptr(char *p) { free(p); } // remove eventually (wdl_freefunc replaced)
|
||||
};
|
||||
|
||||
|
||||
template <class VAL> class WDL_StringKeyedArray2 : public WDL_AssocArrayImpl<const char *, VAL>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit WDL_StringKeyedArray2(bool caseSensitive=true, void (*valdispose)(VAL)=NULL, bool copyKeys=true)
|
||||
: WDL_AssocArrayImpl<const char*, VAL>(caseSensitive?WDL_assocarray_cmpstr<const char>:WDL_assocarray_cmpistr<const char>, copyKeys?dupstr:NULL, copyKeys?wdl_freefunc<const char *>:NULL, valdispose) {}
|
||||
|
||||
~WDL_StringKeyedArray2() { }
|
||||
|
||||
static const char *dupstr(const char *s) { return strdup(s); } // these might not be necessary but depending on the libc maybe...
|
||||
static void freecharptr(char *p) { free(p); } // remove eventually (wdl_freefunc replaced)
|
||||
};
|
||||
|
||||
// sorts text as text, sorts anything that looks like a number as a number
|
||||
template <class VAL> class WDL_LogicalSortStringKeyedArray : public WDL_StringKeyedArray<VAL>
|
||||
{
|
||||
public:
|
||||
|
||||
explicit WDL_LogicalSortStringKeyedArray(bool caseSensitive=true, void (*valdispose)(VAL)=NULL, bool copyKeys=true)
|
||||
: WDL_StringKeyedArray<VAL>(caseSensitive, valdispose, copyKeys)
|
||||
{
|
||||
WDL_StringKeyedArray<VAL>::m_keycmp = caseSensitive?cmpstr:cmpistr; // override
|
||||
}
|
||||
|
||||
~WDL_LogicalSortStringKeyedArray() { }
|
||||
|
||||
static int cmpstr(const char * const *a, const char * const *b)
|
||||
{
|
||||
int r=WDL_strcmp_logical_ex(*a, *b, 1, WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT);
|
||||
return r?r:strcmp(*a,*b);
|
||||
}
|
||||
static int cmpistr(const char * const *a, const char * const *b)
|
||||
{
|
||||
int r=WDL_strcmp_logical_ex(*a, *b, 0, WDL_STRCMP_LOGICAL_EX_FLAG_UTF8CONVERT);
|
||||
return r?r:stricmp(*a,*b);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <class VAL> class WDL_PtrKeyedArray : public WDL_KeyedArray<INT_PTR, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_PtrKeyedArray(void (*valdispose)(VAL)=NULL) : WDL_KeyedArray<INT_PTR, VAL>(valdispose) {}
|
||||
};
|
||||
|
||||
template <class KEY, class VAL> class WDL_PointerKeyedArray : public WDL_KeyedArray<KEY, VAL>
|
||||
{
|
||||
public:
|
||||
explicit WDL_PointerKeyedArray(void (*valdispose)(VAL)=NULL) : WDL_KeyedArray<KEY, VAL>(valdispose) {}
|
||||
};
|
||||
|
||||
struct WDL_Set_DummyRec { };
|
||||
template <class KEY> class WDL_Set : public WDL_AssocArrayImpl<KEY,WDL_Set_DummyRec>
|
||||
{
|
||||
public:
|
||||
explicit WDL_Set(int (*keycmp)(const KEY *k1, const KEY *k2),
|
||||
KEY (*keydup)(KEY)=NULL,
|
||||
void (*keydispose)(KEY)=NULL
|
||||
)
|
||||
: WDL_AssocArrayImpl<KEY, WDL_Set_DummyRec>(keycmp,keydup,keydispose)
|
||||
{
|
||||
}
|
||||
|
||||
int Insert(KEY key)
|
||||
{
|
||||
WDL_Set_DummyRec r;
|
||||
return WDL_AssocArrayImpl<KEY, WDL_Set_DummyRec>::Insert(key,r);
|
||||
}
|
||||
void AddUnsorted(KEY key)
|
||||
{
|
||||
WDL_Set_DummyRec r;
|
||||
WDL_AssocArrayImpl<KEY, WDL_Set_DummyRec>::AddUnsorted(key,r);
|
||||
}
|
||||
|
||||
bool Get(KEY key) const
|
||||
{
|
||||
return WDL_AssocArrayImpl<KEY, WDL_Set_DummyRec>::Exists(key);
|
||||
}
|
||||
bool Enumerate(int i, KEY *key=NULL)
|
||||
{
|
||||
return WDL_AssocArrayImpl<KEY, WDL_Set_DummyRec>::EnumeratePtr(i,key) != NULL;
|
||||
}
|
||||
};
|
||||
|
||||
template <class KEY> class WDL_PtrSet : public WDL_Set<KEY>
|
||||
{
|
||||
public:
|
||||
explicit WDL_PtrSet() : WDL_Set<KEY>( WDL_assocarray_cmp<KEY> ) { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
#include "audiobuffercontainer.h"
|
||||
#include "queue.h"
|
||||
#include <assert.h>
|
||||
|
||||
void ChannelPinMapper::Reset()
|
||||
{
|
||||
for (int i=0; i < CHANNELPINMAPPER_MAXPINS; ++i)
|
||||
m_mapping[i].set_excl(i);
|
||||
}
|
||||
|
||||
void ChannelPinMapper::SetNPins(int nPins)
|
||||
{
|
||||
if (nPins<0) nPins=0;
|
||||
else if (nPins>CHANNELPINMAPPER_MAXPINS) nPins=CHANNELPINMAPPER_MAXPINS;
|
||||
int i;
|
||||
for (i = m_nPins; i < nPins; ++i)
|
||||
{
|
||||
ClearPin(i);
|
||||
if (i < m_nCh)
|
||||
{
|
||||
SetPin(i, i, true);
|
||||
}
|
||||
}
|
||||
m_nPins = nPins;
|
||||
}
|
||||
|
||||
void ChannelPinMapper::SetNChannels(int nCh, bool auto_passthru)
|
||||
{
|
||||
if (auto_passthru) for (int i = m_nCh; i < nCh && i < m_nPins; ++i) {
|
||||
SetPin(i, i, true);
|
||||
}
|
||||
m_nCh = nCh;
|
||||
}
|
||||
|
||||
void ChannelPinMapper::Init(const PinMapPin * pMapping, int nPins)
|
||||
{
|
||||
if (nPins<0) nPins=0;
|
||||
else if (nPins>CHANNELPINMAPPER_MAXPINS) nPins=CHANNELPINMAPPER_MAXPINS;
|
||||
memcpy(m_mapping, pMapping, nPins*sizeof(PinMapPin));
|
||||
memset(m_mapping+nPins, 0, (CHANNELPINMAPPER_MAXPINS-nPins)*sizeof(PinMapPin));
|
||||
m_nPins = m_nCh = nPins;
|
||||
}
|
||||
|
||||
#define BITMASK64(bitIdx) (((WDL_UINT64)1)<<(bitIdx))
|
||||
|
||||
void ChannelPinMapper::ClearPin(int pinIdx)
|
||||
{
|
||||
if (pinIdx >=0 && pinIdx < CHANNELPINMAPPER_MAXPINS) m_mapping[pinIdx].clear();
|
||||
}
|
||||
|
||||
void ChannelPinMapper::SetPin(int pinIdx, int chIdx, bool on)
|
||||
{
|
||||
if (pinIdx >=0 && pinIdx < CHANNELPINMAPPER_MAXPINS)
|
||||
{
|
||||
if (on)
|
||||
{
|
||||
m_mapping[pinIdx].set_chan(chIdx);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mapping[pinIdx].clear_chan(chIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ChannelPinMapper::TogglePin(int pinIdx, int chIdx)
|
||||
{
|
||||
bool on = GetPin(pinIdx, chIdx);
|
||||
on = !on;
|
||||
SetPin(pinIdx, chIdx, on);
|
||||
return on;
|
||||
}
|
||||
|
||||
bool ChannelPinMapper::GetPin(int pinIdx, int chIdx) const
|
||||
{
|
||||
if (pinIdx >= 0 && pinIdx < CHANNELPINMAPPER_MAXPINS)
|
||||
{
|
||||
return m_mapping[pinIdx].has_chan(chIdx);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ChannelPinMapper::IsStraightPassthrough() const
|
||||
{
|
||||
if (m_nCh != m_nPins) return false;
|
||||
PinMapPin tmp;
|
||||
tmp.clear();
|
||||
for (int i = 0; i < m_nPins; ++i)
|
||||
{
|
||||
tmp.set_chan(i);
|
||||
if (!tmp.equal_to(m_mapping[i])) return false;
|
||||
tmp.clear_chan(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#define PINMAPPER_MAGIC 1000
|
||||
|
||||
const char *ChannelPinMapper::SaveStateNew(int* pLen)
|
||||
{
|
||||
m_cfgret.Clear();
|
||||
int magic = PINMAPPER_MAGIC;
|
||||
WDL_Queue__AddToLE(&m_cfgret, &magic);
|
||||
WDL_Queue__AddToLE(&m_cfgret, &m_nCh);
|
||||
WDL_Queue__AddToLE(&m_cfgret, &m_nPins);
|
||||
const int num64 = wdl_max(1,(wdl_min(m_nCh,CHANNELPINMAPPER_MAXPINS) + 63)/64);
|
||||
for (int y = 0; y < num64; y ++)
|
||||
{
|
||||
for (int x = 0; x < m_nPins; x ++)
|
||||
{
|
||||
const WDL_UINT64 v = m_mapping[x].get_64(y);
|
||||
WDL_Queue__AddToLE(&m_cfgret, &v);
|
||||
}
|
||||
}
|
||||
*pLen = m_cfgret.GetSize();
|
||||
return (const char*)m_cfgret.Get();
|
||||
}
|
||||
|
||||
bool ChannelPinMapper::LoadState(const char* buf, int len)
|
||||
{
|
||||
WDL_Queue chunk;
|
||||
chunk.Add(buf, len);
|
||||
int* pMagic = WDL_Queue__GetTFromLE(&chunk, (int*)0);
|
||||
if (!pMagic || *pMagic != PINMAPPER_MAGIC) return false;
|
||||
int* pNCh = WDL_Queue__GetTFromLE(&chunk, (int*) 0);
|
||||
int* pNPins = WDL_Queue__GetTFromLE(&chunk, (int*) 0);
|
||||
if (!pNCh || !pNPins) return false;
|
||||
const int src_pins = *pNPins;
|
||||
SetNPins(src_pins);
|
||||
SetNChannels(*pNCh);
|
||||
const int num64 = wdl_max(1,(wdl_min(m_nCh,CHANNELPINMAPPER_MAXPINS)+63)/64);
|
||||
const int maplen = src_pins * sizeof(WDL_UINT64);
|
||||
for (int y = 0; y < num64; y ++)
|
||||
{
|
||||
if (chunk.Available() < maplen) return y>0;
|
||||
const WDL_UINT64 *pMap = (const WDL_UINT64 *)WDL_Queue__GetDataFromLE(&chunk, maplen, sizeof(WDL_UINT64));
|
||||
const int sz = wdl_min(m_nPins,src_pins);
|
||||
for (int x = 0; x < sz; x ++)
|
||||
{
|
||||
m_mapping[x].set_64(pMap[x], y);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
AudioBufferContainer::AudioBufferContainer()
|
||||
{
|
||||
m_nCh = 0;
|
||||
m_nFrames = 0;
|
||||
m_fmt = FMT_32FP;
|
||||
m_interleaved = true;
|
||||
m_hasData = false;
|
||||
}
|
||||
|
||||
// converts interleaved buffer to interleaved buffer, using min(len_in,len_out) and zeroing any extra samples
|
||||
// isInput means it reads from track channels and writes to plugin pins
|
||||
// wantZeroExcessOutput=false means that untouched channels will be preserved in buf_out
|
||||
void PinMapperConvertBuffers(const double *buf, int len_in, int nch_in,
|
||||
double *buf_out, int len_out, int nch_out,
|
||||
const ChannelPinMapper *pinmap, bool isInput, bool wantZeroExcessOutput)
|
||||
{
|
||||
|
||||
if (pinmap->IsStraightPassthrough() || !pinmap->GetNPins())
|
||||
{
|
||||
int x;
|
||||
char *op = (char *)buf_out;
|
||||
const char *ip = (const char *)buf;
|
||||
|
||||
const int ip_adv = nch_in * sizeof(double);
|
||||
|
||||
const int clen = wdl_min(nch_in, nch_out) * sizeof(double);
|
||||
const int zlen = nch_out > nch_in ? (nch_out - nch_in) * sizeof(double) : 0;
|
||||
|
||||
const int cplen = wdl_min(len_in,len_out);
|
||||
|
||||
for (x=0;x<cplen;x++)
|
||||
{
|
||||
memcpy(op,ip,clen);
|
||||
op += clen;
|
||||
if (zlen)
|
||||
{
|
||||
if (wantZeroExcessOutput) memset(op,0,zlen);
|
||||
op += zlen;
|
||||
}
|
||||
ip += ip_adv;
|
||||
}
|
||||
if (x < len_out && wantZeroExcessOutput) memset(op, 0, (len_out-x)*sizeof(double)*nch_out);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (wantZeroExcessOutput) memset(buf_out,0,len_out*nch_out*sizeof(double));
|
||||
|
||||
const int npins = wdl_min(pinmap->GetNPins(),isInput ? nch_out : nch_in);
|
||||
const int nchan = isInput ? nch_in : nch_out;
|
||||
|
||||
int p;
|
||||
PinMapPin clearmask;
|
||||
clearmask.clear();
|
||||
for (p = 0; p < npins; p ++)
|
||||
{
|
||||
const PinMapPin &map = pinmap->m_mapping[p];
|
||||
for (unsigned int x = 0; map.enum_chans(&x,nchan); x ++)
|
||||
{
|
||||
int i=len_in;
|
||||
const double *ip = buf + (isInput ? x : p);
|
||||
const int out_idx = (isInput ? p : x);
|
||||
|
||||
bool want_zero=false;
|
||||
if (!wantZeroExcessOutput)
|
||||
{
|
||||
if (!clearmask.has_chan(out_idx))
|
||||
{
|
||||
clearmask.set_chan(out_idx);
|
||||
want_zero=true;
|
||||
}
|
||||
}
|
||||
|
||||
double *op = buf_out + out_idx;
|
||||
|
||||
if (want_zero)
|
||||
{
|
||||
while (i-- > 0)
|
||||
{
|
||||
*op = *ip;
|
||||
op += nch_out;
|
||||
ip += nch_in;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i-- > 0)
|
||||
{
|
||||
*op += *ip;
|
||||
op += nch_out;
|
||||
ip += nch_in;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
#ifndef _AUDIOBUFFERCONTAINER_
|
||||
#define _AUDIOBUFFERCONTAINER_
|
||||
|
||||
#include "wdltypes.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "ptrlist.h"
|
||||
#include "queue.h"
|
||||
|
||||
|
||||
#define CHANNELPINMAPPER_MAXPINS 128
|
||||
|
||||
|
||||
struct PinMapPin
|
||||
{
|
||||
enum { PINMAP_PIN_MAX_CHANNELS = CHANNELPINMAPPER_MAXPINS };
|
||||
|
||||
enum { STATE_ENT_BITS=64, STATE_SIZE=(PINMAP_PIN_MAX_CHANNELS + STATE_ENT_BITS - 1) / STATE_ENT_BITS };
|
||||
WDL_UINT64 state[STATE_SIZE];
|
||||
static WDL_UINT64 make_mask(unsigned int idx) { return WDL_UINT64_CONST(1) << (idx & (STATE_ENT_BITS-1)); }
|
||||
static WDL_UINT64 full_mask() { return ~WDL_UINT64_CONST(0); }
|
||||
|
||||
WDL_UINT64 get_64(unsigned int offs=0) const {
|
||||
return WDL_NORMALLY(offs < STATE_SIZE) ? state[offs] : 0;
|
||||
}
|
||||
void set_64(WDL_UINT64 s, unsigned int offs=0) {
|
||||
if (WDL_NORMALLY(offs < STATE_SIZE)) state[offs]=s;
|
||||
}
|
||||
unsigned int get_64_max() const { return STATE_SIZE; }
|
||||
unsigned int get_64_top(unsigned int minv=0) const {
|
||||
unsigned int x = STATE_SIZE;
|
||||
while (x > minv && !state[x-1]) x--;
|
||||
return x;
|
||||
}
|
||||
|
||||
void clear() { memset(state,0,sizeof(state)); }
|
||||
void clear_chan(unsigned int ch) { if (WDL_NORMALLY(ch < PINMAP_PIN_MAX_CHANNELS)) state[ch/STATE_ENT_BITS] &= ~make_mask(ch); }
|
||||
void set_chan(unsigned int ch) { if (WDL_NORMALLY(ch < PINMAP_PIN_MAX_CHANNELS)) state[ch/STATE_ENT_BITS] |= make_mask(ch); }
|
||||
void tog_chan(unsigned int ch) { if (WDL_NORMALLY(ch < PINMAP_PIN_MAX_CHANNELS)) state[ch/STATE_ENT_BITS] ^= make_mask(ch); }
|
||||
void set_chan_lt(unsigned int cnt)
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(cnt > PINMAP_PIN_MAX_CHANNELS)) cnt = PINMAP_PIN_MAX_CHANNELS;
|
||||
for (int x = 0; cnt && x < STATE_SIZE; x ++)
|
||||
{
|
||||
if (cnt < STATE_ENT_BITS) { state[x] |= make_mask(cnt)-1; cnt=0; }
|
||||
else { state[x] = full_mask(); cnt -= STATE_ENT_BITS; }
|
||||
}
|
||||
}
|
||||
void set_excl(unsigned int ch) { clear(); set_chan(ch); }
|
||||
|
||||
bool has_chan(unsigned int ch) const { return WDL_NORMALLY(ch < PINMAP_PIN_MAX_CHANNELS) && (state[ch/STATE_ENT_BITS] & make_mask(ch)); }
|
||||
bool has_chan_lt(unsigned int cnt) const
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(cnt > PINMAP_PIN_MAX_CHANNELS)) cnt = PINMAP_PIN_MAX_CHANNELS;
|
||||
for (int x = 0; cnt && x < STATE_SIZE; x ++)
|
||||
{
|
||||
if (cnt < STATE_ENT_BITS) return (state[x] & (make_mask(cnt)-1));
|
||||
if (state[x]) return true;
|
||||
cnt -= STATE_ENT_BITS;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// call with 0, then increment after each call (returns false when done)
|
||||
bool enum_chans(unsigned int *ch, unsigned int maxch=PINMAP_PIN_MAX_CHANNELS) const
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(maxch > PINMAP_PIN_MAX_CHANNELS))
|
||||
maxch = PINMAP_PIN_MAX_CHANNELS;
|
||||
|
||||
unsigned int x = *ch;
|
||||
if (x >= maxch) return false;
|
||||
|
||||
WDL_UINT64 s = state[x / STATE_ENT_BITS] >> (x & (STATE_ENT_BITS-1));
|
||||
for (;;)
|
||||
{
|
||||
if (s)
|
||||
{
|
||||
do
|
||||
{
|
||||
if (s&1) { *ch = x; return true; }
|
||||
s>>=1;
|
||||
x++;
|
||||
WDL_ASSERT(x & (STATE_ENT_BITS-1)); // we should never run out of bits!
|
||||
}
|
||||
while (x < maxch);
|
||||
break;
|
||||
}
|
||||
x = (x & ~(STATE_ENT_BITS-1)) + STATE_ENT_BITS;
|
||||
if (x >= maxch) break;
|
||||
s = state[x / STATE_ENT_BITS];
|
||||
}
|
||||
|
||||
*ch = x;
|
||||
return false;
|
||||
}
|
||||
|
||||
PinMapPin & operator |= (const PinMapPin &v)
|
||||
{
|
||||
for (int x = 0; x < STATE_SIZE; x ++) state[x]|=v.state[x];
|
||||
return *this;
|
||||
}
|
||||
|
||||
PinMapPin & operator &= (const PinMapPin &v)
|
||||
{
|
||||
for (int x = 0; x < STATE_SIZE; x ++) state[x]&=v.state[x];
|
||||
return *this;
|
||||
}
|
||||
|
||||
void invert()
|
||||
{
|
||||
for (int x = 0; x < STATE_SIZE; x ++) state[x]^=full_mask();
|
||||
}
|
||||
|
||||
bool equal_to(const PinMapPin &v, unsigned int nch_top = PINMAP_PIN_MAX_CHANNELS) const
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(nch_top > PINMAP_PIN_MAX_CHANNELS)) nch_top = PINMAP_PIN_MAX_CHANNELS;
|
||||
for (unsigned int x = 0; x < nch_top; x += STATE_ENT_BITS)
|
||||
{
|
||||
if ((v.state[x/STATE_ENT_BITS]^state[x/STATE_ENT_BITS]) &
|
||||
(((nch_top-x) < STATE_ENT_BITS) ? (make_mask(nch_top-x)-1) : full_mask()))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ChannelPinMapper
|
||||
{
|
||||
public:
|
||||
|
||||
ChannelPinMapper() : m_nCh(0), m_nPins(0) { Reset(); }
|
||||
~ChannelPinMapper() {}
|
||||
|
||||
void SetNPins(int nPins);
|
||||
void SetNChannels(int nCh, bool auto_passthru=true);
|
||||
// or ...
|
||||
void Init(const PinMapPin * pMapping, int nPins);
|
||||
// or ...
|
||||
void Reset(); // set to full passthrough
|
||||
|
||||
int GetNPins() const { return m_nPins; }
|
||||
int GetNChannels() const { return m_nCh; }
|
||||
|
||||
void ClearPin(int pinIdx);
|
||||
void SetPin(int pinIdx, int chIdx, bool on);
|
||||
bool TogglePin(int pinIdx, int chIdx);
|
||||
|
||||
// true if this pin is mapped to this channel
|
||||
bool GetPin(int pinIdx, int chIdx) const;
|
||||
|
||||
// true if this mapper is a straight 1:1 passthrough
|
||||
bool IsStraightPassthrough() const;
|
||||
|
||||
const char *SaveStateNew(int* pLen); // owned
|
||||
bool LoadState(const char* buf, int len);
|
||||
|
||||
PinMapPin m_mapping[CHANNELPINMAPPER_MAXPINS];
|
||||
int m_nCh, m_nPins;
|
||||
|
||||
private:
|
||||
|
||||
WDL_Queue m_cfgret;
|
||||
};
|
||||
|
||||
// converts interleaved buffer to interleaved buffer, using min(len_in,len_out) and zeroing any extra samples
|
||||
// isInput means it reads from track channels and writes to plugin pins
|
||||
// wantZeroExcessOutput=false means that untouched channels will be preserved in buf_out
|
||||
void PinMapperConvertBuffers(const double *buf, int len_in, int nch_in,
|
||||
double *buf_out, int len_out, int nch_out,
|
||||
const ChannelPinMapper *pinmap, bool isInput, bool wantZeroExcessOutput);
|
||||
|
||||
// use for float and double only ... ints will break it
|
||||
class AudioBufferContainer
|
||||
{
|
||||
public:
|
||||
|
||||
AudioBufferContainer();
|
||||
~AudioBufferContainer() {}
|
||||
|
||||
enum
|
||||
{
|
||||
FMT_32FP=4,
|
||||
FMT_64FP=8
|
||||
};
|
||||
|
||||
static bool BufConvert(void* dest, const void* src, int destFmt, int srcFmt, int nFrames, int destStride, int srcStride);
|
||||
|
||||
int GetNChannels() const { return m_nCh; }
|
||||
int GetNFrames() const { return m_nFrames; }
|
||||
int GetFormat() const { return m_fmt; }
|
||||
|
||||
void Resize(int nCh, int nFrames, bool preserveData);
|
||||
// call Reformat(GetFormat(), false) to discard current data (for efficient repopulating)
|
||||
void Reformat(int fmt, bool preserveData);
|
||||
|
||||
// src=NULL to memset(0)
|
||||
void* SetAllChannels(int fmt, const void* src, int nCh, int nFrames);
|
||||
|
||||
// src=NULL to memset(0)
|
||||
void* SetChannel(int fmt, const void* src, int chIdx, int nFrames);
|
||||
|
||||
void* MixChannel(int fmt, const void* src, int chIdx, int nFrames, bool addToDest, double wt_start, double wt_end);
|
||||
|
||||
void* GetAllChannels(int fmt, bool preserveData);
|
||||
void* GetChannel(int fmt, int chIdx, bool preserveData);
|
||||
|
||||
void CopyFrom(const AudioBufferContainer* rhs);
|
||||
|
||||
private:
|
||||
|
||||
void ReLeave(bool interleave, bool preserveData);
|
||||
|
||||
WDL_HeapBuf m_data;
|
||||
int m_nCh;
|
||||
int m_nFrames;
|
||||
|
||||
int m_fmt;
|
||||
bool m_interleaved;
|
||||
bool m_hasData;
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
|
||||
void SetPinsFromChannels(AudioBufferContainer* dest, AudioBufferContainer* src, const ChannelPinMapper* mapper, int forceMinChanCnt=0);
|
||||
void SetChannelsFromPins(AudioBufferContainer* dest, AudioBufferContainer* src, const ChannelPinMapper* mapper, double wt_start=1.0, double wt_end=1.0);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef _WDL_BITFIELD_H_
|
||||
#define _WDL_BITFIELD_H_
|
||||
|
||||
#include "heapbuf.h"
|
||||
|
||||
class WDL_BitField // ultra simple bit field
|
||||
{
|
||||
public:
|
||||
bool SetSize(int sz) // clears state
|
||||
{
|
||||
void *b=m_hb.ResizeOK((sz+7)/8);
|
||||
if (b) memset(b,0,m_hb.GetSize());
|
||||
return !!b;
|
||||
}
|
||||
int GetApproxSize() const { return m_hb.GetSize()*8; } // may return slightly greater than the size set
|
||||
|
||||
bool IsSet(unsigned int idx) const
|
||||
{
|
||||
const unsigned char mask = 1<<(idx&7);
|
||||
idx>>=3;
|
||||
return idx < (unsigned int)m_hb.GetSize() && (((unsigned char *)m_hb.Get())[idx]&mask);
|
||||
}
|
||||
void Set(unsigned int idx)
|
||||
{
|
||||
const unsigned char mask = 1<<(idx&7);
|
||||
idx>>=3;
|
||||
if (idx < (unsigned int)m_hb.GetSize()) ((unsigned char *)m_hb.Get())[idx] |= mask;
|
||||
}
|
||||
|
||||
private:
|
||||
WDL_HeapBuf m_hb;
|
||||
};
|
||||
|
||||
#endif //_WDL_BITFIELD_H_
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
#ifndef _WDL_CHUNKALLOC_H_
|
||||
#define _WDL_CHUNKALLOC_H_
|
||||
|
||||
#include "wdltypes.h"
|
||||
|
||||
class WDL_ChunkAlloc
|
||||
{
|
||||
struct _hdr
|
||||
{
|
||||
struct _hdr *_next;
|
||||
char data[16];
|
||||
};
|
||||
|
||||
_hdr *m_chunks;
|
||||
int m_chunksize, m_chunkused;
|
||||
|
||||
public:
|
||||
|
||||
WDL_ChunkAlloc(int chunksize=65500) { m_chunks=NULL; m_chunkused=0; m_chunksize=chunksize>16?chunksize:16; }
|
||||
~WDL_ChunkAlloc() { Free(); }
|
||||
|
||||
void Free()
|
||||
{
|
||||
_hdr *a = m_chunks;
|
||||
m_chunks=0;
|
||||
m_chunkused=0;
|
||||
while (a) { _hdr *f=a; a=a->_next; free(f); }
|
||||
}
|
||||
|
||||
void *Alloc(int sz, int align=0)
|
||||
{
|
||||
if (sz<1) return NULL;
|
||||
|
||||
if (align < 1 || (align & (align-1))) align=1;
|
||||
|
||||
if (m_chunks)
|
||||
{
|
||||
int use_sz=sz;
|
||||
char *p = m_chunks->data + m_chunkused;
|
||||
int a = ((int) (INT_PTR)p) & (align-1);
|
||||
if (a)
|
||||
{
|
||||
use_sz += align-a;
|
||||
p += align-a;
|
||||
}
|
||||
if (use_sz <= m_chunksize - m_chunkused)
|
||||
{
|
||||
m_chunkused += use_sz;
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
// we assume that malloc always gives at least 8 byte alignment, and our _next ptr may offset that by 4,
|
||||
// so no need to allocate extra if less than 4 bytes of alignment requested
|
||||
int use_align = (align>=4 ? align : 0);
|
||||
int alloc_sz=sz+use_align;
|
||||
if (alloc_sz < m_chunksize)
|
||||
{
|
||||
// if existing chunk has less free space in it than we would at chunksize, allocate chunksize
|
||||
if (!m_chunks || m_chunkused > alloc_sz) alloc_sz=m_chunksize;
|
||||
}
|
||||
_hdr *nc = (_hdr *)malloc(sizeof(_hdr) + alloc_sz - 16);
|
||||
if (!nc) return NULL;
|
||||
|
||||
int use_sz=sz;
|
||||
char *ret = nc->data;
|
||||
int a = ((int) (INT_PTR)ret) & (align-1);
|
||||
if (a)
|
||||
{
|
||||
use_sz += align-a;
|
||||
ret += align-a;
|
||||
}
|
||||
|
||||
if (m_chunks && (m_chunksize-m_chunkused) >= (alloc_sz - use_sz))
|
||||
{
|
||||
// current chunk has as much or more free space than our chunk, put our chunk on the list second
|
||||
nc->_next = m_chunks->_next;
|
||||
m_chunks->_next=nc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// push our chunk to the top of the list
|
||||
nc->_next = m_chunks;
|
||||
m_chunks=nc;
|
||||
m_chunkused = alloc_sz >= m_chunksize ? use_sz : m_chunksize;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *StrDup(const char *s)
|
||||
{
|
||||
if (!s) return NULL;
|
||||
const int l = (int) strlen(s)+1;
|
||||
char *ret = (char*)Alloc(l);
|
||||
if (!ret) return NULL;
|
||||
memcpy(ret,s,l);
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
+275
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
WDL - circbuf.h
|
||||
Copyright (C) 2005 Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file provides a simple class for a circular FIFO queue of bytes.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _WDL_CIRCBUF_H_
|
||||
#define _WDL_CIRCBUF_H_
|
||||
|
||||
#include "heapbuf.h"
|
||||
|
||||
class WDL_CircBuf
|
||||
{
|
||||
public:
|
||||
WDL_CircBuf()
|
||||
{
|
||||
m_inbuf = m_wrptr = 0;
|
||||
m_buf = NULL;
|
||||
m_alloc = 0;
|
||||
}
|
||||
~WDL_CircBuf()
|
||||
{
|
||||
free(m_buf);
|
||||
}
|
||||
void SetSize(int size)
|
||||
{
|
||||
if (size<0) size=0;
|
||||
m_inbuf = m_wrptr = 0;
|
||||
if (size != m_alloc || !m_buf)
|
||||
{
|
||||
m_alloc = size;
|
||||
free(m_buf);
|
||||
m_buf = size ? (char*)malloc(size) : NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void SetSizePreserveContents(int newsz)
|
||||
{
|
||||
if (newsz < NbInBuf()) newsz = NbInBuf(); // do not allow destructive resize down
|
||||
const int oldsz = m_alloc, dsize = newsz - oldsz;
|
||||
if (!dsize) return;
|
||||
if (!m_inbuf||!m_buf) { SetSize(newsz); return; }
|
||||
|
||||
const int div1 = m_inbuf - m_wrptr; // div1>0 is size of end block, div1<0 is offset of start block
|
||||
char *buf=NULL;
|
||||
if (dsize > 0)
|
||||
{
|
||||
buf = (char *)realloc(m_buf, newsz);
|
||||
if (WDL_NORMALLY(buf) && div1 > 0) // block crossing loop, need to shuffle some data
|
||||
{
|
||||
if (div1 > m_wrptr) // m_wrptr is size of start block, div1 is size of end block
|
||||
{
|
||||
// end block is larger than start, move some of start block to end of end block and shuffle forward start
|
||||
if (dsize >= m_wrptr)
|
||||
{
|
||||
if (m_wrptr>0) memmove(buf+oldsz,buf,m_wrptr);
|
||||
m_wrptr += oldsz;
|
||||
}
|
||||
else
|
||||
{
|
||||
memmove(buf + oldsz, buf, dsize);
|
||||
m_wrptr -= dsize;
|
||||
memmove(buf, buf+dsize, m_wrptr);
|
||||
}
|
||||
}
|
||||
else // end block is smaller, move it to the new end of buffer
|
||||
{
|
||||
memmove(buf + newsz - div1, buf + oldsz - div1, div1);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (div1 < 0) // shrinking, and not a wrapped buffer
|
||||
{
|
||||
if (m_wrptr > newsz)
|
||||
{
|
||||
memmove(m_buf,m_buf-div1, m_inbuf);
|
||||
m_wrptr = m_inbuf;
|
||||
}
|
||||
buf = (char *)realloc(m_buf, newsz);
|
||||
}
|
||||
|
||||
if (!buf) // failed realloc(), or sizing down with block crossing loop boundary
|
||||
{
|
||||
buf = (char *)malloc(newsz);
|
||||
if (WDL_NOT_NORMALLY(!buf)) return;
|
||||
const int peeked = Peek(buf,0,m_inbuf);
|
||||
if (peeked != m_inbuf) { WDL_ASSERT(peeked == m_inbuf); }
|
||||
free(m_buf);
|
||||
m_wrptr = m_inbuf = peeked;
|
||||
}
|
||||
if (m_wrptr > newsz) { WDL_ASSERT(m_wrptr <= newsz); }
|
||||
if (m_wrptr >= newsz) m_wrptr=0;
|
||||
m_alloc = newsz;
|
||||
m_buf = buf;
|
||||
}
|
||||
void Reset() { m_inbuf = m_wrptr = 0; }
|
||||
int Add(const void *buf, int l)
|
||||
{
|
||||
if (!m_buf) return 0;
|
||||
const int bf = m_alloc - m_inbuf;
|
||||
if (l>bf) l = bf;
|
||||
if (l > 0)
|
||||
{
|
||||
m_wrptr = __write_bytes(m_wrptr,l,buf);
|
||||
m_inbuf += l;
|
||||
}
|
||||
return l;
|
||||
}
|
||||
void UnAdd(int amt)
|
||||
{
|
||||
if (amt > 0)
|
||||
{
|
||||
if (amt > m_inbuf) amt=m_inbuf;
|
||||
m_wrptr -= amt;
|
||||
if (m_wrptr < 0) m_wrptr += m_alloc;
|
||||
m_inbuf -= amt;
|
||||
}
|
||||
}
|
||||
|
||||
void Skip(int l) // can be used to rewind read pointer
|
||||
{
|
||||
m_inbuf -= l;
|
||||
if (m_inbuf<0) m_inbuf=0;
|
||||
else if (m_inbuf>m_alloc) m_inbuf=m_alloc;
|
||||
}
|
||||
|
||||
int Peek(void *buf, int offs, int len) const
|
||||
{
|
||||
if (offs<0||!m_buf) return 0;
|
||||
const int ibo = m_inbuf-offs;
|
||||
if (len > ibo) len = ibo;
|
||||
if (len > 0)
|
||||
{
|
||||
int rp = m_wrptr - ibo;
|
||||
if (rp < 0) rp += m_alloc;
|
||||
const int wr1 = m_alloc - rp;
|
||||
char * const rd = m_buf;
|
||||
if (wr1 < len)
|
||||
{
|
||||
memcpy(buf,rd+rp,wr1);
|
||||
memcpy((char*)buf+wr1,rd,len-wr1);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(buf,rd+rp,len);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
void WriteAtReadPointer(const void *buf, int len, int offs=0)
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(offs<0) || WDL_NOT_NORMALLY(offs>=m_inbuf)) return;
|
||||
if (!m_buf || len<1) return;
|
||||
if (offs+len > m_inbuf) len = m_inbuf-offs;
|
||||
|
||||
int write_offs = m_wrptr - m_inbuf + offs;
|
||||
if (write_offs < 0) write_offs += m_alloc;
|
||||
__write_bytes(write_offs, len, buf);
|
||||
}
|
||||
|
||||
int Get(void *buf, int l)
|
||||
{
|
||||
const int amt = Peek(buf,0,l);
|
||||
m_inbuf -= amt;
|
||||
return amt;
|
||||
}
|
||||
int NbFree() const { return m_alloc - m_inbuf; } // formerly Available()
|
||||
int NbInBuf() const { return m_inbuf; }
|
||||
int GetTotalSize() const { return m_alloc; }
|
||||
|
||||
private:
|
||||
int __write_bytes(int wrptr, int l, const void *buf) // no bounds checking, return end offset
|
||||
{
|
||||
const int wr1 = m_alloc-wrptr;
|
||||
char * const p = m_buf, * const pw = p + wrptr;
|
||||
if (wr1 < l)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
memcpy(pw, buf, wr1);
|
||||
memcpy(p, (char*)buf + wr1, l-wr1);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(pw, 0, wr1);
|
||||
memset(p, 0, l-wr1);
|
||||
}
|
||||
return l-wr1;
|
||||
}
|
||||
|
||||
if (buf) memcpy(pw, buf, l);
|
||||
else memset(pw, 0, l);
|
||||
return wr1 == l ? 0 : wrptr+l;
|
||||
}
|
||||
|
||||
char *m_buf;
|
||||
int m_inbuf, m_wrptr,m_alloc;
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
|
||||
template <class T>
|
||||
class WDL_TypedCircBuf
|
||||
{
|
||||
public:
|
||||
|
||||
WDL_TypedCircBuf() {}
|
||||
~WDL_TypedCircBuf() {}
|
||||
|
||||
void SetSize(int size)
|
||||
{
|
||||
mBuf.SetSize(size * sizeof(T));
|
||||
}
|
||||
void SetSizePreserveContents(int size)
|
||||
{
|
||||
mBuf.SetSizePreserveContents(size*sizeof(T));
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
mBuf.Reset();
|
||||
}
|
||||
|
||||
void UnAdd(int l) { mBuf.UnAdd(l*sizeof(T)); }
|
||||
|
||||
int Add(const T* buf, int l)
|
||||
{
|
||||
return mBuf.Add(buf, l * sizeof(T)) / sizeof(T);
|
||||
}
|
||||
|
||||
int Get(T* buf, int l)
|
||||
{
|
||||
return mBuf.Get(buf, l * sizeof(T)) / sizeof(T);
|
||||
}
|
||||
|
||||
int Peek(T* buf, int offs, int l)
|
||||
{
|
||||
return mBuf.Peek(buf, offs*sizeof(T), l * sizeof(T)) / sizeof(T);
|
||||
}
|
||||
void Skip(int l) { mBuf.Skip(l*sizeof(T)); }
|
||||
|
||||
void WriteAtReadPointer(const void *buf, int len, int offs=0) { mBuf.WriteAtReadPointer(buf,len*sizeof(T),offs*sizeof(T)); }
|
||||
|
||||
int NbFree() const { return mBuf.NbFree() / sizeof(T); } // formerly Available()
|
||||
int ItemsInQueue() const { return mBuf.NbInBuf() / sizeof(T); }
|
||||
int NbInBuf() const { return mBuf.NbInBuf() / sizeof(T); }
|
||||
int GetTotalSize() const { return mBuf.GetTotalSize() / sizeof(T); }
|
||||
|
||||
private:
|
||||
WDL_CircBuf mBuf;
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
bessel_polynomial.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
algorithm to calculate coefficients for a bessel polynomial from krall & fink
|
||||
series.
|
||||
*/
|
||||
|
||||
#ifndef _BESSEL_POLYNOMIAL_H_
|
||||
#define _BESSEL_POLYNOMIAL_H_
|
||||
|
||||
#include "custom_math.h"
|
||||
#include "factorial.h"
|
||||
|
||||
#ifdef _BESSEL_USE_INLINE_
|
||||
#define _BESSEL_INLINE _CMATH_INLINE
|
||||
#else
|
||||
#define _BESSEL_INLINE
|
||||
#endif
|
||||
|
||||
#ifndef _CMATH_ANSI
|
||||
#define _BESSEL_MAX_ORDER 10
|
||||
#else
|
||||
#define _BESSEL_MAX_ORDER 3
|
||||
#endif
|
||||
|
||||
/* return a coefficient */
|
||||
_BESSEL_INLINE cmath_std_int_t
|
||||
bessel_coefficient(const cmath_uint16_t k, const cmath_uint16_t n)
|
||||
{
|
||||
register cmath_std_int_t c;
|
||||
const cmath_uint16_t nmk = (cmath_uint16_t)(n - k);
|
||||
c = factorial(2*n - k);
|
||||
c /= (factorial(nmk)*factorial(k)) * (1 << nmk);
|
||||
return c;
|
||||
}
|
||||
|
||||
/* calculate all coefficients for n-th order polynomial */
|
||||
_BESSEL_INLINE
|
||||
void bessel_polynomial( cmath_std_int_t *coeff,
|
||||
const cmath_uint16_t order,
|
||||
const cmath_uint16_t reverse )
|
||||
{
|
||||
register cmath_uint16_t i = (cmath_uint16_t)(order + 1);
|
||||
if (reverse)
|
||||
{
|
||||
while (i--)
|
||||
coeff[order-i] = bessel_coefficient(i, order);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (i--)
|
||||
coeff[i] = bessel_coefficient(i, order);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* _BESSEL_POLYNOMIAL_H */
|
||||
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
complex_number.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
portable complex number operations
|
||||
*/
|
||||
|
||||
#ifndef _COMPLEX_NUMBER_H_
|
||||
#define _COMPLEX_NUMBER_H_
|
||||
|
||||
#include "custom_math.h"
|
||||
|
||||
/* settings */
|
||||
#ifndef _CNUM_NO_INLINE
|
||||
#define _CNUM_INLINE _CMATH_INLINE
|
||||
#else
|
||||
#define _CNUM_INLINE
|
||||
#endif
|
||||
|
||||
#ifndef _CNUM_NO_ALIAS
|
||||
#define _CNUM_ALIAS _CMATH_MAY_ALIAS
|
||||
#else
|
||||
#define _CNUM_ALIAS
|
||||
#endif
|
||||
|
||||
/* types & constants */
|
||||
#ifndef cnum_t
|
||||
#define cnum_t cmath_t
|
||||
#endif
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cnum_t r _CMATH_ALIGN(8);
|
||||
cnum_t i _CMATH_ALIGN(8);
|
||||
} _CNUM_ALIAS cnum_s;
|
||||
|
||||
const cnum_s cnum_zero = {0, 0};
|
||||
const cnum_s cnum_i1 = {0, 1};
|
||||
const cnum_s cnum_r1 = {1, 0};
|
||||
const cnum_s cnum_r2 = {2, 0};
|
||||
|
||||
/* methods */
|
||||
#define _CNUM(r, i) cnum_new(r, i)
|
||||
#define _CNUMD(x, r, i) cnum_s x = {r, i}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_set(cnum_s *x, const cnum_t r, const cnum_t i)
|
||||
{
|
||||
x->r = r;
|
||||
x->i = i;
|
||||
return *x;
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_from(cnum_s *x, const cnum_s y)
|
||||
{
|
||||
x->r = y.r;
|
||||
x->i = y.i;
|
||||
return *x;
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_new(const cnum_t r, const cnum_t i)
|
||||
{
|
||||
cnum_s x;
|
||||
x.r = r;
|
||||
x.i = i;
|
||||
return x;
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_cartesian(const cnum_s x)
|
||||
{
|
||||
return cnum_new(x.r * cmath_cos(x.i), x.r * cmath_sin(x.i));
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_polar(const cnum_s x)
|
||||
{
|
||||
return cnum_new(cmath_cabs(x.r, x.i), cmath_carg(x.r, x.i));
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_conjugate(const cnum_s x)
|
||||
{
|
||||
return cnum_new(x.r, -x.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_negative(const cnum_s x)
|
||||
{
|
||||
return cnum_new(-x.r, -x.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_swap(const cnum_s x)
|
||||
{
|
||||
return cnum_new(x.i, x.r);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_add(const cnum_s x, const cnum_s y)
|
||||
{
|
||||
return cnum_new(x.r + y.r, x.i + y.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_add_r(const cnum_s x, const cnum_t y)
|
||||
{
|
||||
return cnum_new(x.r + y, x.i + y);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_sub(const cnum_s x, const cnum_s y)
|
||||
{
|
||||
return cnum_new(x.r - y.r, x.i - y.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_sub_r(register cnum_s x, const cnum_t y)
|
||||
{
|
||||
return cnum_new(x.r - y, x.i - y);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_r_sub(const cnum_t x, register cnum_s y)
|
||||
{
|
||||
return cnum_new(x - y.r, x - y.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_mul(const cnum_s x, const cnum_s y)
|
||||
{
|
||||
return cnum_new(x.r*y.r - x.i*y.i, x.r*y.i + x.i*y.r);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_mul_r(const cnum_s x, const cnum_t y)
|
||||
{
|
||||
return cnum_new(x.r*y, x.i*y);
|
||||
}
|
||||
|
||||
#define cnum_sqr(x) \
|
||||
cnum_mul(x, x)
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_div_r(const cnum_s x, const cnum_t y)
|
||||
{
|
||||
return cnum_new(x.r/y, x.i/y);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_r_div(const cnum_t x, const cnum_s y)
|
||||
{
|
||||
return cnum_new(x/y.r, x/y.i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_div(const cnum_s x, const cnum_s y)
|
||||
{
|
||||
return cnum_div_r(cnum_mul(x, cnum_conjugate(y)),
|
||||
(y.r*y.r + cmath_abs(y.i*y.i)));
|
||||
}
|
||||
|
||||
#define cnum_inv(x) \
|
||||
cnum_div(cnum_r1, x)
|
||||
|
||||
#define _CNUM_CHECK_EXP_D_ \
|
||||
cmath_abs(deg - cmath_round(deg)) == 0
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_exp(const cnum_s x)
|
||||
{
|
||||
cnum_t sin_i = cmath_sin(x.i);
|
||||
cnum_t cos_i = cmath_cos(x.i);
|
||||
const cnum_t exp_r = cmath_exp(x.r);
|
||||
|
||||
#ifndef _CNUM_NO_CHECK_EXP_
|
||||
register cnum_t deg;
|
||||
|
||||
if (x.r == 0)
|
||||
return cnum_zero;
|
||||
deg = x.i / cmath_pi;
|
||||
if (_CNUM_CHECK_EXP_D_)
|
||||
sin_i = 0;
|
||||
deg += 0.5;
|
||||
if (_CNUM_CHECK_EXP_D_)
|
||||
cos_i = 0;
|
||||
deg = x.i / cmath_pi2;
|
||||
if (_CNUM_CHECK_EXP_D_)
|
||||
cos_i = 1;
|
||||
#endif
|
||||
|
||||
return cnum_new(exp_r*cos_i, exp_r*sin_i);
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_log_k(const cnum_s x, const cmath_int32_t k)
|
||||
{
|
||||
return cnum_new(cmath_log(cmath_cabs(x.r, x.i)),
|
||||
(cmath_carg(x.r, x.i) + (cmath_pi2*k)));
|
||||
}
|
||||
|
||||
#define cnum_log(x) \
|
||||
cnum_log_k(x, 0)
|
||||
|
||||
#define cnum_log_b_k(x, b, k) \
|
||||
cnum_div(cnum_log_k(x, k), cnum_log_k(b, k))
|
||||
|
||||
#define cnum_log_b(b, x) \
|
||||
cnum_div(cnum_log(x), cnum_log(b))
|
||||
|
||||
#define cnum_log2(x) \
|
||||
cnum_log_b(x, 2)
|
||||
|
||||
#define cnum_log2_k(x, k) \
|
||||
cnum_log_b_k(x, 2, k)
|
||||
|
||||
#define cnum_log10(x) \
|
||||
cnum_log_b(x, 2)
|
||||
|
||||
#define cnum_log10_k(x, k) \
|
||||
cnum_log_b_k(x, 10, k)
|
||||
|
||||
#define _CNUM_CHECK_POW_C_ \
|
||||
if (x.r == 0 && x.i == 0) \
|
||||
return cnum_zero; \
|
||||
if (y.r == 0 && y.i == 0) \
|
||||
return cnum_r1 \
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_pow_c_k(const cnum_s x, const cnum_s y, const cmath_int32_t k)
|
||||
{
|
||||
_CNUM_CHECK_POW_C_;
|
||||
return cnum_exp(cnum_mul(cnum_log_k(x, k), y));
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_pow_c(const cnum_s x, const cnum_s y)
|
||||
{
|
||||
_CNUM_CHECK_POW_C_;
|
||||
return cnum_exp(cnum_mul(cnum_log(x), y));
|
||||
}
|
||||
|
||||
_CNUM_INLINE
|
||||
cnum_s cnum_pow(const cnum_s x, const cnum_t n)
|
||||
{
|
||||
const cnum_t r_pow_n = cmath_pow(cmath_cabs(x.r, x.i), n);
|
||||
const cnum_t theta_n = cmath_carg(x.r, x.i) * n;
|
||||
if (n == 0)
|
||||
return cnum_new(1, 0);
|
||||
if (n == 1)
|
||||
return x;
|
||||
return cnum_new(r_pow_n * cmath_cos(theta_n), r_pow_n * cmath_sin(theta_n));
|
||||
}
|
||||
|
||||
#define cnum_root_c_k(x, y, k) \
|
||||
cnum_exp(cnum_div(cnum_log_k(x, k), y))
|
||||
|
||||
#define cnum_root_c(x, y) \
|
||||
cnum_exp(cnum_div(cnum_log(x), y))
|
||||
|
||||
#define cnum_root(x, n) \
|
||||
cnum_pow(x, 1/n)
|
||||
|
||||
#define cnum_sqrt(x) \
|
||||
cnum_pow(x, 0.5)
|
||||
|
||||
#define cnum_sin(x) \
|
||||
cnum_new(cmath_sin((x).r)*cmath_cosh((x).i), \
|
||||
cmath_cos((x).r)*cmath_sinh((x).i))
|
||||
|
||||
#define cnum_sinh(x) \
|
||||
cnum_new(cmath_sinh(x.r)*cmath_cos(x.i), cmath_cosh(x.r)*sin(x.i))
|
||||
|
||||
#define cnum_cos(x) \
|
||||
cnum_new(cmath_cos(x.r)*cmath_cosh(x.i), -cmath_sin(x.r)*cmath_sinh(x.i))
|
||||
|
||||
#define cnum_cosh(x) \
|
||||
cnum_new(cmath_cosh(x.r)*cmath_cos(x.i), cmath_sinh(x.r)*cmath_sin(x.i))
|
||||
|
||||
#define cnum_tan(x) \
|
||||
cnum_div(cnum_sin(x), cnum_cos(x))
|
||||
|
||||
#define cnum_tanh(x) \
|
||||
cnum_div(cnum_sinh(x), cnum_cosh(x))
|
||||
|
||||
#define cnum_csc(x) \
|
||||
cnum_inv(cnum_sin(x))
|
||||
|
||||
#define cnum_sec(x) \
|
||||
cnum_inv(cnum_cos(x))
|
||||
|
||||
#define cnum_cotan(x) \
|
||||
cnum_inv(cnum_tan(x))
|
||||
|
||||
#define cnum_asin(x) \
|
||||
cnum_negative(cnum_mul(cnum_i1, cnum_log(cnum_add(cnum_mul(cnum_i1, x), \
|
||||
cnum_sqrt(cnum_sub(cnum_r1, cnum_sqr(x)))))))
|
||||
|
||||
#define cnum_acos(x) \
|
||||
cnum_negative(cnum_mul(cnum_i1, cnum_log(cnum_add(x, cnum_mul(cnum_i1, \
|
||||
cnum_sqrt(cnum_sub(cnum_r1, cnum_sqr(x))))))))
|
||||
|
||||
#define cnum_atan(x) \
|
||||
cnum_div(cnum_mul(cnum_i1, cnum_log(cnum_div(cnum_sub(cnum_r1, \
|
||||
cnum_mul(cnum_i1, x)), cnum_add(cnum_r1, cnum_mul(cnum_i1, x))))), cnum_r2)
|
||||
|
||||
#define cnum_acsc(x) \
|
||||
cnum_asin(cnum_inv(x))
|
||||
|
||||
#define cnum_asec(x) \
|
||||
cnum_acos(cnum_inv(x))
|
||||
|
||||
#define cnum_acot(x) \
|
||||
cnum_atan(cnum_inv(x))
|
||||
|
||||
#define cnum_csch(x) \
|
||||
cnum_inv(cnum_sinh(x))
|
||||
|
||||
#define cnum_sech(x) \
|
||||
cnum_inv(cnum_cosh(x))
|
||||
|
||||
#define cnum_coth(x) \
|
||||
cnum_inv(cnum_tanh(x))
|
||||
|
||||
#define cnum_asinh(x) \
|
||||
cnum_log(cnum_add(x, cnum_sqrt(cnum_add(cnum_r1, cnum_sqr(x)))))
|
||||
|
||||
#define cnum_acosh(x) \
|
||||
cnum_log(cnum_add(x, cnum_mul(cnum_sqrt(cnum_add(x, cnum_r1)), \
|
||||
cnum_sqrt(cnum_sub(x, cnum_r1)))))
|
||||
|
||||
#define cnum_atanh(x) \
|
||||
cnum_div(cnum_sub(cnum_log(cnum_add(cnum_r1, x)), \
|
||||
cnum_log(cnum_sub(cnum_r1, x))), cnum_r2)
|
||||
|
||||
#define cnum_acsch(x) \
|
||||
cnum_asinh(cnum_inv(x))
|
||||
|
||||
#define cnum_asech(x) \
|
||||
cnum_acosh(cnum_inv(x))
|
||||
|
||||
#define cnum_asech(x) \
|
||||
cnum_acosh(cnum_inv(x))
|
||||
|
||||
#define cnum_acoth(x) \
|
||||
cnum_atanh(cnum_inv(x))
|
||||
|
||||
#endif /* _COMPLEX_NUMBER_H_ */
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
custom_math.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
portable definitions for ansi c and cross compiler compatibility.
|
||||
contains: numeric constants, custom math functions, macros & other.
|
||||
*/
|
||||
|
||||
#ifndef _CUSTOM_MATH_H_
|
||||
#define _CUSTOM_MATH_H_
|
||||
|
||||
#include "math.h"
|
||||
|
||||
/* check for "c89" mode */
|
||||
#if (defined _MSC_VER && defined __STDC__) || \
|
||||
(defined __GNUC__ && defined __STRICT_ANSI__)
|
||||
#define _CMATH_ANSI
|
||||
#endif
|
||||
|
||||
/* enable inline */
|
||||
#if defined __cplusplus || (!defined _CMATH_ANSI && defined _CMATH_USE_INLINE)
|
||||
#ifdef _MSC_VER
|
||||
#define _CMATH_INLINE __inline
|
||||
#else
|
||||
#define _CMATH_INLINE inline
|
||||
#endif
|
||||
#else
|
||||
#define _CMATH_INLINE
|
||||
#endif
|
||||
|
||||
/* align type to size of type */
|
||||
#if defined __GNUC__ || defined __TINYC__
|
||||
#define _CMATH_ALIGN(x) __attribute__ ((aligned(x)))
|
||||
#define _CMATH_ALIGN_T(x) __attribute__ ((aligned(sizeof(x))))
|
||||
#else
|
||||
#define _CMATH_ALIGN(x)
|
||||
#define _CMATH_ALIGN_T(x)
|
||||
#endif
|
||||
|
||||
/* printf max integer */
|
||||
#ifndef _CMATH_ANSI
|
||||
#ifdef _WIN32
|
||||
#define _CMATH_PR_STD_UINT "I64u"
|
||||
#define _CMATH_PR_STD_INT "I64i"
|
||||
#define _CMATH_PR_STD_HEX "I64x"
|
||||
#else
|
||||
#define _CMATH_PR_STD_UINT "llu"
|
||||
#define _CMATH_PR_STD_INT "lli"
|
||||
#define _CMATH_PR_STD_HEX "llx"
|
||||
#endif
|
||||
#else
|
||||
#define _CMATH_PR_STD_UINT "u"
|
||||
#define _CMATH_PR_STD_INT "d"
|
||||
#define _CMATH_PR_STD_HEX "x"
|
||||
#endif
|
||||
|
||||
/* msvc specifics */
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4514)
|
||||
|
||||
#define MK_L(x) (x)
|
||||
#define MK_UL(x) (x)
|
||||
#define MK_LL(x) (x)
|
||||
#define MK_ULL(x) (x)
|
||||
#else
|
||||
#define MK_L(x) (x##L)
|
||||
#define MK_UL(x) (x##UL)
|
||||
#ifdef _CMATH_ANSI
|
||||
#define MK_LL(x) (x##L)
|
||||
#define MK_ULL(x) (x##UL)
|
||||
#else
|
||||
#define MK_LL(x) (x##LL)
|
||||
#define MK_ULL(x) (x##ULL)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* definitions depending on c standard */
|
||||
#ifdef _CMATH_ANSI
|
||||
#define cmath_std_signbit MK_UL(0x7fffffff)
|
||||
#define cmath_std_float_t float
|
||||
#define cmath_std_int_t int
|
||||
#else
|
||||
#define cmath_std_signbit MK_ULL(0x7fffffffffffffff)
|
||||
#define cmath_std_float_t double
|
||||
#ifdef _MSC_VER
|
||||
#define cmath_std_int_t __int64
|
||||
#else
|
||||
#define cmath_std_int_t long long
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* types and constants */
|
||||
#ifndef cmath_t
|
||||
#define cmath_t double
|
||||
#endif
|
||||
|
||||
#define cmath_std_uint_t unsigned cmath_std_int_t
|
||||
|
||||
#define cmath_pi 3.1415926535897932384626433832795
|
||||
#define cmath_pi2 6.2831853071795864769252867665590
|
||||
#define cmath_pi_2 1.5707963267948966192313216916398
|
||||
#define cmath_e 2.7182818284590452353602874713526
|
||||
#define cmath_sqrt2 1.4142135623730950488016887242097
|
||||
#define cmath_pi_180 0.0174532925199432957692369076848
|
||||
#define cmath_180_pi 57.295779513082320876798154814105
|
||||
|
||||
#define cmath_int8_t char
|
||||
#define cmath_uint8_t unsigned char
|
||||
#define cmath_int16_t short
|
||||
#define cmath_uint16_t unsigned short
|
||||
#define cmath_int32_t int
|
||||
#define cmath_uint32_t unsigned int
|
||||
|
||||
/* aliased types */
|
||||
#ifdef __GNUC__
|
||||
#define _CMATH_MAY_ALIAS __attribute__((__may_alias__))
|
||||
#else
|
||||
#define _CMATH_MAY_ALIAS
|
||||
#endif
|
||||
|
||||
typedef cmath_t _CMATH_MAY_ALIAS cmath_t_a;
|
||||
|
||||
/* possible approximations */
|
||||
#define cmath_sin sin
|
||||
#define cmath_cos cos
|
||||
#define cmath_tan tan
|
||||
#define cmath_asin asin
|
||||
#define cmath_acos acos
|
||||
#define cmath_atan atan
|
||||
#define cmath_atan2 atan2
|
||||
#define cmath_sinh sinh
|
||||
#define cmath_cosh cosh
|
||||
#define cmath_tanh tanh
|
||||
#define cmath_exp exp
|
||||
#define cmath_pow pow
|
||||
#define cmath_sqrt sqrt
|
||||
#define cmath_log log
|
||||
#define cmath_log2 log2
|
||||
#define cmath_log10 log10
|
||||
|
||||
/* methods */
|
||||
#define cmath_array_size(x) \
|
||||
(sizeof(x) / sizeof(*(x)))
|
||||
|
||||
#define poly_order(x) \
|
||||
(sizeof(x) / sizeof(*(x)) - 1)
|
||||
|
||||
#define cmath_cabs(a, b) \
|
||||
cmath_sqrt((a)*(a) + (b)*(b))
|
||||
|
||||
#define cmath_carg(a, b) \
|
||||
cmath_atan2((b), (a))
|
||||
|
||||
#define cmath_radians(x) \
|
||||
((x)*cmath_pi_180)
|
||||
|
||||
#define cmath_degrees(x) \
|
||||
((x)*cmath_180_pi)
|
||||
|
||||
_CMATH_INLINE
|
||||
cmath_t cmath_powi(const cmath_t x, register cmath_uint16_t n)
|
||||
{
|
||||
register cmath_t result = 1;
|
||||
while (n--)
|
||||
result *= x;
|
||||
return result;
|
||||
}
|
||||
|
||||
_CMATH_INLINE
|
||||
cmath_t cmath_abs(const cmath_t x)
|
||||
{
|
||||
register union
|
||||
{
|
||||
cmath_std_int_t i;
|
||||
cmath_std_float_t j;
|
||||
} u;
|
||||
u.j = (cmath_std_float_t)x;
|
||||
u.i &= cmath_std_signbit;
|
||||
return u.j;
|
||||
}
|
||||
|
||||
_CMATH_INLINE
|
||||
cmath_t cmath_round(const cmath_t x)
|
||||
{
|
||||
if (x < 0.0)
|
||||
return (cmath_t)(cmath_std_int_t)(x - 0.5);
|
||||
else
|
||||
return (cmath_t)(cmath_std_int_t)(x + 0.5);
|
||||
}
|
||||
|
||||
#endif /* _CUSTOM_MATH_H_ */
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
durand_kerner.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
durand-kerner (weierstrass) algorithm for finding complex roots
|
||||
of polynomials.
|
||||
accuracy depends a lot on data type precision.
|
||||
*/
|
||||
|
||||
#ifndef _DURAND_KERNER_H_
|
||||
#define _DURAND_KERNER_H_
|
||||
|
||||
#include "horner.h"
|
||||
#include "custom_math.h"
|
||||
#include "complex_number.h"
|
||||
|
||||
/* settings */
|
||||
#ifdef _DURAND_KERNER_USE_INLINE_
|
||||
#define _DURAND_KERNER_INLINE _CMATH_INLINE
|
||||
#else
|
||||
#define _DURAND_KERNER_INLINE
|
||||
#endif
|
||||
|
||||
#define DK_EPSILON 1E-16
|
||||
#define DK_MAX_ITR 1E+3
|
||||
#define DK_MAX_N 256
|
||||
|
||||
const cnum_s dk_demoivre_c = {0.4, 0.9};
|
||||
|
||||
/* accepts an array of complex numbers */
|
||||
_DURAND_KERNER_INLINE
|
||||
void durand_kerner_c
|
||||
(const cnum_s *coeff, cnum_s *roots, const cmath_uint16_t order)
|
||||
{
|
||||
register cmath_uint16_t i, j;
|
||||
register cmath_uint32_t itr;
|
||||
cnum_s coeff_sc[DK_MAX_N];
|
||||
cnum_s x;
|
||||
cnum_s hor; /* needs an address or breaks g++ 4.x */
|
||||
|
||||
i = 0;
|
||||
while(i < order)
|
||||
{
|
||||
cnum_from(&roots[i], cnum_pow(dk_demoivre_c, i));
|
||||
i++;
|
||||
}
|
||||
|
||||
cnum_from(&coeff_sc[0], cnum_r1);
|
||||
i = 1;
|
||||
while(i < order+1)
|
||||
{
|
||||
cnum_from(&coeff_sc[i], cnum_div(coeff[i], coeff[0]));
|
||||
i++;
|
||||
}
|
||||
|
||||
itr = 0;
|
||||
while(itr < DK_MAX_ITR)
|
||||
{
|
||||
i = 0;
|
||||
while(i < order)
|
||||
{
|
||||
j = 0;
|
||||
x = cnum_r1;
|
||||
while (j < order)
|
||||
{
|
||||
if (i != j)
|
||||
x = cnum_mul(cnum_sub(roots[i], roots[j]), x);
|
||||
j++;
|
||||
}
|
||||
hor = horner_eval_c(coeff_sc, roots[i], order);
|
||||
x = cnum_div(hor, x);
|
||||
x = cnum_sub(roots[i], x);
|
||||
if (cmath_abs(cmath_abs(x.r) - cmath_abs(roots[i].r)) < DK_EPSILON &&
|
||||
cmath_abs(cmath_abs(x.i) - cmath_abs(roots[i].i)) < DK_EPSILON)
|
||||
return;
|
||||
cnum_from(&roots[i], x);
|
||||
i++;
|
||||
}
|
||||
itr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* accepts an array of real numbers */
|
||||
_DURAND_KERNER_INLINE
|
||||
void durand_kerner
|
||||
(const cmath_t *coeff, cnum_s *roots, const cmath_uint16_t order)
|
||||
{
|
||||
register cmath_uint16_t i;
|
||||
cnum_s coeff_c[DK_MAX_N];
|
||||
i = 0;
|
||||
while(i < (order+1))
|
||||
{
|
||||
cnum_set(&coeff_c[i], coeff[i], 0);
|
||||
i++;
|
||||
}
|
||||
durand_kerner_c(coeff_c, roots, order);
|
||||
}
|
||||
|
||||
#endif /* _DURAND_KERNER_H_ */
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
factorial.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
methods to return low-order factorials depending on allowed data types.
|
||||
|
||||
20! = 2432902008176640000 is the maximum factorial to be held
|
||||
in a unsigned 64bit integer.
|
||||
13! = 479001600 is the maximum factorial to be held in a unsigned
|
||||
32bit integer.
|
||||
*/
|
||||
|
||||
#ifndef _FACTORIAL_H_
|
||||
#define _FACTORIAL_H_
|
||||
|
||||
#include "custom_math.h"
|
||||
|
||||
#define FACTORIAL_LOWER \
|
||||
MK_ULL(1), \
|
||||
MK_ULL(1), \
|
||||
MK_ULL(2), \
|
||||
MK_ULL(6), \
|
||||
MK_ULL(24), \
|
||||
MK_ULL(120), \
|
||||
MK_ULL(720), \
|
||||
MK_ULL(5040), \
|
||||
MK_ULL(40320), \
|
||||
MK_ULL(362880), \
|
||||
MK_ULL(3628800), \
|
||||
MK_ULL(39916800), \
|
||||
MK_ULL(479001600)
|
||||
|
||||
#define FACTORIAL_HIGHER \
|
||||
MK_ULL(6227020800), \
|
||||
MK_ULL(87178291200), \
|
||||
MK_ULL(1307674368000), \
|
||||
MK_ULL(20922789888000), \
|
||||
MK_ULL(355687428096000), \
|
||||
MK_ULL(6402373705728000), \
|
||||
MK_ULL(121645100408832000), \
|
||||
MK_ULL(2432902008176640000)
|
||||
|
||||
static const cmath_std_uint_t _factorials[] =
|
||||
{
|
||||
#ifdef _CMATH_ANSI
|
||||
FACTORIAL_LOWER
|
||||
#else
|
||||
FACTORIAL_LOWER,
|
||||
FACTORIAL_HIGHER
|
||||
#endif
|
||||
};
|
||||
|
||||
static const cmath_t _inv_factorials[] =
|
||||
{
|
||||
1.00000000000000000000000000000000,
|
||||
1.00000000000000000000000000000000,
|
||||
0.50000000000000000000000000000000,
|
||||
0.16666666666666666666666666666667,
|
||||
0.04166666666666666666666666666666,
|
||||
0.00833333333333333333333333333333,
|
||||
0.00138888888888888888888888888888,
|
||||
0.00019841269841269841269841269841,
|
||||
0.00002480158730158730158730158730,
|
||||
0.00000275573192239858906525573192,
|
||||
0.00000027557319223985890652557319,
|
||||
0.00000002505210838544171877505210,
|
||||
0.00000000208767569878680989792100,
|
||||
0.00000000016059043836821614599390,
|
||||
0.00000000001147074559772972471385,
|
||||
0.00000000000076471637318198164750,
|
||||
0.00000000000004779477332387385297,
|
||||
0.00000000000000281145725434552076,
|
||||
0.00000000000000015619206968586225,
|
||||
0.00000000000000000822063524662433,
|
||||
0.00000000000000000041103176233122
|
||||
};
|
||||
|
||||
_CMATH_INLINE
|
||||
cmath_std_uint_t factorial(const cmath_uint32_t x)
|
||||
{
|
||||
if(x >= cmath_array_size(_factorials))
|
||||
return 0;
|
||||
else
|
||||
return _factorials[x];
|
||||
}
|
||||
|
||||
_CMATH_INLINE
|
||||
cmath_t inv_factorial(const cmath_uint32_t x)
|
||||
{
|
||||
if(x >= cmath_array_size(_inv_factorials))
|
||||
return 0;
|
||||
else
|
||||
return _inv_factorials[x];
|
||||
}
|
||||
|
||||
#endif /* _FACTORIAL_H_ */
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
horner.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
algorithm to evaluate integer order polynomials using horner's scheme.
|
||||
*/
|
||||
|
||||
#ifndef _HORNER_H_
|
||||
#define _HORNER_H_
|
||||
|
||||
#include "custom_math.h"
|
||||
#include "complex_number.h"
|
||||
|
||||
/* settings */
|
||||
#ifndef _HORNER_INLINE
|
||||
#define _HORNER_INLINE _CMATH_INLINE
|
||||
#else
|
||||
#define _HORNER_INLINE
|
||||
#endif
|
||||
|
||||
/* real */
|
||||
_HORNER_INLINE
|
||||
cmath_t horner_eval
|
||||
(const cmath_t *coeff, const cmath_t x, cmath_uint16_t order)
|
||||
{
|
||||
register cmath_t y = coeff[0];
|
||||
register cmath_uint16_t n = 1;
|
||||
order += 1;
|
||||
while(n < order)
|
||||
{
|
||||
y = y*x + coeff[n];
|
||||
n++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
/* complex */
|
||||
_HORNER_INLINE
|
||||
cnum_s horner_eval_c
|
||||
(const cnum_s *coeff, const cnum_s x, cmath_uint16_t order)
|
||||
{
|
||||
register cmath_uint16_t n = 1;
|
||||
cnum_s y = coeff[0];
|
||||
order += 1;
|
||||
while(n < order)
|
||||
{
|
||||
y = cnum_add(cnum_mul(y, x), coeff[n]);
|
||||
n++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
#endif /* _HORNER_H_ */
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
test_bessel.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
test bessel_polynomial.h and other related headers
|
||||
|
||||
gcc -W -Wall -Wextra -ansi pedantic
|
||||
cl /W4 /Za
|
||||
|
||||
reduced precisions for ansi c
|
||||
*/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "custom_math.h"
|
||||
#include "bessel_polynomial.h"
|
||||
#include "durand_kerner.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
register cmath_uint16_t i = 0;
|
||||
register cmath_int16_t diff = 0;
|
||||
|
||||
cmath_uint32_t in_order = _BESSEL_MAX_ORDER + 1;
|
||||
cmath_uint16_t order;
|
||||
const cmath_uint16_t reverse = 1;
|
||||
|
||||
cmath_std_int_t coeff[_BESSEL_MAX_ORDER + 1];
|
||||
|
||||
cnum_t dk_coeff[_BESSEL_MAX_ORDER + 1];
|
||||
cnum_s dk_roots[_BESSEL_MAX_ORDER];
|
||||
|
||||
/* */
|
||||
#ifdef _CMATH_ANSI
|
||||
puts("\n\nansi c is: on");
|
||||
#else
|
||||
puts("\n\nansi c is: off");
|
||||
#endif
|
||||
|
||||
/* */
|
||||
while (in_order > _BESSEL_MAX_ORDER)
|
||||
{
|
||||
printf("\nenter order of bessel polynomial (0 - %d): ", _BESSEL_MAX_ORDER);
|
||||
scanf("%u", &in_order);
|
||||
}
|
||||
|
||||
order = (cmath_uint16_t)in_order;
|
||||
bessel_polynomial(coeff, order, reverse);
|
||||
|
||||
printf("\norder [N]: %d", order);
|
||||
printf("\nreversed bessel: %d\n\n", reverse);
|
||||
printf("list of coefficients:\n");
|
||||
while (i <= order)
|
||||
{
|
||||
printf("order[%2d]: ", (order - i));
|
||||
printf("%"_CMATH_PR_STD_INT"\n", coeff[i]);
|
||||
i++;
|
||||
}
|
||||
puts("\npolynomial:");
|
||||
printf("y(x) = ");
|
||||
|
||||
i = 0;
|
||||
while (i <= order)
|
||||
{
|
||||
diff = (cmath_int16_t)(order - i);
|
||||
if (diff > 0)
|
||||
if (coeff[i] > 1)
|
||||
{
|
||||
printf("%"_CMATH_PR_STD_INT, coeff[i]);
|
||||
if (diff > 1)
|
||||
printf("*x^%d + ", diff);
|
||||
else
|
||||
printf("*x + ");
|
||||
}
|
||||
else
|
||||
printf("x^%d + ", diff);
|
||||
else
|
||||
printf("%"_CMATH_PR_STD_INT"", coeff[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
/* */
|
||||
puts("\n\nlist roots:");
|
||||
i = 0;
|
||||
while (i < order+1)
|
||||
{
|
||||
dk_coeff[i] = (cnum_t)coeff[i];
|
||||
i++;
|
||||
}
|
||||
|
||||
durand_kerner(dk_coeff, dk_roots, order);
|
||||
|
||||
i = 0;
|
||||
while (i < order)
|
||||
{
|
||||
printf("root[%2d]: %.15f \t % .15f*i\n",
|
||||
i+1, (double)dk_roots[i].r, (double)dk_roots[i].i);
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
test_eval.h
|
||||
Copyright (C) 2011 and later Lubomir I. Ivanov (neolit123 [at] gmail)
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
/*
|
||||
test horner.h for complex numbers and other related headers
|
||||
|
||||
gcc -W -Wall -Wextra -ansi pedantic
|
||||
cl /W4 /Za
|
||||
|
||||
reduced precisions for ansi c
|
||||
*/
|
||||
|
||||
#include "stdio.h"
|
||||
#include "complex_number.h"
|
||||
#include "horner.h"
|
||||
#include "durand_kerner.h"
|
||||
|
||||
int main(void)
|
||||
{
|
||||
cmath_uint16_t i = 0;
|
||||
|
||||
cnum_t y[] = {2, -6, 2, -1};
|
||||
cnum_s cy[] = {{2, 0}, {-6, 0}, {2, 0}, {-1, 0}};
|
||||
|
||||
cnum_t fx = horner_eval(y, 5, poly_order(y));
|
||||
cnum_s fcx = horner_eval_c(cy, _CNUM(5, 0), poly_order(y));
|
||||
|
||||
cnum_t dk_coeff[] = {12, -7, 0.001, 0, 3, -5};
|
||||
cnum_s dk_roots[5];
|
||||
|
||||
cnum_s dk_coeff_c[] = {{12, 0}, {-7, 0}, {0.001, 0}, {0, 0}, {3, 0}, {-5, 0}};
|
||||
cnum_s dk_roots_c[5];
|
||||
|
||||
durand_kerner(dk_coeff, dk_roots, poly_order(dk_coeff));
|
||||
durand_kerner_c(dk_coeff_c, dk_roots_c, poly_order(dk_coeff_c));
|
||||
|
||||
/* */
|
||||
#ifdef _CMATH_ANSI
|
||||
puts("\n\nansi c is: on");
|
||||
#else
|
||||
puts("\n\nansi c is: off");
|
||||
#endif
|
||||
|
||||
/* */
|
||||
puts("\n\nevaluate polynomials:\n");
|
||||
printf("* y[]: %.15f\n", (double)fx);
|
||||
printf("* cy[]: %.15f \t %.15f*i\n", (double)fcx.r, (double)fcx.i);
|
||||
|
||||
/* */
|
||||
puts("\nfind roots:");
|
||||
puts("\n* dk_coeff[]:");
|
||||
i = 0;
|
||||
while (i < poly_order(dk_coeff))
|
||||
{
|
||||
printf("root[%2d]: %.15f \t % .15f*i\n",
|
||||
i+1, (double)dk_roots[i].r, (double)dk_roots[i].i);
|
||||
i++;
|
||||
}
|
||||
i = 0;
|
||||
puts("\n* dk_coeff_c[]:");
|
||||
while (i < poly_order(dk_coeff_c))
|
||||
{
|
||||
printf("root[%2d]: %.15f \t % .15f*i\n",
|
||||
i+1, (double)dk_roots_c[i].r, (double)dk_roots_c[i].i);
|
||||
i++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
+1068
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
WDL - convoengine.h
|
||||
Copyright (C) 2006 and later Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
|
||||
This file provides an interface to the WDL fast convolution engine. This engine can convolve audio streams using
|
||||
either brute force (for small impulses), or a partitioned FFT scheme (for larger impulses).
|
||||
|
||||
Note that this library needs to have lookahead ability in order to process samples. Calling Add(somevalue) may produce Avail() < somevalue.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _WDL_CONVOENGINE_H_
|
||||
#define _WDL_CONVOENGINE_H_
|
||||
|
||||
#include "queue.h"
|
||||
#include "fastqueue.h"
|
||||
#include "fft.h"
|
||||
|
||||
//#define WDL_CONVO_WANT_FULLPRECISION_IMPULSE_STORAGE // define this for slowerness with -138dB error difference in resulting output (+-1 LSB at 24 bit)
|
||||
|
||||
#ifdef WDL_CONVO_WANT_FULLPRECISION_IMPULSE_STORAGE
|
||||
|
||||
typedef WDL_FFT_REAL WDL_CONVO_IMPULSEBUFf;
|
||||
typedef WDL_FFT_COMPLEX WDL_CONVO_IMPULSEBUFCPLXf;
|
||||
|
||||
#else
|
||||
typedef float WDL_CONVO_IMPULSEBUFf;
|
||||
typedef struct
|
||||
{
|
||||
WDL_CONVO_IMPULSEBUFf re, im;
|
||||
}
|
||||
WDL_CONVO_IMPULSEBUFCPLXf;
|
||||
#endif
|
||||
|
||||
class WDL_ImpulseBuffer
|
||||
{
|
||||
public:
|
||||
WDL_ImpulseBuffer()
|
||||
{
|
||||
samplerate=44100.0;
|
||||
impulses.list.Add(new WDL_TypedBuf<WDL_FFT_REAL>);
|
||||
}
|
||||
~WDL_ImpulseBuffer()
|
||||
{
|
||||
impulses.list.Empty(true);
|
||||
}
|
||||
|
||||
int GetLength() { return impulses.list.GetSize() ? impulses[0].GetSize() : 0; }
|
||||
int SetLength(int samples); // resizes/clears all channels accordingly, returns actual size set (can be 0 if error)
|
||||
void SetNumChannels(int usench, bool duplicateExisting=true); // handles allocating/converting/etc
|
||||
int GetNumChannels() { return impulses.list.GetSize(); }
|
||||
|
||||
double samplerate;
|
||||
struct {
|
||||
WDL_PtrList<WDL_TypedBuf<WDL_FFT_REAL> > list;
|
||||
|
||||
WDL_TypedBuf<WDL_FFT_REAL> &operator[](size_t i) const
|
||||
{
|
||||
WDL_TypedBuf<WDL_FFT_REAL> *p = list.Get(i);
|
||||
if (WDL_NORMALLY(p != NULL)) return *p;
|
||||
return *list.Get(0); // if for some reason an out of range index was passed, return first item rather than crash
|
||||
}
|
||||
} impulses;
|
||||
|
||||
};
|
||||
|
||||
class WDL_ConvolutionEngine
|
||||
{
|
||||
public:
|
||||
WDL_ConvolutionEngine();
|
||||
~WDL_ConvolutionEngine();
|
||||
|
||||
int SetImpulse(WDL_ImpulseBuffer *impulse, int fft_size=-1, int impulse_sample_offset=0, int max_imp_size=0, bool forceBrute=false);
|
||||
|
||||
int GetFFTSize() { return m_fft_size; }
|
||||
int GetLatency() { return m_fft_size/2; }
|
||||
|
||||
void Reset(); // clears out any latent samples
|
||||
|
||||
void Add(WDL_FFT_REAL **bufs, int len, int nch);
|
||||
|
||||
int Avail(int wantSamples);
|
||||
WDL_FFT_REAL **Get(); // returns length valid
|
||||
void Advance(int len);
|
||||
|
||||
private:
|
||||
|
||||
struct ImpChannelInfo {
|
||||
WDL_TypedBuf<WDL_CONVO_IMPULSEBUFf> imp;
|
||||
WDL_TypedBuf<char> zflag;
|
||||
};
|
||||
|
||||
struct ProcChannelInfo {
|
||||
WDL_Queue samplesout;
|
||||
WDL_Queue samplesin2;
|
||||
WDL_FastQueue samplesin;
|
||||
|
||||
WDL_TypedBuf<WDL_FFT_REAL> samplehist; // FFT'd sample blocks per channel
|
||||
WDL_TypedBuf<char> samplehist_zflag;
|
||||
WDL_TypedBuf<WDL_FFT_REAL> overlaphist;
|
||||
|
||||
int hist_pos;
|
||||
};
|
||||
|
||||
|
||||
WDL_PtrList<ImpChannelInfo> m_impdata;
|
||||
|
||||
int m_impulse_len;
|
||||
int m_fft_size;
|
||||
|
||||
int m_proc_nch;
|
||||
WDL_PtrList<ProcChannelInfo> m_proc;
|
||||
|
||||
|
||||
WDL_TypedBuf<WDL_FFT_REAL> m_combinebuf;
|
||||
WDL_TypedBuf<WDL_FFT_REAL *> m_get_tmpptrs;
|
||||
|
||||
public:
|
||||
|
||||
// _div stuff
|
||||
int m_zl_delaypos;
|
||||
int m_zl_dumpage;
|
||||
|
||||
//#define WDLCONVO_ZL_ACCOUNTING
|
||||
#ifdef WDLCONVO_ZL_ACCOUNTING
|
||||
int m_zl_fftcnt;//removeme (testing of benchmarks)
|
||||
#endif
|
||||
void AddSilenceToOutput(int len);
|
||||
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
// low latency version
|
||||
class WDL_ConvolutionEngine_Div
|
||||
{
|
||||
public:
|
||||
WDL_ConvolutionEngine_Div();
|
||||
~WDL_ConvolutionEngine_Div();
|
||||
|
||||
int SetImpulse(WDL_ImpulseBuffer *impulse, int maxfft_size=0, int known_blocksize=0, int max_imp_size=0, int impulse_offset=0, int latency_allowed=0);
|
||||
|
||||
int GetLatency();
|
||||
void Reset();
|
||||
|
||||
void Add(WDL_FFT_REAL **bufs, int len, int nch);
|
||||
|
||||
int Avail(int wantSamples);
|
||||
WDL_FFT_REAL **Get(); // returns length valid
|
||||
void Advance(int len);
|
||||
|
||||
private:
|
||||
WDL_PtrList<WDL_ConvolutionEngine> m_engines;
|
||||
|
||||
WDL_PtrList<WDL_Queue> m_sout;
|
||||
WDL_TypedBuf<WDL_FFT_REAL *> m_get_tmpptrs;
|
||||
|
||||
bool m_need_feedsilence;
|
||||
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,384 @@
|
||||
#ifndef _CAFCHANNELFORMATS_H_
|
||||
#define _CAFCHANNELFORMATS_H_
|
||||
|
||||
// from CoreAudioBaseTypes.h in the ios sdk, good luck finding it online
|
||||
|
||||
enum
|
||||
{
|
||||
// these are identical to the WAVEFORMATEXTENSIBLE bitmask
|
||||
kAudioChannelBit_Left = (1U<<0), // WAVEXT: SPEAKER_FRONT_LEFT
|
||||
kAudioChannelBit_Right = (1U<<1), // WAVEXT: SPEAKER_FRONT_RIGHT
|
||||
kAudioChannelBit_Center = (1U<<2), // WAVEXT: SPEAKER_FRONT_CENTER
|
||||
kAudioChannelBit_LFEScreen = (1U<<3), // WAVEXT: SPEAKER_LOW_FREQUENCY
|
||||
kAudioChannelBit_LeftSurround = (1U<<4), // WAVEXT: SPEAKER_BACK_LEFT
|
||||
kAudioChannelBit_RightSurround = (1U<<5), // WAVEXT: SPEAKER_BACK_RIGHT
|
||||
kAudioChannelBit_LeftCenter = (1U<<6), // WAVEXT: SPEAKER_FRONT_LEFT_OF_CENTER
|
||||
kAudioChannelBit_RightCenter = (1U<<7), // WAVEXT: SPEAKER_FROM_RIGHT_OF_CENTER
|
||||
kAudioChannelBit_CenterSurround = (1U<<8), // WAVEXT: SPEAKER_BACK_CENTER
|
||||
kAudioChannelBit_LeftSurroundDirect = (1U<<9), // WAVEXT: SPEAKER_SIDE_LEFT
|
||||
kAudioChannelBit_RightSurroundDirect = (1U<<10), // WAVEXT: SPEAKER_SIDE_RIGHT
|
||||
kAudioChannelBit_TopCenterSurround = (1U<<11), // WAVEXT: SPEAKER_TOP_CENTER
|
||||
kAudioChannelBit_VerticalHeightLeft = (1U<<12), // WAVEXT: SPEAKER_TOP_FRONT_LEFT
|
||||
kAudioChannelBit_VerticalHeightCenter = (1U<<13), // WAVEXT: SPEAKER_TOP_FRONT_CENTER
|
||||
kAudioChannelBit_VerticalHeightRight = (1U<<14), // WAVEXT: SPEAKER_TOP_FRONT_RIGHT
|
||||
kAudioChannelBit_TopBackLeft = (1U<<15), // WAVEXT: SPEAKER_TOP_BACK_LEFT
|
||||
kAudioChannelBit_TopBackCenter = (1U<<16), // WAVEXT: SPEAKER_TOP_BACK_CENTER
|
||||
kAudioChannelBit_TopBackRight = (1U<<17), // WAVEXT: SPEAKER_TOP_BACK_RIGHT
|
||||
kAudioChannelBit_LeftTopFront = (1U<<18),
|
||||
kAudioChannelBit_CenterTopFront = (1U<<19),
|
||||
kAudioChannelBit_RightTopFront = (1U<<20),
|
||||
kAudioChannelBit_LeftTopMiddle = (1U<<21),
|
||||
kAudioChannelBit_CenterTopMiddle = (1U<<22),
|
||||
kAudioChannelBit_RightTopMiddle = (1U<<23),
|
||||
kAudioChannelBit_LeftTopRear = (1U<<24),
|
||||
kAudioChannelBit_CenterTopRear = (1U<<25),
|
||||
kAudioChannelBit_RightTopRear = (1U<<26),
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
// Some channel abbreviations used below:
|
||||
// L - left
|
||||
// R - right
|
||||
// C - center
|
||||
// Ls - left surround
|
||||
// Rs - right surround
|
||||
// Cs - center surround
|
||||
// Rls - rear left surround
|
||||
// Rrs - rear right surround
|
||||
// Lw - left wide
|
||||
// Rw - right wide
|
||||
// Lsd - left surround direct
|
||||
// Rsd - right surround direct
|
||||
// Lc - left center
|
||||
// Rc - right center
|
||||
// Ts - top surround
|
||||
// Vhl - vertical height left
|
||||
// Vhc - vertical height center
|
||||
// Vhr - vertical height right
|
||||
// Ltf - left top front
|
||||
// Ctf - center top front
|
||||
// Rtf - right top front
|
||||
// Ltm - left top middle
|
||||
// Ctm - center top middle
|
||||
// Rtm - right top middle
|
||||
// Ltr - left top rear
|
||||
// Ctr - center top rear
|
||||
// Rtr - right top rear
|
||||
// Lt - left matrix total. for matrix encoded stereo.
|
||||
// Rt - right matrix total. for matrix encoded stereo.
|
||||
|
||||
// General layouts
|
||||
kAudioChannelLayoutTag_UseChannelDescriptions = (0U<<16) | 0, // use the array of AudioChannelDescriptions to define the mapping.
|
||||
kAudioChannelLayoutTag_UseChannelBitmap = (1U<<16) | 0, // use the bitmap to define the mapping.
|
||||
|
||||
kAudioChannelLayoutTag_Mono = (100U<<16) | 1, // a standard mono stream
|
||||
kAudioChannelLayoutTag_Stereo = (101U<<16) | 2, // a standard stereo stream (L R) - implied playback
|
||||
kAudioChannelLayoutTag_StereoHeadphones = (102U<<16) | 2, // a standard stereo stream (L R) - implied headphone playback
|
||||
kAudioChannelLayoutTag_MatrixStereo = (103U<<16) | 2, // a matrix encoded stereo stream (Lt, Rt)
|
||||
kAudioChannelLayoutTag_MidSide = (104U<<16) | 2, // mid/side recording
|
||||
kAudioChannelLayoutTag_XY = (105U<<16) | 2, // coincident mic pair (often 2 figure 8's)
|
||||
kAudioChannelLayoutTag_Binaural = (106U<<16) | 2, // binaural stereo (left, right)
|
||||
kAudioChannelLayoutTag_Ambisonic_B_Format = (107U<<16) | 4, // W, X, Y, Z
|
||||
|
||||
kAudioChannelLayoutTag_Quadraphonic = (108U<<16) | 4, // L R Ls Rs -- 90 degree speaker separation
|
||||
kAudioChannelLayoutTag_Pentagonal = (109U<<16) | 5, // L R Ls Rs C -- 72 degree speaker separation
|
||||
kAudioChannelLayoutTag_Hexagonal = (110U<<16) | 6, // L R Ls Rs C Cs -- 60 degree speaker separation
|
||||
kAudioChannelLayoutTag_Octagonal = (111U<<16) | 8, // L R Ls Rs C Cs Lw Rw -- 45 degree speaker separation
|
||||
kAudioChannelLayoutTag_Cube = (112U<<16) | 8, // left, right, rear left, rear right
|
||||
// top left, top right, top rear left, top rear right
|
||||
|
||||
// MPEG defined layouts
|
||||
kAudioChannelLayoutTag_MPEG_1_0 = kAudioChannelLayoutTag_Mono, // C
|
||||
kAudioChannelLayoutTag_MPEG_2_0 = kAudioChannelLayoutTag_Stereo, // L R
|
||||
kAudioChannelLayoutTag_MPEG_3_0_A = (113U<<16) | 3, // L R C
|
||||
kAudioChannelLayoutTag_MPEG_3_0_B = (114U<<16) | 3, // C L R
|
||||
kAudioChannelLayoutTag_MPEG_4_0_A = (115U<<16) | 4, // L R C Cs
|
||||
kAudioChannelLayoutTag_MPEG_4_0_B = (116U<<16) | 4, // C L R Cs
|
||||
kAudioChannelLayoutTag_MPEG_5_0_A = (117U<<16) | 5, // L R C Ls Rs
|
||||
kAudioChannelLayoutTag_MPEG_5_0_B = (118U<<16) | 5, // L R Ls Rs C
|
||||
kAudioChannelLayoutTag_MPEG_5_0_C = (119U<<16) | 5, // L C R Ls Rs
|
||||
kAudioChannelLayoutTag_MPEG_5_0_D = (120U<<16) | 5, // C L R Ls Rs
|
||||
kAudioChannelLayoutTag_MPEG_5_1_A = (121U<<16) | 6, // L R C LFE Ls Rs
|
||||
kAudioChannelLayoutTag_MPEG_5_1_B = (122U<<16) | 6, // L R Ls Rs C LFE
|
||||
kAudioChannelLayoutTag_MPEG_5_1_C = (123U<<16) | 6, // L C R Ls Rs LFE
|
||||
kAudioChannelLayoutTag_MPEG_5_1_D = (124U<<16) | 6, // C L R Ls Rs LFE
|
||||
kAudioChannelLayoutTag_MPEG_6_1_A = (125U<<16) | 7, // L R C LFE Ls Rs Cs
|
||||
kAudioChannelLayoutTag_MPEG_7_1_A = (126U<<16) | 8, // L R C LFE Ls Rs Lc Rc
|
||||
kAudioChannelLayoutTag_MPEG_7_1_B = (127U<<16) | 8, // C Lc Rc L R Ls Rs LFE (doc: IS-13818-7 MPEG2-AAC Table 3.1)
|
||||
kAudioChannelLayoutTag_MPEG_7_1_C = (128U<<16) | 8, // L R C LFE Ls Rs Rls Rrs
|
||||
kAudioChannelLayoutTag_Emagic_Default_7_1 = (129U<<16) | 8, // L R Ls Rs C LFE Lc Rc
|
||||
kAudioChannelLayoutTag_SMPTE_DTV = (130U<<16) | 8, // L R C LFE Ls Rs Lt Rt
|
||||
// (kAudioChannelLayoutTag_ITU_5_1 plus a matrix encoded stereo mix)
|
||||
// ITU defined layouts
|
||||
kAudioChannelLayoutTag_ITU_1_0 = kAudioChannelLayoutTag_Mono, // C
|
||||
kAudioChannelLayoutTag_ITU_2_0 = kAudioChannelLayoutTag_Stereo, // L R
|
||||
|
||||
kAudioChannelLayoutTag_ITU_2_1 = (131U<<16) | 3, // L R Cs
|
||||
kAudioChannelLayoutTag_ITU_2_2 = (132U<<16) | 4, // L R Ls Rs
|
||||
kAudioChannelLayoutTag_ITU_3_0 = kAudioChannelLayoutTag_MPEG_3_0_A, // L R C
|
||||
kAudioChannelLayoutTag_ITU_3_1 = kAudioChannelLayoutTag_MPEG_4_0_A, // L R C Cs
|
||||
|
||||
kAudioChannelLayoutTag_ITU_3_2 = kAudioChannelLayoutTag_MPEG_5_0_A, // L R C Ls Rs
|
||||
kAudioChannelLayoutTag_ITU_3_2_1 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
|
||||
kAudioChannelLayoutTag_ITU_3_4_1 = kAudioChannelLayoutTag_MPEG_7_1_C, // L R C LFE Ls Rs Rls Rrs
|
||||
|
||||
// DVD defined layouts
|
||||
kAudioChannelLayoutTag_DVD_0 = kAudioChannelLayoutTag_Mono, // C (mono)
|
||||
kAudioChannelLayoutTag_DVD_1 = kAudioChannelLayoutTag_Stereo, // L R
|
||||
kAudioChannelLayoutTag_DVD_2 = kAudioChannelLayoutTag_ITU_2_1, // L R Cs
|
||||
kAudioChannelLayoutTag_DVD_3 = kAudioChannelLayoutTag_ITU_2_2, // L R Ls Rs
|
||||
kAudioChannelLayoutTag_DVD_4 = (133U<<16) | 3, // L R LFE
|
||||
kAudioChannelLayoutTag_DVD_5 = (134U<<16) | 4, // L R LFE Cs
|
||||
kAudioChannelLayoutTag_DVD_6 = (135U<<16) | 5, // L R LFE Ls Rs
|
||||
kAudioChannelLayoutTag_DVD_7 = kAudioChannelLayoutTag_MPEG_3_0_A, // L R C
|
||||
kAudioChannelLayoutTag_DVD_8 = kAudioChannelLayoutTag_MPEG_4_0_A, // L R C Cs
|
||||
kAudioChannelLayoutTag_DVD_9 = kAudioChannelLayoutTag_MPEG_5_0_A, // L R C Ls Rs
|
||||
kAudioChannelLayoutTag_DVD_10 = (136U<<16) | 4, // L R C LFE
|
||||
kAudioChannelLayoutTag_DVD_11 = (137U<<16) | 5, // L R C LFE Cs
|
||||
kAudioChannelLayoutTag_DVD_12 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
|
||||
// 13 through 17 are duplicates of 8 through 12.
|
||||
kAudioChannelLayoutTag_DVD_13 = kAudioChannelLayoutTag_DVD_8, // L R C Cs
|
||||
kAudioChannelLayoutTag_DVD_14 = kAudioChannelLayoutTag_DVD_9, // L R C Ls Rs
|
||||
kAudioChannelLayoutTag_DVD_15 = kAudioChannelLayoutTag_DVD_10, // L R C LFE
|
||||
kAudioChannelLayoutTag_DVD_16 = kAudioChannelLayoutTag_DVD_11, // L R C LFE Cs
|
||||
kAudioChannelLayoutTag_DVD_17 = kAudioChannelLayoutTag_DVD_12, // L R C LFE Ls Rs
|
||||
kAudioChannelLayoutTag_DVD_18 = (138U<<16) | 5, // L R Ls Rs LFE
|
||||
kAudioChannelLayoutTag_DVD_19 = kAudioChannelLayoutTag_MPEG_5_0_B, // L R Ls Rs C
|
||||
kAudioChannelLayoutTag_DVD_20 = kAudioChannelLayoutTag_MPEG_5_1_B, // L R Ls Rs C LFE
|
||||
|
||||
// These layouts are recommended for AudioUnit usage
|
||||
// These are the symmetrical layouts
|
||||
kAudioChannelLayoutTag_AudioUnit_4 = kAudioChannelLayoutTag_Quadraphonic,
|
||||
kAudioChannelLayoutTag_AudioUnit_5 = kAudioChannelLayoutTag_Pentagonal,
|
||||
kAudioChannelLayoutTag_AudioUnit_6 = kAudioChannelLayoutTag_Hexagonal,
|
||||
kAudioChannelLayoutTag_AudioUnit_8 = kAudioChannelLayoutTag_Octagonal,
|
||||
// These are the surround-based layouts
|
||||
kAudioChannelLayoutTag_AudioUnit_5_0 = kAudioChannelLayoutTag_MPEG_5_0_B, // L R Ls Rs C
|
||||
kAudioChannelLayoutTag_AudioUnit_6_0 = (139U<<16) | 6, // L R Ls Rs C Cs
|
||||
kAudioChannelLayoutTag_AudioUnit_7_0 = (140U<<16) | 7, // L R Ls Rs C Rls Rrs
|
||||
kAudioChannelLayoutTag_AudioUnit_7_0_Front = (148U<<16) | 7, // L R Ls Rs C Lc Rc
|
||||
kAudioChannelLayoutTag_AudioUnit_5_1 = kAudioChannelLayoutTag_MPEG_5_1_A, // L R C LFE Ls Rs
|
||||
kAudioChannelLayoutTag_AudioUnit_6_1 = kAudioChannelLayoutTag_MPEG_6_1_A, // L R C LFE Ls Rs Cs
|
||||
kAudioChannelLayoutTag_AudioUnit_7_1 = kAudioChannelLayoutTag_MPEG_7_1_C, // L R C LFE Ls Rs Rls Rrs
|
||||
kAudioChannelLayoutTag_AudioUnit_7_1_Front = kAudioChannelLayoutTag_MPEG_7_1_A, // L R C LFE Ls Rs Lc Rc
|
||||
|
||||
kAudioChannelLayoutTag_AAC_3_0 = kAudioChannelLayoutTag_MPEG_3_0_B, // C L R
|
||||
kAudioChannelLayoutTag_AAC_Quadraphonic = kAudioChannelLayoutTag_Quadraphonic, // L R Ls Rs
|
||||
kAudioChannelLayoutTag_AAC_4_0 = kAudioChannelLayoutTag_MPEG_4_0_B, // C L R Cs
|
||||
kAudioChannelLayoutTag_AAC_5_0 = kAudioChannelLayoutTag_MPEG_5_0_D, // C L R Ls Rs
|
||||
kAudioChannelLayoutTag_AAC_5_1 = kAudioChannelLayoutTag_MPEG_5_1_D, // C L R Ls Rs Lfe
|
||||
kAudioChannelLayoutTag_AAC_6_0 = (141U<<16) | 6, // C L R Ls Rs Cs
|
||||
kAudioChannelLayoutTag_AAC_6_1 = (142U<<16) | 7, // C L R Ls Rs Cs Lfe
|
||||
kAudioChannelLayoutTag_AAC_7_0 = (143U<<16) | 7, // C L R Ls Rs Rls Rrs
|
||||
kAudioChannelLayoutTag_AAC_7_1 = kAudioChannelLayoutTag_MPEG_7_1_B, // C Lc Rc L R Ls Rs Lfe
|
||||
kAudioChannelLayoutTag_AAC_7_1_B = (183U<<16) | 8, // C L R Ls Rs Rls Rrs LFE
|
||||
kAudioChannelLayoutTag_AAC_7_1_C = (184U<<16) | 8, // C L R Ls Rs LFE Vhl Vhr
|
||||
kAudioChannelLayoutTag_AAC_Octagonal = (144U<<16) | 8, // C L R Ls Rs Rls Rrs Cs
|
||||
|
||||
kAudioChannelLayoutTag_TMH_10_2_std = (145U<<16) | 16, // L R C Vhc Lsd Rsd Ls Rs Vhl Vhr Lw Rw Csd Cs LFE1 LFE2
|
||||
kAudioChannelLayoutTag_TMH_10_2_full = (146U<<16) | 21, // TMH_10_2_std plus: Lc Rc HI VI Haptic
|
||||
|
||||
kAudioChannelLayoutTag_AC3_1_0_1 = (149U<<16) | 2, // C LFE
|
||||
kAudioChannelLayoutTag_AC3_3_0 = (150U<<16) | 3, // L C R
|
||||
kAudioChannelLayoutTag_AC3_3_1 = (151U<<16) | 4, // L C R Cs
|
||||
kAudioChannelLayoutTag_AC3_3_0_1 = (152U<<16) | 4, // L C R LFE
|
||||
kAudioChannelLayoutTag_AC3_2_1_1 = (153U<<16) | 4, // L R Cs LFE
|
||||
kAudioChannelLayoutTag_AC3_3_1_1 = (154U<<16) | 5, // L C R Cs LFE
|
||||
|
||||
kAudioChannelLayoutTag_EAC_6_0_A = (155U<<16) | 6, // L C R Ls Rs Cs
|
||||
kAudioChannelLayoutTag_EAC_7_0_A = (156U<<16) | 7, // L C R Ls Rs Rls Rrs
|
||||
|
||||
kAudioChannelLayoutTag_EAC3_6_1_A = (157U<<16) | 7, // L C R Ls Rs LFE Cs
|
||||
kAudioChannelLayoutTag_EAC3_6_1_B = (158U<<16) | 7, // L C R Ls Rs LFE Ts
|
||||
kAudioChannelLayoutTag_EAC3_6_1_C = (159U<<16) | 7, // L C R Ls Rs LFE Vhc
|
||||
kAudioChannelLayoutTag_EAC3_7_1_A = (160U<<16) | 8, // L C R Ls Rs LFE Rls Rrs
|
||||
kAudioChannelLayoutTag_EAC3_7_1_B = (161U<<16) | 8, // L C R Ls Rs LFE Lc Rc
|
||||
kAudioChannelLayoutTag_EAC3_7_1_C = (162U<<16) | 8, // L C R Ls Rs LFE Lsd Rsd
|
||||
kAudioChannelLayoutTag_EAC3_7_1_D = (163U<<16) | 8, // L C R Ls Rs LFE Lw Rw
|
||||
kAudioChannelLayoutTag_EAC3_7_1_E = (164U<<16) | 8, // L C R Ls Rs LFE Vhl Vhr
|
||||
|
||||
kAudioChannelLayoutTag_EAC3_7_1_F = (165U<<16) | 8, // L C R Ls Rs LFE Cs Ts
|
||||
kAudioChannelLayoutTag_EAC3_7_1_G = (166U<<16) | 8, // L C R Ls Rs LFE Cs Vhc
|
||||
kAudioChannelLayoutTag_EAC3_7_1_H = (167U<<16) | 8, // L C R Ls Rs LFE Ts Vhc
|
||||
|
||||
kAudioChannelLayoutTag_DTS_3_1 = (168U<<16) | 4, // C L R LFE
|
||||
kAudioChannelLayoutTag_DTS_4_1 = (169U<<16) | 5, // C L R Cs LFE
|
||||
kAudioChannelLayoutTag_DTS_6_0_A = (170U<<16) | 6, // Lc Rc L R Ls Rs
|
||||
kAudioChannelLayoutTag_DTS_6_0_B = (171U<<16) | 6, // C L R Rls Rrs Ts
|
||||
kAudioChannelLayoutTag_DTS_6_0_C = (172U<<16) | 6, // C Cs L R Rls Rrs
|
||||
kAudioChannelLayoutTag_DTS_6_1_A = (173U<<16) | 7, // Lc Rc L R Ls Rs LFE
|
||||
kAudioChannelLayoutTag_DTS_6_1_B = (174U<<16) | 7, // C L R Rls Rrs Ts LFE
|
||||
kAudioChannelLayoutTag_DTS_6_1_C = (175U<<16) | 7, // C Cs L R Rls Rrs LFE
|
||||
kAudioChannelLayoutTag_DTS_7_0 = (176U<<16) | 7, // Lc C Rc L R Ls Rs
|
||||
kAudioChannelLayoutTag_DTS_7_1 = (177U<<16) | 8, // Lc C Rc L R Ls Rs LFE
|
||||
kAudioChannelLayoutTag_DTS_8_0_A = (178U<<16) | 8, // Lc Rc L R Ls Rs Rls Rrs
|
||||
kAudioChannelLayoutTag_DTS_8_0_B = (179U<<16) | 8, // Lc C Rc L R Ls Cs Rs
|
||||
kAudioChannelLayoutTag_DTS_8_1_A = (180U<<16) | 9, // Lc Rc L R Ls Rs Rls Rrs LFE
|
||||
kAudioChannelLayoutTag_DTS_8_1_B = (181U<<16) | 9, // Lc C Rc L R Ls Cs Rs LFE
|
||||
kAudioChannelLayoutTag_DTS_6_1_D = (182U<<16) | 7, // C L R Ls Rs LFE Cs
|
||||
|
||||
kAudioChannelLayoutTag_WAVE_2_1 = kAudioChannelLayoutTag_DVD_4, // 3 channels, L R LFE
|
||||
kAudioChannelLayoutTag_WAVE_3_0 = kAudioChannelLayoutTag_MPEG_3_0_A, // 3 channels, L R C
|
||||
kAudioChannelLayoutTag_WAVE_4_0_A = kAudioChannelLayoutTag_ITU_2_2, // 4 channels, L R Ls Rs
|
||||
kAudioChannelLayoutTag_WAVE_4_0_B = (185U<<16) | 4, // 4 channels, L R Rls Rrs
|
||||
kAudioChannelLayoutTag_WAVE_5_0_A = kAudioChannelLayoutTag_MPEG_5_0_A, // 5 channels, L R C Ls Rs
|
||||
kAudioChannelLayoutTag_WAVE_5_0_B = (186U<<16) | 5, // 5 channels, L R C Rls Rrs
|
||||
kAudioChannelLayoutTag_WAVE_5_1_A = kAudioChannelLayoutTag_MPEG_5_1_A, // 6 channels, L R C LFE Ls Rs
|
||||
kAudioChannelLayoutTag_WAVE_5_1_B = (187U<<16) | 6, // 6 channels, L R C LFE Rls Rrs
|
||||
kAudioChannelLayoutTag_WAVE_6_1 = (188U<<16) | 7, // 7 channels, L R C LFE Cs Ls Rs
|
||||
kAudioChannelLayoutTag_WAVE_7_1 = (189U<<16) | 8, // 8 channels, L R C LFE Rls Rrs Ls Rs
|
||||
|
||||
kAudioChannelLayoutTag_HOA_ACN_SN3D = (190U<<16) | 0, // Higher Order Ambisonics, Ambisonics Channel Number, SN3D normalization
|
||||
// needs to be ORed with the actual number of channels (not the HOA order)
|
||||
kAudioChannelLayoutTag_HOA_ACN_N3D = (191U<<16) | 0, // Higher Order Ambisonics, Ambisonics Channel Number, N3D normalization
|
||||
// needs to be ORed with the actual number of channels (not the HOA order)
|
||||
|
||||
kAudioChannelLayoutTag_Atmos_5_1_2 = (194U<<16) | 8, ///< L R C LFE Ls Rs Ltm Rtm
|
||||
kAudioChannelLayoutTag_Atmos_5_1_4 = (195U<<16) | 10, ///< L R C LFE Ls Rs Vhl Vhr Ltr Rtr
|
||||
kAudioChannelLayoutTag_Atmos_7_1_2 = (196U<<16) | 10, ///< L R C LFE Ls Rs Rls Rrs Ltm Rtm
|
||||
kAudioChannelLayoutTag_Atmos_7_1_4 = (192U<<16) | 12, ///< L R C LFE Ls Rs Rls Rrs Vhl Vhr Ltr Rtr
|
||||
kAudioChannelLayoutTag_Atmos_9_1_6 = (193U<<16) | 16, ///< L R C LFE Ls Rs Rls Rrs Lw Rw Vhl Vhr Ltm Rtm Ltr Rtr // L R C LFE Ls Rs Ltm Rtm
|
||||
|
||||
kAudioChannelLayoutTag_DiscreteInOrder = (147U<<16) | 0, ///< needs to be ORed with the actual number of channels // needs to be ORed with the actual number of channels
|
||||
|
||||
kAudioChannelLayoutTag_BeginReserved = 0xF0000000, // Channel layout tag values in this range are reserved for internal use
|
||||
kAudioChannelLayoutTag_EndReserved = 0xFFFEFFFF,
|
||||
|
||||
kAudioChannelLayoutTag_Unknown = 0xFFFF0000 // needs to be ORed with the actual number of channels
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kAudioChannelLabel_Unknown = 0xFFFFFFFF, // unknown or unspecified other use
|
||||
kAudioChannelLabel_Unused = 0, // channel is present, but has no intended use or destination
|
||||
kAudioChannelLabel_UseCoordinates = 100, // channel is described by the mCoordinates fields.
|
||||
|
||||
kAudioChannelLabel_Left = 1,
|
||||
kAudioChannelLabel_Right = 2,
|
||||
kAudioChannelLabel_Center = 3,
|
||||
kAudioChannelLabel_LFEScreen = 4,
|
||||
kAudioChannelLabel_LeftSurround = 5,
|
||||
kAudioChannelLabel_RightSurround = 6,
|
||||
kAudioChannelLabel_LeftCenter = 7,
|
||||
kAudioChannelLabel_RightCenter = 8,
|
||||
kAudioChannelLabel_CenterSurround = 9, // WAVE: "Back Center" or plain "Rear Surround"
|
||||
kAudioChannelLabel_LeftSurroundDirect = 10,
|
||||
kAudioChannelLabel_RightSurroundDirect = 11,
|
||||
kAudioChannelLabel_TopCenterSurround = 12,
|
||||
kAudioChannelLabel_VerticalHeightLeft = 13, // WAVE: "Top Front Left"
|
||||
kAudioChannelLabel_VerticalHeightCenter = 14, // WAVE: "Top Front Center"
|
||||
kAudioChannelLabel_VerticalHeightRight = 15, // WAVE: "Top Front Right"
|
||||
|
||||
kAudioChannelLabel_TopBackLeft = 16,
|
||||
kAudioChannelLabel_TopBackCenter = 17,
|
||||
kAudioChannelLabel_TopBackRight = 18,
|
||||
|
||||
kAudioChannelLabel_RearSurroundLeft = 33,
|
||||
kAudioChannelLabel_RearSurroundRight = 34,
|
||||
kAudioChannelLabel_LeftWide = 35,
|
||||
kAudioChannelLabel_RightWide = 36,
|
||||
kAudioChannelLabel_LFE2 = 37,
|
||||
kAudioChannelLabel_LeftTotal = 38, // matrix encoded 4 channels
|
||||
kAudioChannelLabel_RightTotal = 39, // matrix encoded 4 channels
|
||||
kAudioChannelLabel_HearingImpaired = 40,
|
||||
kAudioChannelLabel_Narration = 41,
|
||||
kAudioChannelLabel_Mono = 42,
|
||||
kAudioChannelLabel_DialogCentricMix = 43,
|
||||
|
||||
kAudioChannelLabel_CenterSurroundDirect = 44, // back center, non diffuse
|
||||
|
||||
kAudioChannelLabel_Haptic = 45,
|
||||
|
||||
kAudioChannelLabel_LeftTopFront = 46,
|
||||
kAudioChannelLabel_CenterTopFront = 47,
|
||||
kAudioChannelLabel_RightTopFront = 48,
|
||||
kAudioChannelLabel_LeftTopMiddle = 49,
|
||||
kAudioChannelLabel_CenterTopMiddle = 50,
|
||||
kAudioChannelLabel_RightTopMiddle = 51,
|
||||
kAudioChannelLabel_LeftTopRear = 52,
|
||||
kAudioChannelLabel_CenterTopRear = 53,
|
||||
kAudioChannelLabel_RightTopRear = 54,
|
||||
|
||||
// first order ambisonic channels
|
||||
kAudioChannelLabel_Ambisonic_W = 200,
|
||||
kAudioChannelLabel_Ambisonic_X = 201,
|
||||
kAudioChannelLabel_Ambisonic_Y = 202,
|
||||
kAudioChannelLabel_Ambisonic_Z = 203,
|
||||
|
||||
// Mid/Side Recording
|
||||
kAudioChannelLabel_MS_Mid = 204,
|
||||
kAudioChannelLabel_MS_Side = 205,
|
||||
|
||||
// X-Y Recording
|
||||
kAudioChannelLabel_XY_X = 206,
|
||||
kAudioChannelLabel_XY_Y = 207,
|
||||
|
||||
// Binaural Recording
|
||||
kAudioChannelLabel_BinauralLeft = 208,
|
||||
kAudioChannelLabel_BinauralRight = 209,
|
||||
|
||||
// other
|
||||
kAudioChannelLabel_HeadphonesLeft = 301,
|
||||
kAudioChannelLabel_HeadphonesRight = 302,
|
||||
kAudioChannelLabel_ClickTrack = 304,
|
||||
kAudioChannelLabel_ForeignLanguage = 305,
|
||||
|
||||
// generic discrete channel
|
||||
kAudioChannelLabel_Discrete = 400,
|
||||
|
||||
// numbered discrete channel
|
||||
kAudioChannelLabel_Discrete_0 = (1U<<16) | 0,
|
||||
kAudioChannelLabel_Discrete_1 = (1U<<16) | 1,
|
||||
kAudioChannelLabel_Discrete_2 = (1U<<16) | 2,
|
||||
kAudioChannelLabel_Discrete_3 = (1U<<16) | 3,
|
||||
kAudioChannelLabel_Discrete_4 = (1U<<16) | 4,
|
||||
kAudioChannelLabel_Discrete_5 = (1U<<16) | 5,
|
||||
kAudioChannelLabel_Discrete_6 = (1U<<16) | 6,
|
||||
kAudioChannelLabel_Discrete_7 = (1U<<16) | 7,
|
||||
kAudioChannelLabel_Discrete_8 = (1U<<16) | 8,
|
||||
kAudioChannelLabel_Discrete_9 = (1U<<16) | 9,
|
||||
kAudioChannelLabel_Discrete_10 = (1U<<16) | 10,
|
||||
kAudioChannelLabel_Discrete_11 = (1U<<16) | 11,
|
||||
kAudioChannelLabel_Discrete_12 = (1U<<16) | 12,
|
||||
kAudioChannelLabel_Discrete_13 = (1U<<16) | 13,
|
||||
kAudioChannelLabel_Discrete_14 = (1U<<16) | 14,
|
||||
kAudioChannelLabel_Discrete_15 = (1U<<16) | 15,
|
||||
kAudioChannelLabel_Discrete_65535 = (1U<<16) | 65535,
|
||||
|
||||
// generic HOA ACN channel
|
||||
kAudioChannelLabel_HOA_ACN = 500,
|
||||
|
||||
// numbered HOA ACN channels
|
||||
kAudioChannelLabel_HOA_ACN_0 = (2U << 16) | 0,
|
||||
kAudioChannelLabel_HOA_ACN_1 = (2U << 16) | 1,
|
||||
kAudioChannelLabel_HOA_ACN_2 = (2U << 16) | 2,
|
||||
kAudioChannelLabel_HOA_ACN_3 = (2U << 16) | 3,
|
||||
kAudioChannelLabel_HOA_ACN_4 = (2U << 16) | 4,
|
||||
kAudioChannelLabel_HOA_ACN_5 = (2U << 16) | 5,
|
||||
kAudioChannelLabel_HOA_ACN_6 = (2U << 16) | 6,
|
||||
kAudioChannelLabel_HOA_ACN_7 = (2U << 16) | 7,
|
||||
kAudioChannelLabel_HOA_ACN_8 = (2U << 16) | 8,
|
||||
kAudioChannelLabel_HOA_ACN_9 = (2U << 16) | 9,
|
||||
kAudioChannelLabel_HOA_ACN_10 = (2U << 16) | 10,
|
||||
kAudioChannelLabel_HOA_ACN_11 = (2U << 16) | 11,
|
||||
kAudioChannelLabel_HOA_ACN_12 = (2U << 16) | 12,
|
||||
kAudioChannelLabel_HOA_ACN_13 = (2U << 16) | 13,
|
||||
kAudioChannelLabel_HOA_ACN_14 = (2U << 16) | 14,
|
||||
kAudioChannelLabel_HOA_ACN_15 = (2U << 16) | 15,
|
||||
kAudioChannelLabel_HOA_ACN_65024 = (2U << 16) | 65024, // 254th order uses 65025 channels
|
||||
|
||||
kAudioChannelLabel_BeginReserved = 0xF0000000, // Channel label values in this range are reserved for internal use
|
||||
kAudioChannelLabel_EndReserved = 0xFFFFFFFE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
kAudioChannelFlags_AllOff = 0,
|
||||
kAudioChannelFlags_RectangularCoordinates = (1U<<0),
|
||||
kAudioChannelFlags_SphericalCoordinates = (1U<<1),
|
||||
kAudioChannelFlags_Meters = (1U<<2)
|
||||
};
|
||||
|
||||
|
||||
#endif // _CAFCHANNELFORMATS_H_
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
#ifndef _WDL_DB2VAL_H_
|
||||
#define _WDL_DB2VAL_H_
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#define TWENTY_OVER_LN10 8.6858896380650365530225783783321
|
||||
#define LN10_OVER_TWENTY 0.11512925464970228420089957273422
|
||||
#define DB2VAL(x) exp((x)*LN10_OVER_TWENTY)
|
||||
|
||||
static inline double VAL2DB(double x)
|
||||
{
|
||||
if (x < 0.0000000298023223876953125) return -150.0;
|
||||
double v=log(x)*TWENTY_OVER_LN10;
|
||||
return v<-150.0?-150.0:v;
|
||||
}
|
||||
|
||||
static inline double VAL2DB_EX(double x, double mindb)
|
||||
{
|
||||
return x <= DB2VAL(mindb) ? mindb : (log(x)*TWENTY_OVER_LN10);
|
||||
}
|
||||
|
||||
#endif
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
#ifndef _WDL_DELAY_LINE_H_
|
||||
#define _WDL_DELAY_LINE_H_
|
||||
|
||||
#include "circbuf.h"
|
||||
|
||||
template<class SampleType> class WDL_DelayLine {
|
||||
public:
|
||||
WDL_DelayLine() : m_nch(1) { }
|
||||
~WDL_DelayLine() { }
|
||||
|
||||
// call before accessing anything.
|
||||
// total_size is maximum delay line length required (preserves contents on resize)
|
||||
// if valid_size >=0, ensures delay line has exactly valid_size pairs set (must be <= total_size!)
|
||||
void set_nch_length(int nch, int total_size, int valid_size=-1)
|
||||
{
|
||||
if (WDL_NOT_NORMALLY(valid_size > total_size)) valid_size=total_size;
|
||||
|
||||
int avail = get_avail_pairs();
|
||||
const int minsz = valid_size >= 0 ? valid_size : total_size;
|
||||
if (avail > minsz)
|
||||
{
|
||||
skip_pairs(avail-minsz);
|
||||
avail = minsz;
|
||||
}
|
||||
|
||||
if (m_nch != nch && WDL_NORMALLY(nch>0))
|
||||
{
|
||||
if (nch > m_nch) m_q.SetSizePreserveContents(total_size*nch);
|
||||
|
||||
SampleType work[2048];
|
||||
const int chunk = 2048 / wdl_max(nch,m_nch);
|
||||
int nleft = avail;
|
||||
while (nleft > 0)
|
||||
{
|
||||
const int a = wdl_min(chunk,nleft);
|
||||
nleft-=a;
|
||||
|
||||
m_q.Get(work,a*m_nch);
|
||||
VALIDATE_BUFFER(work,a*m_nch);
|
||||
reinterleave_buffer(work,m_nch,nch,a);
|
||||
m_q.Add(work,a*nch);
|
||||
}
|
||||
|
||||
if (nch < m_nch) m_q.SetSizePreserveContents(total_size*nch);
|
||||
|
||||
m_nch=nch;
|
||||
}
|
||||
else
|
||||
{
|
||||
const int newsz = total_size*nch, cursz = m_q.GetTotalSize();
|
||||
if (newsz > cursz || newsz < cursz/2) m_q.SetSizePreserveContents(newsz);
|
||||
}
|
||||
|
||||
if (valid_size > 0)
|
||||
{
|
||||
const int need_extra = valid_size - get_avail_pairs();
|
||||
if (need_extra > 0)
|
||||
{
|
||||
// insert zero data at read pointer in delay line
|
||||
m_q.Skip(-need_extra*nch);
|
||||
m_q.WriteAtReadPointer(NULL,need_extra*nch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_pairs(const SampleType *buf, int pairs) // pushes data off old end of queue
|
||||
{
|
||||
WDL_ASSERT(pairs>=0);
|
||||
if (pairs <= 0) return;
|
||||
|
||||
VALIDATE_BUFFER(buf,pairs*m_nch);
|
||||
|
||||
int add_sz = pairs*m_nch;
|
||||
if (add_sz > m_q.NbFree())
|
||||
{
|
||||
if (add_sz > m_q.GetTotalSize())
|
||||
{
|
||||
if (buf) buf += add_sz - m_q.GetTotalSize();
|
||||
add_sz = m_q.GetTotalSize();
|
||||
}
|
||||
m_q.Skip(add_sz - m_q.NbFree());
|
||||
}
|
||||
|
||||
const int added = m_q.Add(buf,add_sz);
|
||||
if (added != add_sz) { WDL_ASSERT(added == add_sz); }
|
||||
}
|
||||
|
||||
void unadd_pairs(int pairs)
|
||||
{
|
||||
WDL_ASSERT(pairs>=0);
|
||||
if (pairs>0) m_q.UnAdd(pairs*m_nch);
|
||||
}
|
||||
|
||||
int peek_pairs(SampleType *buf, int pairs, int offs=0) // allow to request more than available, returns amt returned
|
||||
{
|
||||
WDL_ASSERT(offs >= 0);
|
||||
const int avail = get_avail_pairs();
|
||||
const int rdsize = wdl_min(avail - offs, pairs);
|
||||
if (rdsize <= 0) return 0;
|
||||
|
||||
int ret = m_q.Peek(buf,offs*m_nch,rdsize*m_nch);
|
||||
WDL_ASSERT(ret == rdsize*m_nch);
|
||||
VALIDATE_BUFFER(buf,rdsize);
|
||||
return ret/m_nch;
|
||||
}
|
||||
|
||||
void get_pairs(SampleType *buf, int pairs) // asserts if pairs > available
|
||||
{
|
||||
int amt;
|
||||
WDL_ASSERT(pairs <= get_avail_pairs());
|
||||
if (buf)
|
||||
{
|
||||
amt = peek_pairs(buf,pairs,0);
|
||||
WDL_ASSERT(amt == pairs);
|
||||
VALIDATE_BUFFER(buf,amt);
|
||||
}
|
||||
else
|
||||
{
|
||||
amt = pairs;
|
||||
}
|
||||
skip_pairs(amt);
|
||||
}
|
||||
|
||||
void skip_pairs(int samt) { if (samt > 0) m_q.Skip(samt*m_nch); }
|
||||
int get_avail_pairs() const { return m_q.NbInBuf()/m_nch; }
|
||||
|
||||
void free_memory() { m_q.SetSize(0); } // frees memory
|
||||
void clear() { m_q.Reset(); } // keeps max buffer size intact but clears contents
|
||||
|
||||
static void reinterleave_buffer(SampleType *rdptr, int in_nch, int out_nch, int len)
|
||||
{
|
||||
if (len < 1 || !rdptr) return;
|
||||
|
||||
int x=len-1;
|
||||
SampleType *wrptr = rdptr;
|
||||
if (out_nch < in_nch)
|
||||
{
|
||||
const int sz1=out_nch*sizeof(SampleType);
|
||||
while (x--)
|
||||
{
|
||||
rdptr += in_nch;
|
||||
wrptr += out_nch;
|
||||
memmove(wrptr,rdptr,sz1);
|
||||
}
|
||||
}
|
||||
else if (out_nch > in_nch)
|
||||
{
|
||||
const int sz1=in_nch*sizeof(SampleType);
|
||||
const int sz2=(out_nch-in_nch)*sizeof(SampleType);
|
||||
rdptr += in_nch*x;
|
||||
wrptr += out_nch*x;
|
||||
while(x--)
|
||||
{
|
||||
memmove(wrptr,rdptr,sz1);
|
||||
memset(wrptr+in_nch,0,sz2);
|
||||
|
||||
rdptr-=in_nch;
|
||||
wrptr-=out_nch;
|
||||
}
|
||||
memset(wrptr+in_nch,0,sz2); // last iteration doesnt need memcpy (but does need clear)
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
WDL_TypedCircBuf<SampleType> m_q;
|
||||
int m_nch;
|
||||
|
||||
static void VALIDATE_BUFFER(const SampleType *buf, int cnt)
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
if (buf) for (int x = 0; x < cnt; x ++)
|
||||
{
|
||||
double v = buf[x];
|
||||
WDL_ASSERT(v >= -40000.0 && v < 40000.0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+248
@@ -0,0 +1,248 @@
|
||||
#ifndef _WDL_DENORMAL_H_
|
||||
#define _WDL_DENORMAL_H_
|
||||
|
||||
#include <string.h>
|
||||
#include "wdltypes.h"
|
||||
// note: the _aggressive versions filter out anything less than around 1.0e-16 or so (approximately) to 0.0, including -0.0 (becomes 0.0)
|
||||
// note: new! the _aggressive versions also filter inf and NaN to 0.0
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define WDL_DENORMAL_INLINE inline
|
||||
#elif defined(_MSC_VER)
|
||||
#define WDL_DENORMAL_INLINE __inline
|
||||
#else
|
||||
#ifdef WDL_STATICFUNC_UNUSED
|
||||
#define WDL_DENORMAL_INLINE WDL_STATICFUNC_UNUSED
|
||||
#else
|
||||
#define WDL_DENORMAL_INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_FLOAT_W(const float *a) { unsigned int v; memcpy(&v,a,sizeof(v)); return v; }
|
||||
static WDL_DENORMAL_INLINE unsigned int WDL_DENORMAL_DOUBLE_HW(const double *a) { WDL_UINT64 v; memcpy(&v,(char*)a,sizeof(v)); return (unsigned int) (v>>32); }
|
||||
|
||||
#define WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF 0x3cA00000 // 0x3B8000000 maybe instead? that's 10^-5 smaller or so
|
||||
#define WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF 0x25000000
|
||||
|
||||
|
||||
// define WDL_DENORMAL_WANTS_SCOPED_FTZ, and then use a WDL_denormal_ftz_scope in addition to denormal_*(), then
|
||||
// if FTZ is available it will be used instead...
|
||||
//
|
||||
#ifdef WDL_DENORMAL_WANTS_SCOPED_FTZ
|
||||
|
||||
#if defined(__SSE2__) || _M_IX86_FP >= 2 || defined(_M_X64)
|
||||
#define WDL_DENORMAL_FTZMODE
|
||||
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned int
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#else
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
#define wdl_denorm_mm_getcsr() _mm_getcsr()
|
||||
#define wdl_denorm_mm_setcsr(x) _mm_setcsr(x)
|
||||
#if defined(__SSE3__)
|
||||
#define wdl_denorm_mm_csr_mask (32768|4096|2048|1024|512|256|128|64) // FTZ, all exceptions, DAZ
|
||||
#else
|
||||
#define wdl_denorm_mm_csr_mask (32768|4096|2048|1024|512|256|128) // FTZ and all exceptions (target SSE2)
|
||||
#endif
|
||||
#elif defined(__arm__) || defined(__aarch64__)
|
||||
#define WDL_DENORMAL_FTZMODE
|
||||
#define WDL_DENORMAL_FTZSTATE_TYPE unsigned long
|
||||
static unsigned long __attribute__((unused)) wdl_denorm_mm_getcsr()
|
||||
{
|
||||
unsigned long rv;
|
||||
#ifdef __aarch64__
|
||||
asm volatile ( "mrs %0, fpcr" : "=r" (rv));
|
||||
#else
|
||||
asm volatile ( "fmrx %0, fpscr" : "=r" (rv));
|
||||
#endif
|
||||
return rv;
|
||||
}
|
||||
static void __attribute__((unused)) wdl_denorm_mm_setcsr(unsigned long v)
|
||||
{
|
||||
#ifdef __aarch64__
|
||||
asm volatile ( "msr fpcr, %0" :: "r"(v));
|
||||
#else
|
||||
asm volatile ( "fmxr fpscr, %0" :: "r"(v));
|
||||
#endif
|
||||
}
|
||||
#define wdl_denorm_mm_csr_mask (1<<24)
|
||||
#endif
|
||||
|
||||
class WDL_denormal_ftz_scope
|
||||
{
|
||||
public:
|
||||
WDL_denormal_ftz_scope()
|
||||
{
|
||||
#ifdef WDL_DENORMAL_FTZMODE
|
||||
const WDL_DENORMAL_FTZSTATE_TYPE b = wdl_denorm_mm_csr_mask;
|
||||
old_state = wdl_denorm_mm_getcsr();
|
||||
if ((need_restore = (old_state & b) != b))
|
||||
wdl_denorm_mm_setcsr(old_state|b);
|
||||
#endif
|
||||
}
|
||||
~WDL_denormal_ftz_scope()
|
||||
{
|
||||
#ifdef WDL_DENORMAL_FTZMODE
|
||||
if (need_restore) wdl_denorm_mm_setcsr(old_state);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef WDL_DENORMAL_FTZMODE
|
||||
WDL_DENORMAL_FTZSTATE_TYPE old_state;
|
||||
bool need_restore;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#if !defined(WDL_DENORMAL_FTZMODE) && !defined(WDL_DENORMAL_DO_NOT_FILTER)
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double(double a)
|
||||
{
|
||||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
|
||||
}
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double2(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) > 0x100000 ? a : 0.0;
|
||||
}
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_double_aggressive(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float(float a)
|
||||
{
|
||||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float2(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) > 0x800000 ? a : 0.0f;
|
||||
}
|
||||
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_float_aggressive(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_double(double *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_double_aggressive(double *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_float(float *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_float_aggressive(float *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus // automatic typed versions (though one should probably use the explicit versions...
|
||||
|
||||
|
||||
static double WDL_DENORMAL_INLINE denormal_filter(double a)
|
||||
{
|
||||
return (WDL_DENORMAL_DOUBLE_HW(&a)&0x7ff00000) ? a : 0.0;
|
||||
}
|
||||
static double WDL_DENORMAL_INLINE denormal_filter_aggressive(double a)
|
||||
{
|
||||
return ((WDL_DENORMAL_DOUBLE_HW(&a)+0x100000)&0x7ff00000) >= WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF ? a : 0.0;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter(float a)
|
||||
{
|
||||
return (WDL_DENORMAL_FLOAT_W(&a)&0x7f800000) ? a : 0.0f;
|
||||
}
|
||||
|
||||
static float WDL_DENORMAL_INLINE denormal_filter_aggressive(float a)
|
||||
{
|
||||
return ((WDL_DENORMAL_FLOAT_W(&a)+0x800000)&0x7f800000) >= WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF ? a : 0.0f;
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE denormal_fix(double *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_DOUBLE_HW(a)&0x7ff00000)) *a=0.0;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(double *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_DOUBLE_HW(a)+0x100000)&0x7ff00000) < WDL_DENORMAL_DOUBLE_AGGRESSIVE_CUTOFF) *a=0.0;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix(float *a)
|
||||
{
|
||||
if (!(WDL_DENORMAL_FLOAT_W(a)&0x7f800000)) *a=0.0f;
|
||||
}
|
||||
static void WDL_DENORMAL_INLINE denormal_fix_aggressive(float *a)
|
||||
{
|
||||
if (((WDL_DENORMAL_FLOAT_W(a)+0x800000)&0x7f800000) < WDL_DENORMAL_FLOAT_AGGRESSIVE_CUTOFF) *a=0.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // cplusplus versions
|
||||
|
||||
#else // end of !WDL_DENORMAL_DO_NOT_FILTER (and other platform-specific checks)
|
||||
|
||||
#define denormal_filter(x) (x)
|
||||
#define denormal_filter2(x) (x)
|
||||
#define denormal_filter_double(x) (x)
|
||||
#define denormal_filter_double2(x) (x)
|
||||
#define denormal_filter_double_aggressive(x) (x)
|
||||
#define denormal_filter_float(x) (x)
|
||||
#define denormal_filter_float2(x) (x)
|
||||
#define denormal_filter_float_aggressive(x) (x)
|
||||
#define denormal_filter_aggressive(x) (x)
|
||||
#define denormal_fix(x) do { } while(0)
|
||||
#define denormal_fix_aggressive(x) do { } while(0)
|
||||
#define denormal_fix_double(x) do { } while(0)
|
||||
#define denormal_fix_double_aggressive(x) do { } while(0)
|
||||
#define denormal_fix_float(x) do { } while(0)
|
||||
#define denormal_fix_float_aggressive(x) do { } while(0)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////
|
||||
// this isnt a denormal function but it is similar, so we'll put it here as a bonus
|
||||
|
||||
static void WDL_DENORMAL_INLINE GetDoubleMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
WDL_UINT64 i, o;
|
||||
memcpy(&i,in,sizeof(i));
|
||||
memcpy(&o,out,sizeof(o));
|
||||
i &= WDL_UINT64_CONST(0x7fffffffffffffff);
|
||||
if (i > o) memcpy(out,&i,sizeof(i));
|
||||
}
|
||||
|
||||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(float *out, const float *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
unsigned int i, o;
|
||||
memcpy(&i, in, sizeof(i));
|
||||
memcpy(&o, out, sizeof(o));
|
||||
i &= 0x7fffffff;
|
||||
if (i > o) memcpy(out, &i, sizeof(i));
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
static void WDL_DENORMAL_INLINE GetFloatMaxAbsValue(double *out, const double *in) // note: the value pointed to by "out" must be >=0.0, __NOT__ <= -0.0
|
||||
{
|
||||
GetDoubleMaxAbsValue(out,in);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
#ifndef _WDL_DESTROYCHECK_H_
|
||||
#define _WDL_DESTROYCHECK_H_
|
||||
|
||||
// this is a useful class for verifying that an object (usually "this") hasn't been destroyed:
|
||||
// to use it you add a WDL_DestroyState as a member of your class, then use the WDL_DestroyCheck
|
||||
// helper class (creating it when the pointer is known valid, and checking it later to see if it
|
||||
// is still valid).
|
||||
//
|
||||
// example:
|
||||
// class myClass {
|
||||
// WDL_DestroyState dest;
|
||||
// ...
|
||||
// };
|
||||
//
|
||||
// calling code (on myClass *classInstnace):
|
||||
// WDL_DestroyCheck chk(&classInstance->dest);
|
||||
// somefunction();
|
||||
// if (!chk.isOK()) printf("classInstance got deleted!\n");
|
||||
//
|
||||
// NOTE: only use this when these objects will be accessed from the same thread -- it will fail miserably
|
||||
// in a multithreaded environment
|
||||
|
||||
|
||||
|
||||
|
||||
class WDL_DestroyCheck
|
||||
{
|
||||
public:
|
||||
class WDL_DestroyStateNextRec { public: WDL_DestroyCheck *next; };
|
||||
|
||||
WDL_DestroyStateNextRec n, *prev;
|
||||
WDL_DestroyCheck(WDL_DestroyStateNextRec *state)
|
||||
{
|
||||
n.next=NULL;
|
||||
if ((prev=state))
|
||||
{
|
||||
if ((n.next=prev->next)) n.next->prev = &n;
|
||||
prev->next=this;
|
||||
}
|
||||
}
|
||||
~WDL_DestroyCheck()
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
prev->next = n.next;
|
||||
if (n.next) n.next->prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
bool isOK() { return !!prev; }
|
||||
};
|
||||
|
||||
class WDL_DestroyState : public WDL_DestroyCheck::WDL_DestroyStateNextRec
|
||||
{
|
||||
public:
|
||||
WDL_DestroyState() { next=NULL; }
|
||||
|
||||
~WDL_DestroyState()
|
||||
{
|
||||
WDL_DestroyCheck *p = next;
|
||||
while (p) { WDL_DestroyCheck *np = p->n.next; p->prev=NULL; p->n.next=NULL; p=np; }
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
#ifndef _WDL_DIFFCALC_H_
|
||||
#define _WDL_DIFFCALC_H_
|
||||
|
||||
#include "assocarray.h"
|
||||
|
||||
|
||||
|
||||
// Based on "An O(ND) Difference Algorithm and Its Variations", Myers
|
||||
// http://xmailserver.org/diff2.pdf
|
||||
|
||||
|
||||
template <class T> class WDL_DiffCalc
|
||||
{
|
||||
public:
|
||||
WDL_DiffCalc() {}
|
||||
virtual ~WDL_DiffCalc() {}
|
||||
|
||||
// cmp() returns 0 if the elements are equal.
|
||||
// returns the length of the merged list and populates m_rx, m_ry.
|
||||
int Diff(const T* x, const T* y, int nx, int ny, int (*cmp)(const T*, const T*))
|
||||
{
|
||||
m_rx.Resize(0, false);
|
||||
m_ry.Resize(0, false);
|
||||
ClearV();
|
||||
|
||||
if (!nx && !ny) return 0;
|
||||
if (!nx || !ny)
|
||||
{
|
||||
int i, n=max(nx, ny);
|
||||
for (i=0; i < n; ++i)
|
||||
{
|
||||
m_rx.Add(nx ? i : -1);
|
||||
m_ry.Add(ny ? i : -1);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
if (!cmp(x, y)) // special case
|
||||
{
|
||||
int i, n;
|
||||
for (n=1; n < min(nx, ny); ++n)
|
||||
{
|
||||
if (cmp(x+n, y+n)) break;
|
||||
}
|
||||
int len=Diff(x+n, y+n, nx-n, ny-n, cmp);
|
||||
int *rx=m_rx.Get(), *ry=m_ry.Get();
|
||||
for (i=0; i < len; ++i)
|
||||
{
|
||||
if (rx[i] >= 0) rx[i] += n;
|
||||
if (ry[i] >= 0) ry[i] += n;
|
||||
}
|
||||
len += n;
|
||||
while (n--)
|
||||
{
|
||||
m_rx.Insert(n, 0);
|
||||
m_ry.Insert(n, 0);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
SetV(0, 1, 0);
|
||||
int d, k, xi, yi;
|
||||
for (d=0; d <= nx+ny; ++d)
|
||||
{
|
||||
for (k=-d; k <= d ; k += 2)
|
||||
{
|
||||
if (k == -d || (k != d && GetV(d, k-1) < GetV(d, k+1)))
|
||||
{
|
||||
xi=GetV(d, k+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
xi=GetV(d, k-1)+1;
|
||||
}
|
||||
yi=xi-k;
|
||||
while (xi < nx && yi < ny && !cmp(x+xi, y+yi))
|
||||
{
|
||||
++xi;
|
||||
++yi;
|
||||
}
|
||||
SetV(d+1, k, xi);
|
||||
if (xi >= nx && yi >= ny) break;
|
||||
}
|
||||
if (xi >= nx && yi >= ny) break;
|
||||
}
|
||||
|
||||
int len=(nx+ny+d)/2;
|
||||
int *rx=m_rx.Resize(len);
|
||||
int *ry=m_ry.Resize(len);
|
||||
int pos=len;
|
||||
|
||||
while (d)
|
||||
{
|
||||
while (xi > 0 && yi > 0 && !cmp(x+xi-1, y+yi-1))
|
||||
{
|
||||
--pos;
|
||||
rx[pos]=--xi;
|
||||
ry[pos]=--yi;
|
||||
}
|
||||
--pos;
|
||||
if (k == -d || (k != d && GetV(d, k-1) < GetV(d, k+1)))
|
||||
{
|
||||
++k;
|
||||
rx[pos]=-1;
|
||||
ry[pos]=--yi;
|
||||
}
|
||||
else
|
||||
{
|
||||
--k;
|
||||
rx[pos]=--xi;
|
||||
ry[pos]=-1;
|
||||
}
|
||||
--d;
|
||||
}
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
// m_rx, m_ry hold the index of each merged list element in x and y,
|
||||
// or -1 if the merged list element is not an element in that source list.
|
||||
// example: X="ABCABBA", Y="CBABAC"
|
||||
// 0123456 012345
|
||||
// WDL_Merge() returns "ABCBABBAC"
|
||||
// 012 3456
|
||||
// 0123 45
|
||||
// m_rx={ 0, 1, 2, -1, 3, 4, 5, 6, -1}
|
||||
// m_ry={-1, -1, 0, 1, 2, 3, -1, 4, 5}
|
||||
WDL_TypedBuf<int> m_rx, m_ry;
|
||||
|
||||
private:
|
||||
|
||||
WDL_IntKeyedArray<int> m_v; // x coord of d-contour on row k
|
||||
void ClearV()
|
||||
{
|
||||
m_v.DeleteAll();
|
||||
}
|
||||
void SetV(int d, int k, int xi)
|
||||
{
|
||||
m_v.Insert(_key(d, k), xi);
|
||||
}
|
||||
int GetV(int d, int k)
|
||||
{
|
||||
return m_v.Get(_key(d, k));
|
||||
}
|
||||
static int _key(int d, int k) { return (d<<16)|(k+(1<<15)); }
|
||||
};
|
||||
|
||||
|
||||
// this is a separate function from WDL_DiffCalc only because it requires T::operator=
|
||||
template <class T> int WDL_Merge(const T* x, const T* y, int nx, int ny,
|
||||
int (*cmp)(const T*, const T*), T* list)
|
||||
{
|
||||
WDL_DiffCalc<T> dc;
|
||||
int i, n=dc.Diff(x, y, nx, ny, cmp);
|
||||
int *rx=dc.m_rx.Get(), *ry=dc.m_ry.Get();
|
||||
for (i=0; i < n; ++i)
|
||||
{
|
||||
if (list) list[i]=(rx[i] >= 0 ? x[rx[i]] : y[ry[i]]);
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif
|
||||
+329
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
WDL - dirscan.h
|
||||
Copyright (C) 2005 and later Cockos Incorporated
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
This file provides the interface and implementation for WDL_DirScan, a simple
|
||||
(and somewhat portable) directory reading class. On non-Win32 systems it wraps
|
||||
opendir()/readdir()/etc. On Win32, it uses FindFirst*, and supports wildcards as
|
||||
well.
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _WDL_DIRSCAN_H_
|
||||
#define _WDL_DIRSCAN_H_
|
||||
|
||||
#include "wdlstring.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
extern struct stat wdl_stat_chk;
|
||||
// if this fails on linux, use CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
typedef char wdl_dirscan_assert_failed_stat_not_64[sizeof(wdl_stat_chk.st_size)!=8 ? -1 : 1];
|
||||
#endif
|
||||
|
||||
class WDL_DirScan
|
||||
{
|
||||
public:
|
||||
WDL_DirScan() :
|
||||
#ifdef _WIN32
|
||||
m_h(INVALID_HANDLE_VALUE)
|
||||
#ifndef WDL_NO_SUPPORT_UTF8
|
||||
, m_wcmode(false)
|
||||
#endif
|
||||
#else
|
||||
m_h(NULL), m_ent(NULL)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
~WDL_DirScan()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
int First(const char *dirname
|
||||
#ifdef _WIN32
|
||||
, int isExactSpec=0
|
||||
#endif
|
||||
) // returns 0 if success
|
||||
{
|
||||
WDL_FastString scanstr(dirname);
|
||||
const int l = scanstr.GetLength();
|
||||
if (l < 1) return -1;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!isExactSpec)
|
||||
{
|
||||
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1);
|
||||
m_leading_path = scanstr;
|
||||
scanstr.Append("\\*");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_leading_path = scanstr;
|
||||
|
||||
// remove trailing wildcards and directory separator from m_leading_path
|
||||
const char *sp = m_leading_path.Get();
|
||||
int idx = m_leading_path.GetLength() - 1;
|
||||
while (idx > 0 && sp[idx] != '/' && sp[idx] != '\\') idx--;
|
||||
if (idx > 0) m_leading_path.SetLen(idx);
|
||||
}
|
||||
#else
|
||||
if (dirname[l-1] == '\\' || dirname[l-1] == '/') scanstr.SetLen(l-1);
|
||||
m_leading_path = scanstr;
|
||||
if (!scanstr.GetLength()) scanstr.Set("/"); // fix for scanning /
|
||||
#endif
|
||||
|
||||
Close();
|
||||
#ifdef _WIN32
|
||||
#ifndef WDL_NO_SUPPORT_UTF8
|
||||
m_h=INVALID_HANDLE_VALUE;
|
||||
#ifdef WDL_SUPPORT_WIN9X
|
||||
m_wcmode = GetVersion()< 0x80000000;
|
||||
#else
|
||||
m_wcmode = true;
|
||||
#endif
|
||||
|
||||
if (m_wcmode)
|
||||
{
|
||||
int reqbuf = MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,NULL,0);
|
||||
if (reqbuf > 1000)
|
||||
{
|
||||
WDL_TypedBuf<WCHAR> tmp;
|
||||
tmp.Resize(reqbuf+20);
|
||||
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,tmp.Get(),tmp.GetSize()-10))
|
||||
{
|
||||
correctlongpath(tmp.Get());
|
||||
m_h=FindFirstFileW(tmp.Get(),&m_fd);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WCHAR wfilename[1024];
|
||||
if (MultiByteToWideChar(CP_UTF8,MB_ERR_INVALID_CHARS,scanstr.Get(),-1,wfilename,1024-10))
|
||||
{
|
||||
correctlongpath(wfilename);
|
||||
m_h=FindFirstFileW(wfilename,&m_fd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_h==INVALID_HANDLE_VALUE) m_wcmode=false;
|
||||
|
||||
if (m_h==INVALID_HANDLE_VALUE)
|
||||
#endif
|
||||
m_h=FindFirstFileA(scanstr.Get(),(WIN32_FIND_DATAA*)&m_fd);
|
||||
return (m_h == INVALID_HANDLE_VALUE);
|
||||
#else
|
||||
m_ent=0;
|
||||
m_h=opendir(scanstr.Get());
|
||||
return !m_h || Next();
|
||||
#endif
|
||||
}
|
||||
int Next() // returns 0 on success
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_h == INVALID_HANDLE_VALUE) return -1;
|
||||
#ifndef WDL_NO_SUPPORT_UTF8
|
||||
if (m_wcmode) return !FindNextFileW(m_h,&m_fd);
|
||||
#endif
|
||||
return !FindNextFileA(m_h,(WIN32_FIND_DATAA*)&m_fd);
|
||||
#else
|
||||
if (!m_h) return -1;
|
||||
return !(m_ent=readdir(m_h));
|
||||
#endif
|
||||
}
|
||||
void Close()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (m_h != INVALID_HANDLE_VALUE) FindClose(m_h);
|
||||
m_h=INVALID_HANDLE_VALUE;
|
||||
#else
|
||||
if (m_h) closedir(m_h);
|
||||
m_h=0; m_ent=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
const char *GetCurrentFN()
|
||||
{
|
||||
#ifndef WDL_NO_SUPPORT_UTF8
|
||||
if (m_wcmode)
|
||||
{
|
||||
if (!WideCharToMultiByte(CP_UTF8,0,m_fd.cFileName,-1,m_tmpbuf,sizeof(m_tmpbuf),NULL,NULL))
|
||||
m_tmpbuf[0]=0;
|
||||
return m_tmpbuf;
|
||||
}
|
||||
#endif
|
||||
return ((WIN32_FIND_DATAA *)&m_fd)->cFileName;
|
||||
}
|
||||
#else
|
||||
const char *GetCurrentFN() const { return m_ent?m_ent->d_name : ""; }
|
||||
#endif
|
||||
template<class T> void GetCurrentFullFN(T *str)
|
||||
{
|
||||
str->Set(m_leading_path.Get());
|
||||
#ifdef _WIN32
|
||||
str->Append("\\");
|
||||
#else
|
||||
str->Append("/");
|
||||
#endif
|
||||
str->Append(GetCurrentFN());
|
||||
}
|
||||
int GetCurrentIsDirectory() const // returns 1 if dir, 2 if symlink to dir, 4 if possibly-recursive symlink to dir
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return !!(m_fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
||||
#else
|
||||
char tmp[2048];
|
||||
if (m_ent) switch (m_ent->d_type)
|
||||
{
|
||||
case DT_DIR: return 1;
|
||||
case DT_LNK:
|
||||
{
|
||||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name);
|
||||
char *rp = realpath(tmp,NULL);
|
||||
if (!rp) return 0;
|
||||
|
||||
struct stat sb;
|
||||
int ret = (!stat(rp,&sb) && (sb.st_mode & S_IFMT) == S_IFDIR) ? 2 : 0;
|
||||
if (ret)
|
||||
{
|
||||
// treat symlinks of /path/to/foo -> /path from being resolved (avoiding obvious feedback loops)
|
||||
const int rpl = (int) strlen(rp);
|
||||
if (
|
||||
#ifdef __APPLE__
|
||||
!strnicmp(rp,m_leading_path.Get(),rpl)
|
||||
#else
|
||||
!strncmp(rp,m_leading_path.Get(),rpl)
|
||||
#endif
|
||||
&& (m_leading_path.Get()[rpl] == '/' || m_leading_path.Get()[rpl] == 0)
|
||||
) ret = 4;
|
||||
}
|
||||
free(rp);
|
||||
return ret;
|
||||
}
|
||||
case DT_UNKNOWN:
|
||||
{
|
||||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),m_ent->d_name);
|
||||
DIR *d = opendir(tmp);
|
||||
if (d) { closedir(d); return 1; }
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// these are somewhat windows specific calls, eh
|
||||
#ifdef _WIN32
|
||||
DWORD GetCurrentFileSize(DWORD *HighWord=NULL) const { if (HighWord) *HighWord = m_fd.nFileSizeHigh; return m_fd.nFileSizeLow; }
|
||||
void GetCurrentLastWriteTime(FILETIME *ft) const { *ft = m_fd.ftLastWriteTime; }
|
||||
void GetCurrentLastAccessTime(FILETIME *ft) const { *ft = m_fd.ftLastAccessTime; }
|
||||
void GetCurrentCreationTime(FILETIME *ft) const { *ft = m_fd.ftCreationTime; }
|
||||
DWORD GetCurrentAttributes() const { return m_fd.dwFileAttributes; }
|
||||
#elif defined(_WDL_SWELL_H_)
|
||||
|
||||
void GetCurrentCreationTime(FILETIME *ft)
|
||||
{
|
||||
char tmp[2048];
|
||||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
|
||||
struct stat st={0,};
|
||||
stat(tmp,&st);
|
||||
unsigned long long a=(unsigned long long)st.st_ctime; // seconds since january 1st, 1970
|
||||
a+=11644473600ull; // 1601->1970
|
||||
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
|
||||
ft->dwLowDateTime=a & 0xffffffff;
|
||||
ft->dwHighDateTime=a>>32;
|
||||
}
|
||||
|
||||
void GetCurrentLastWriteTime(FILETIME *ft)
|
||||
{
|
||||
char tmp[2048];
|
||||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
|
||||
struct stat st={0,};
|
||||
stat(tmp,&st);
|
||||
unsigned long long a=(unsigned long long)st.st_mtime; // seconds since january 1st, 1970
|
||||
a+=11644473600ull; // 1601->1970
|
||||
a*=10000000; // seconds to 1/10th microseconds (100 nanoseconds)
|
||||
ft->dwLowDateTime=a & 0xffffffff;
|
||||
ft->dwHighDateTime=a>>32;
|
||||
}
|
||||
DWORD GetCurrentFileSize(DWORD *HighWord=NULL)
|
||||
{
|
||||
char tmp[2048];
|
||||
snprintf(tmp,sizeof(tmp),"%s/%s",m_leading_path.Get(),GetCurrentFN());
|
||||
struct stat st={0,};
|
||||
stat(tmp,&st);
|
||||
|
||||
if (HighWord) *HighWord = (DWORD)(st.st_size>>32);
|
||||
return (DWORD)(st.st_size&0xffffffff);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef WDL_NO_SUPPORT_UTF8
|
||||
bool m_wcmode;
|
||||
WIN32_FIND_DATAW m_fd;
|
||||
char m_tmpbuf[MAX_PATH*5]; // even if each byte gets encoded as 4 utf-8 bytes this should be plenty ;)
|
||||
#else
|
||||
WIN32_FIND_DATAA m_fd;
|
||||
#endif
|
||||
HANDLE m_h;
|
||||
#else
|
||||
DIR *m_h;
|
||||
struct dirent *m_ent;
|
||||
#endif
|
||||
WDL_FastString m_leading_path;
|
||||
|
||||
#ifdef _WIN32
|
||||
static void correctlongpath(WCHAR *buf) // this also exists as wdl_utf8_correctlongpath
|
||||
{
|
||||
const WCHAR *insert;
|
||||
WCHAR *wr;
|
||||
int skip = 0;
|
||||
if (!buf || !buf[0] || wcslen(buf) < 256) return;
|
||||
if (buf[1] == ':') insert=L"\\\\?\\";
|
||||
else if (buf[0] == '\\' && buf[1] == '\\') { insert = L"\\\\?\\UNC\\"; skip=2; }
|
||||
else return;
|
||||
|
||||
wr = buf + wcslen(insert);
|
||||
memmove(wr, buf + skip, (wcslen(buf+skip)+1)*2);
|
||||
memmove(buf,insert,wcslen(insert)*2);
|
||||
while (*wr)
|
||||
{
|
||||
if (*wr == '/') *wr = '\\';
|
||||
wr++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
} WDL_FIXALIGN;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
/*.obj
|
||||
/asm-nseel-x64-macho.asm
|
||||
/asm-nseel-x64.asm
|
||||
/loose_eel.exe
|
||||
|
||||
!/asm-nseel-x64-macho.o
|
||||
!/asm-nseel-x64.obj
|
||||
!/asm-nseel-arm64ec.obj
|
||||
@@ -0,0 +1,172 @@
|
||||
CC=gcc
|
||||
CFLAGS=-g -DWDL_FFT_REALSIZE=8 -Wall -Wno-unused-function -Wno-multichar -Wno-unused-result -Wshadow -Wtype-limits
|
||||
LFLAGS=
|
||||
CXX=g++
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -D_DEBUG -O0 -DWDL_CHECK_FOR_NON_UTF8_FOPEN
|
||||
else
|
||||
CFLAGS += -DNDEBUG -O
|
||||
endif
|
||||
|
||||
CFLAGS += -D_FILE_OFFSET_BITS=64
|
||||
|
||||
OBJS=nseel-caltab.o nseel-compiler.o nseel-eval.o nseel-lextab.o nseel-ram.o nseel-yylex.o nseel-cfunc.o fft.o
|
||||
|
||||
SWELL_OBJS=
|
||||
LICE_OBJS=
|
||||
|
||||
OBJS2=
|
||||
|
||||
UNAME_S := $(shell uname -s)
|
||||
ARCH := $(shell uname -m)
|
||||
|
||||
ifeq ($(ARCH), aarch64)
|
||||
ifeq ($(shell $(CC) -dumpmachine | cut -f 1 -d -), arm)
|
||||
# helper for armv7l userspace on aarch64 cpu
|
||||
ARCH := armv7l
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
CC=clang
|
||||
CXX=clang++
|
||||
CFLAGS += -arch $(ARCH)
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH),arm64)
|
||||
CFLAGS += -fsigned-char
|
||||
else
|
||||
ifneq ($(filter arm%,$(ARCH)),)
|
||||
CFLAGS += -fsigned-char -mfpu=vfp -march=armv6t2 -marm
|
||||
endif
|
||||
ifeq ($(ARCH),aarch64)
|
||||
CFLAGS += -fsigned-char
|
||||
endif
|
||||
endif
|
||||
|
||||
ifndef ALLOW_WARNINGS
|
||||
ifneq ($(UNAME_S),Darwin)
|
||||
CFLAGS += -Werror
|
||||
endif
|
||||
endif
|
||||
ifndef DEPRECATED_WARNINGS
|
||||
CFLAGS += -Wno-deprecated-declarations
|
||||
endif
|
||||
|
||||
|
||||
default: loose_eel eel_pp
|
||||
|
||||
nseel-compiler.o: glue*.h ns-eel*.h
|
||||
nseel-cfunc.o: asm*.c ns-eel*.h
|
||||
loose_eel.o: eel*.h ns-eel*.h
|
||||
nseel-*.o: ns-eel*.h
|
||||
|
||||
vpath %.cpp ../lice ../swell
|
||||
vpath %.mm ../swell
|
||||
vpath %.c ../
|
||||
|
||||
ifdef MAXLOOP
|
||||
CFLAGS += -DNSEEL_LOOPFUNC_SUPPORT_MAXLEN=$(MAXLOOP)
|
||||
else
|
||||
CFLAGS += -DNSEEL_LOOPFUNC_SUPPORT_MAXLEN=0
|
||||
endif
|
||||
|
||||
ifdef DISASSEMBLE
|
||||
CFLAGS += -DEELSCRIPT_DO_DISASSEMBLE
|
||||
endif
|
||||
|
||||
ifndef NO_GFX
|
||||
LICE_OBJS += lice.o lice_image.o lice_line.o lice_ico.o lice_bmp.o lice_textnew.o lice_text.o lice_arc.o
|
||||
CFLAGS += -DEEL_LICE_WANT_STANDALONE
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
CLANG_VER := $(shell clang --version|head -n 1| sed 's/.*version \([0-9][0-9]*\).*/\1/' )
|
||||
CLANG_GT_9 := $(shell [ $(CLANG_VER) -gt 9 ] && echo true )
|
||||
ifeq ($(CLANG_GT_9),true)
|
||||
CFLAGS += -mmacosx-version-min=10.7 -stdlib=libc++
|
||||
else
|
||||
CFLAGS += -mmacosx-version-min=10.5
|
||||
endif
|
||||
SWELL_OBJS += swell-wnd.o swell-gdi.o swell.o swell-misc.o swell-dlg.o swell-menu.o swell-kb.o
|
||||
LFLAGS += -lobjc -framework Cocoa -framework Carbon
|
||||
else
|
||||
|
||||
CFLAGS += -DSWELL_LICE_GDI -DSWELL_EXTRA_MINIMAL
|
||||
ifdef GDK2
|
||||
CFLAGS += -DSWELL_TARGET_GDK=2 $(shell pkg-config --cflags gdk-2.0)
|
||||
LFLAGS += $(shell pkg-config --libs gdk-2.0) -lX11 -lXi
|
||||
else
|
||||
CFLAGS += -DSWELL_TARGET_GDK=3 $(shell pkg-config --cflags gdk-3.0)
|
||||
LFLAGS += $(shell pkg-config --libs gdk-3.0) -lX11 -lXi
|
||||
endif
|
||||
ifndef NOFREETYPE
|
||||
CFLAGS += -DSWELL_FREETYPE $(shell pkg-config --cflags freetype2)
|
||||
LFLAGS += $(shell pkg-config --libs freetype2)
|
||||
endif
|
||||
|
||||
SWELL_OBJS += swell-wnd-generic.o swell-gdi-lice.o swell.o swell-misc-generic.o \
|
||||
swell-dlg-generic.o swell-menu-generic.o swell-kb-generic.o \
|
||||
swell-gdi-generic.o swell-ini.o swell-generic-gdk.o
|
||||
|
||||
LFLAGS += -ldl -lGL
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
ifdef PORTABLE
|
||||
CFLAGS += -DEEL_TARGET_PORTABLE
|
||||
else
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ASM_FMT = macho64
|
||||
NASM_OPTS = --prefix _
|
||||
OBJS2 += asm-nseel-x64-sse.o
|
||||
endif
|
||||
endif
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(ARCH),x86_64)
|
||||
ASM_FMT = elf64
|
||||
NASM_OPTS =
|
||||
OBJS2 += asm-nseel-x64-sse.o
|
||||
endif
|
||||
endif
|
||||
|
||||
asm-nseel-x64-sse.o: asm-nseel-x64-sse.asm
|
||||
nasm -D AMD64ABI -f $(ASM_FMT) $(NASM_OPTS) asm-nseel-x64-sse.asm
|
||||
|
||||
endif
|
||||
CXXFLAGS=$(CFLAGS)
|
||||
|
||||
ifeq ($(CXX),g++)
|
||||
GCC_VER := $(shell $(CXX) --version|head -n 1| sed 's/.* \([0-9][0-9]*\)[.][0-9.]*/\1/' )
|
||||
GCC_GT_10 := $(shell [ "$(GCC_VER)" -gt 10 ] && echo true )
|
||||
GCC_GT_11 := $(shell [ "$(GCC_VER)" -gt 11 ] && echo true )
|
||||
ifeq ($(GCC_GT_10),true)
|
||||
CXXFLAGS += -std=c++03
|
||||
endif
|
||||
ifeq ($(GCC_GT_11),true)
|
||||
# false positive in gcc 12/13
|
||||
CFLAGS += -Wno-dangling-pointer
|
||||
endif
|
||||
endif
|
||||
|
||||
gen-yacc:
|
||||
yacc -v -d eel2.y
|
||||
|
||||
gen-lex: # the output of this, lex.nseel.c, is unused because we have a handwritten parser instead
|
||||
flex eel2.l
|
||||
|
||||
%.o : %.mm
|
||||
$(CXX) $(CXXFLAGS) -c -o $@ $^
|
||||
|
||||
loose_eel: loose_eel.o $(OBJS) $(OBJS2) $(SWELL_OBJS) $(LICE_OBJS)
|
||||
g++ -o $@ $^ $(CXXFLAGS) $(LFLAGS)
|
||||
|
||||
eel_pp: eel_pp.o $(OBJS) $(OBJS2)
|
||||
g++ -o $@ $^ $(CXXFLAGS) $(LFLAGS)
|
||||
|
||||
clean:
|
||||
-rm -f -- loose_eel loose_eel.o eel_pp.o eel_pp $(OBJS) $(SWELL_OBJS) $(LICE_OBJS)
|
||||
|
||||
.PHONY: clean gen-lex gen-yacc
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
|
||||
|
||||
|
||||
function process_file($infn, $outfn)
|
||||
{
|
||||
|
||||
$in = fopen($infn,"r");
|
||||
if (!$in) die("error opening input $infn\n");
|
||||
$out = fopen($outfn,"w");
|
||||
if (!$out) die("error opening output $outfn\n");
|
||||
|
||||
fputs($out,"// THIS FILE AUTOGENERATED FROM $infn by a2i.php\n\n");
|
||||
|
||||
$inblock=0;
|
||||
$labelcnt=0;
|
||||
|
||||
while (($line = fgets($in)))
|
||||
{
|
||||
$line = rtrim($line);
|
||||
if (trim($line) == "FUNCTION_MARKER")
|
||||
{
|
||||
fputs($out,"_emit 0x89;\n");
|
||||
for ($tmp=0;$tmp<11;$tmp++) fputs($out,"_emit 0x90;\n");
|
||||
continue;
|
||||
}
|
||||
$nowrite=0;
|
||||
|
||||
{
|
||||
if (!$inblock)
|
||||
{
|
||||
if (strstr($line,"__asm__("))
|
||||
{
|
||||
$line=str_replace("__asm__(", "__asm {", $line);
|
||||
$inblock=1;
|
||||
if (isset($bthist)) unset($bthist);
|
||||
if (isset($btfut)) unset($btfut);
|
||||
$bthist = array();
|
||||
$btfut = array();
|
||||
}
|
||||
}
|
||||
|
||||
if ($inblock)
|
||||
{
|
||||
if (substr(trim($line),-2) == ");")
|
||||
{
|
||||
$line = str_replace(");","}",$line);
|
||||
$inblock=0;
|
||||
}
|
||||
|
||||
$sline = strstr($line, "\"");
|
||||
$lastchunk = strrchr($line,"\"");
|
||||
if ($sline && $lastchunk && strlen($sline) != strlen($lastchunk))
|
||||
{
|
||||
$beg_restore = substr($line,0,-strlen($sline));
|
||||
|
||||
if (strlen($lastchunk)>1)
|
||||
$end_restore = substr($line,1-strlen($lastchunk));
|
||||
else $end_restore="";
|
||||
|
||||
$sline = substr($sline,1,strlen($sline)-1-strlen($lastchunk));
|
||||
|
||||
// get rid of chars we can ignore
|
||||
$sline=preg_replace("/%\d+/","__TEMP_REPLACE__", $sline);
|
||||
|
||||
$sline=str_replace("\\n","", $sline);
|
||||
$sline=str_replace("\"","", $sline);
|
||||
$sline=str_replace("$","", $sline);
|
||||
$sline=str_replace("%","", $sline);
|
||||
|
||||
|
||||
// get rid of excess whitespace, especially around commas
|
||||
$sline=str_replace(" "," ", $sline);
|
||||
$sline=str_replace(" "," ", $sline);
|
||||
$sline=str_replace(" "," ", $sline);
|
||||
$sline=str_replace(", ",",", $sline);
|
||||
$sline=str_replace(" ,",",", $sline);
|
||||
|
||||
$sline=preg_replace("/st\\(([0-9]+)\\)/","FPREG_$1",$sline);
|
||||
|
||||
|
||||
if (preg_match("/^([0-9]+):/",trim($sline)))
|
||||
{
|
||||
$d = (int) $sline;
|
||||
$a = strstr($sline,":");
|
||||
if ($a) $sline = substr($a,1);
|
||||
|
||||
if (isset($btfut[$d]) && $btfut[$d] != "") $thislbl = $btfut[$d];
|
||||
else $thislbl = "label_" . $labelcnt++;
|
||||
|
||||
$btfut[$d]="";
|
||||
$bthist[$d] = $thislbl;
|
||||
|
||||
fputs($out,$thislbl . ":\n");
|
||||
}
|
||||
|
||||
$sploded = explode(" ",trim($sline));
|
||||
if ($sline != "" && count($sploded)>0)
|
||||
{
|
||||
$inst = trim($sploded[0]);
|
||||
$suffix = "";
|
||||
|
||||
$instline = strstr($sline,$inst);
|
||||
$beg_restore .= substr($sline,0,-strlen($instline));
|
||||
|
||||
$parms = trim(substr($instline,strlen($inst)));
|
||||
|
||||
if ($inst=="j") $inst="jmp";
|
||||
|
||||
//if ($inst == "fdiv" && $parms == "") $inst="fdivr";
|
||||
|
||||
if ($inst != "call" && substr($inst,-2) == "ll") $suffix = "ll";
|
||||
else if ($inst != "call" && $inst != "fmul" && substr($inst,-1) == "l") $suffix = "l";
|
||||
else if (substr($inst,0,1)=="f" && $inst != "fcos" && $inst != "fsincos" && $inst != "fabs" && $inst != "fchs" && substr($inst,-1) == "s") $suffix = "s";
|
||||
|
||||
|
||||
if ($suffix != "" && $inst != "jl") $inst = substr($inst,0,-strlen($suffix));
|
||||
|
||||
$parms = preg_replace("/\\((.{2,3}),(.{2,3})\\)/","($1+$2)",$parms);
|
||||
|
||||
$parms=preg_replace("/EEL_F_SUFFIX (-?[0-9]+)\\((.*)\\)/","qword ptr [$2+$1]",$parms);
|
||||
$parms=preg_replace("/EEL_F_SUFFIX \\((.*)\\)/","qword ptr [$1]",$parms);
|
||||
|
||||
if ($inst == "sh" && $suffix == "ll") { $suffix="l"; $inst="shl"; }
|
||||
|
||||
if ($suffix == "ll" || ($suffix == "l" && substr($inst,0,1) == "f" && substr($inst,0,2) != "fi")) $suffixstr = "qword ptr ";
|
||||
else if ($suffix == "l") $suffixstr = "dword ptr ";
|
||||
else if ($suffix == "s") $suffixstr = "dword ptr ";
|
||||
else $suffixstr = "";
|
||||
$parms=preg_replace("/(-?[0-9]+)\\((.*)\\)/",$suffixstr . "[$2+$1]",$parms);
|
||||
$parms=preg_replace("/\\((.*)\\)/",$suffixstr . "[$1]",$parms);
|
||||
|
||||
|
||||
$parms=str_replace("EEL_F_SUFFIX","qword ptr", $parms);
|
||||
|
||||
$plist = explode(",",$parms);
|
||||
if (count($plist) > 2) echo "Warning: too many parameters $parms!\n";
|
||||
else if (count($plist)==2)
|
||||
{
|
||||
$parms = trim($plist[1]) . ", " . trim($plist[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
|
||||
if ($inst=="fsts") $inst="fstsw";
|
||||
if ($inst=="call" && substr($parms,0,1) == "*") $parms=substr($parms,1);
|
||||
if (substr($inst,0,1) == "j")
|
||||
{
|
||||
if (substr($parms,-1) == "f")
|
||||
{
|
||||
$d = (int) substr($parms,0,-1);
|
||||
if (isset($btfut[$d]) && $btfut[$d] != "") $thislbl = $btfut[$d];
|
||||
else $btfut[$d] = $thislbl = "label_" . $labelcnt++;
|
||||
$parms = $thislbl;
|
||||
}
|
||||
else if (substr($parms,-1) == "b")
|
||||
{
|
||||
$d = (int) substr($parms,0,-1);
|
||||
if ($bthist[$d]=="") echo "Error resolving label $parms\n";
|
||||
$parms = $bthist[$d];
|
||||
}
|
||||
}
|
||||
if (stristr($parms,"[0xfefefefe]"))
|
||||
{
|
||||
if ($inst == "fmul" || $inst=="fadd" || $inst == "fcomp")
|
||||
{
|
||||
if ($inst=="fmul") $hdr="0x0D";
|
||||
if ($inst=="fadd") $hdr="0x05";
|
||||
if ($inst=="fcomp") $hdr="0x1D";
|
||||
|
||||
fputs($out,"_emit 0xDC; // $inst qword ptr [0xfefefefe]\n");
|
||||
fputs($out,"_emit $hdr;\n");
|
||||
fputs($out,"_emit 0xFE;\n");
|
||||
fputs($out,"_emit 0xFE;\n");
|
||||
fputs($out,"_emit 0xFE;\n");
|
||||
fputs($out,"_emit 0xFE;\n");
|
||||
$nowrite=1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$sline = $inst;
|
||||
if ($parms !="") $sline .= " " . $parms;
|
||||
$sline .= ";";
|
||||
|
||||
}
|
||||
|
||||
$sline=preg_replace("/FPREG_([0-9]+)/","st($1)",$sline);
|
||||
$line = $beg_restore . $sline . $end_restore;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (!$nowrite)
|
||||
{
|
||||
if (strstr($line,"__TEMP_REPLACE__"))
|
||||
{
|
||||
$a = strstr($line,"//REPLACE=");
|
||||
if ($a === false) die ("__TEMP_REPLACE__ found, no REPLACE=\n");
|
||||
$line=str_replace("__TEMP_REPLACE__",substr($a,10),$line);
|
||||
}
|
||||
fputs($out,$line . "\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ($inblock) echo "Error (ended in __asm__ block???)\n";
|
||||
|
||||
|
||||
fclose($in);
|
||||
fclose($out);
|
||||
|
||||
};
|
||||
|
||||
process_file("asm-nseel-x86-gcc.c" , "asm-nseel-x86-msvc.c");
|
||||
// process_file("asm-miscfunc-x86-gcc.c" , "asm-miscfunc-x86-msvc.c");
|
||||
//process_file("asm-megabuf-x86-gcc.c" , "asm-megabuf-x86-msvc.c");
|
||||
|
||||
?>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+113
@@ -0,0 +1,113 @@
|
||||
%option reentrant
|
||||
%option prefix="nseel"
|
||||
%option bison-bridge
|
||||
%option bison-locations
|
||||
%option noyywrap
|
||||
%option never-interactive
|
||||
%option batch
|
||||
%option nounput
|
||||
|
||||
%{
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define YY_USER_ACTION yylloc->first_line = yylineno;
|
||||
|
||||
#define YY_FATAL_ERROR(msg) { ((struct yyguts_t*)yyscanner)->yyextra_r->errVar=1; }
|
||||
#define YY_INPUT(buf,result,max_size) { (result) = nseel_gets(yyextra,(buf),max_size); }
|
||||
|
||||
#define YY_EXTRA_TYPE compileContext *
|
||||
|
||||
#undef YY_BUF_SIZE
|
||||
#define YY_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN*2)
|
||||
|
||||
#undef YY_READ_BUF_SIZE
|
||||
#define YY_READ_BUF_SIZE (NSEEL_MAX_VARIABLE_NAMELEN)
|
||||
|
||||
#include "y.tab.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define YY_NO_UNISTD_H
|
||||
#endif
|
||||
|
||||
#include "ns-eel-int.h"
|
||||
|
||||
int nseel_gets(compileContext *ctx, char *buf, size_t sz);
|
||||
|
||||
#define PARSENUM *yylval = nseel_translate(yyextra,yytext, 0); return VALUE;
|
||||
#define EEL_ACTION(x) return x;
|
||||
|
||||
#ifdef stdin
|
||||
#undef stdin
|
||||
#endif
|
||||
#define stdin (0)
|
||||
|
||||
#ifdef stdout
|
||||
#undef stdout
|
||||
#endif
|
||||
#define stdout (0)
|
||||
|
||||
static int g_fake_errno;
|
||||
#ifdef errno
|
||||
#undef errno
|
||||
#endif
|
||||
|
||||
#define errno g_fake_errno
|
||||
|
||||
static void comment(yyscan_t yyscanner);
|
||||
|
||||
%}
|
||||
|
||||
%%
|
||||
|
||||
[0-9]+\.?[0-9]* PARSENUM;
|
||||
\.[0-9]+ PARSENUM;
|
||||
0[xX][0-9a-fA-F]* PARSENUM;
|
||||
\$[xX][0-9a-fA-F]* PARSENUM;
|
||||
\$\~[0-9]* PARSENUM;
|
||||
\$[Ee] PARSENUM;
|
||||
\$[Pp][Ii] PARSENUM;
|
||||
\$[Pp][Hh][Ii] PARSENUM;
|
||||
\$\'.\' PARSENUM;
|
||||
\#[a-zA-Z0-9\._]* *yylval = nseel_translate(yyextra,yytext, 0); return STRING_IDENTIFIER;
|
||||
\<\< return TOKEN_SHL;
|
||||
\>\> return TOKEN_SHR;
|
||||
\<= return TOKEN_LTE;
|
||||
\>= return TOKEN_GTE;
|
||||
== return TOKEN_EQ;
|
||||
=== return TOKEN_EQ_EXACT;
|
||||
\!= return TOKEN_NE;
|
||||
\!== return TOKEN_NE_EXACT;
|
||||
\&\& return TOKEN_LOGICAL_AND;
|
||||
\|\| return TOKEN_LOGICAL_OR;
|
||||
\+= return TOKEN_ADD_OP;
|
||||
-= return TOKEN_SUB_OP;
|
||||
%= return TOKEN_MOD_OP;
|
||||
\|= return TOKEN_OR_OP;
|
||||
\&= return TOKEN_AND_OP;
|
||||
\~= return TOKEN_XOR_OP;
|
||||
\/= return TOKEN_DIV_OP;
|
||||
\*= return TOKEN_MUL_OP;
|
||||
\^= return TOKEN_POW_OP;
|
||||
|
||||
[a-zA-Z_][a-zA-Z0-9\._]* &yylval = nseel_createCompiledValuePtr((compileContext *)yyextra, NULL, yytext); return IDENTIFIER;
|
||||
|
||||
[ \t\r\n]+ /* whitespace */
|
||||
\/\/.*$ /* comment */
|
||||
"/*" { comment(yyscanner); }
|
||||
|
||||
. return (int)yytext[0];
|
||||
|
||||
%%
|
||||
|
||||
static void comment(yyscan_t yyscanner)
|
||||
{
|
||||
int c,lc=0;
|
||||
|
||||
while (0 != (c = input(yyscanner)))
|
||||
{
|
||||
if (c == '/' && lc == '*') return;
|
||||
lc = c;
|
||||
}
|
||||
// end of file, ignore for now
|
||||
}
|
||||
@@ -0,0 +1,370 @@
|
||||
" Vim syntax file
|
||||
" This needs a lot of C-specific stuff removed, please help if you care =)
|
||||
" Language: EEL2, based on:
|
||||
"
|
||||
"
|
||||
" Language: C - Maintainer: Bram Moolenaar <Bram@vim.org> - Last Change: 2009 Nov 17
|
||||
|
||||
" Quit when a (custom) syntax file was already loaded
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
" A bunch of useful C keywords
|
||||
syn keyword cStatement function globals global local instance
|
||||
syn keyword cRepeat while loop
|
||||
syn keyword cRepeat sin cos tan sqrt log log10 asin acos atan atan2 exp abs sqr min max sign rand floor ceil invsqrt freembuf memcpy memset stack_psuh stack_pop stack_peek stack_exch
|
||||
syn keyword cRepeat atomic_setifequal atomic_exch atomic_add atomic_set atomic_get convolve_c fft ifft fft_permute fft_ipermute fopen fread fgets fgetc fwrite fprintf fseek ftell feof fflush fclose
|
||||
syn keyword cRepeat gfx_lineto gfx_lineto gfx_rectto gfx_rect gfx_line gfx_gradrect gfx_muladdrect gfx_deltablit gfx_transformblit gfx_blurto gfx_drawnumber gfx_drawchar gfx_drawstr gfx_measurestr gfx_printf gfx_setpixel gfx_getpixel gfx_getimgdim gfx_setimgdim gfx_loadimg gfx_blit gfx_blitext gfx_blit gfx_setfont gfx_getfont gfx_init gfx_quit gfx_getchar
|
||||
syn keyword cRepeat mdct imdct sleep time time_precise tcp_listen tcp_listen_end tcp_connect tcp_send tcp_recv tcp_set_block tcp_close strlen
|
||||
syn keyword cRepeat strcat strcpy strcmp stricmp strncat strncpy strncmp strnicmp str_setlen strcpy_from strcpy_substr strcpy_substr str_getchar str_setchar str_getchar str_setchar str_insert str_delsub sprintf printf match matchi
|
||||
|
||||
syn keyword cTodo contained TODO FIXME XXX
|
||||
|
||||
" It's easy to accidentally add a space after a backslash that was intended
|
||||
" for line continuation. Some compilers allow it, which makes it
|
||||
" unpredicatable and should be avoided.
|
||||
syn match cBadContinuation contained "\\\s\+$"
|
||||
|
||||
" cCommentGroup allows adding matches for special things in comments
|
||||
syn cluster cCommentGroup contains=cTodo,cBadContinuation
|
||||
|
||||
" String and Character constants
|
||||
" Highlight special characters (those which have a backslash) differently
|
||||
syn match cSpecial display contained "\\\(x\x\+\|\o\{1,3}\|.\|$\)"
|
||||
if !exists("c_no_utf")
|
||||
syn match cSpecial display contained "\\\(u\x\{4}\|U\x\{8}\)"
|
||||
endif
|
||||
if exists("c_no_cformat")
|
||||
syn region cString start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,@Spell
|
||||
" cCppString: same as cString, but ends at end of line
|
||||
syn region cCppString start=+L\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,@Spell
|
||||
else
|
||||
if !exists("c_no_c99") " ISO C99
|
||||
syn match cFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlLjzt]\|ll\|hh\)\=\([aAbdiuoxXDOUfFeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
|
||||
else
|
||||
syn match cFormat display "%\(\d\+\$\)\=[-+' #0*]*\(\d*\|\*\|\*\d\+\$\)\(\.\(\d*\|\*\|\*\d\+\$\)\)\=\([hlL]\|ll\)\=\([bdiuoxXDOUfeEgGcCsSpn]\|\[\^\=.[^]]*\]\)" contained
|
||||
endif
|
||||
syn match cFormat display "%%" contained
|
||||
syn region cString start=+L\="+ skip=+\\\\\|\\"+ end=+"+ contains=cSpecial,cFormat,@Spell
|
||||
" cCppString: same as cString, but ends at end of line
|
||||
syn region cCppString start=+L\="+ skip=+\\\\\|\\"\|\\$+ excludenl end=+"+ end='$' contains=cSpecial,cFormat,@Spell
|
||||
endif
|
||||
|
||||
syn match cCharacter "L\='[^\\]'"
|
||||
syn match cCharacter "L'[^']*'" contains=cSpecial
|
||||
syn match cCharacter "'[^']*'" contains=cSpecial
|
||||
if exists("c_gnu")
|
||||
syn match cSpecialError "L\='\\[^'\"?\\abefnrtv]'"
|
||||
syn match cSpecialCharacter "L\='\\['\"?\\abefnrtv]'"
|
||||
else
|
||||
syn match cSpecialError "L\='\\[^'\"?\\abfnrtv]'"
|
||||
syn match cSpecialCharacter "L\='\\['\"?\\abfnrtv]'"
|
||||
endif
|
||||
syn match cSpecialCharacter display "L\='\\\o\{1,3}'"
|
||||
syn match cSpecialCharacter display "'\\x\x\{1,2}'"
|
||||
syn match cSpecialCharacter display "L'\\x\x\+'"
|
||||
|
||||
"when wanted, highlight trailing white space
|
||||
if exists("c_space_errors")
|
||||
if !exists("c_no_trail_space_error")
|
||||
syn match cSpaceError display excludenl "\s\+$"
|
||||
endif
|
||||
if !exists("c_no_tab_space_error")
|
||||
syn match cSpaceError display " \+\t"me=e-1
|
||||
endif
|
||||
endif
|
||||
|
||||
" This should be before cErrInParen to avoid problems with #define ({ xxx })
|
||||
if exists("c_curly_error")
|
||||
syntax match cCurlyError "}"
|
||||
syntax region cBlock start="{" end="}" contains=ALLBUT,cCurlyError,@cParenGroup,cErrInParen,cCppParen,cErrInBracket,cCppBracket,cCppString,@Spell fold
|
||||
else
|
||||
syntax region cBlock start="{" end="}" transparent fold
|
||||
endif
|
||||
|
||||
"catch errors caused by wrong parenthesis and brackets
|
||||
" also accept <% for {, %> for }, <: for [ and :> for ] (C99)
|
||||
" But avoid matching <::.
|
||||
syn cluster cParenGroup contains=cParenError,cIncluded,cSpecial,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cUserCont,cUserLabel,cOctalZero,cCppOut,cCppOut2,cCppSkip,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom
|
||||
if exists("c_no_curly_error")
|
||||
syn region cParen transparent start='(' end=')' contains=ALLBUT,@cParenGroup,cCppParen,cCppString,@Spell
|
||||
" cCppParen: same as cParen but ends at end-of-line; used in cDefine
|
||||
syn region cCppParen transparent start='(' skip='\\$' excludenl end=')' end='$' contained contains=ALLBUT,@cParenGroup,cParen,cString,@Spell
|
||||
syn match cParenError display ")"
|
||||
syn match cErrInParen display contained "^[{}]\|^<%\|^%>"
|
||||
elseif exists("c_no_bracket_error")
|
||||
syn region cParen transparent start='(' end=')' contains=ALLBUT,@cParenGroup,cCppParen,cCppString,@Spell
|
||||
" cCppParen: same as cParen but ends at end-of-line; used in cDefine
|
||||
syn region cCppParen transparent start='(' skip='\\$' excludenl end=')' end='$' contained contains=ALLBUT,@cParenGroup,cParen,cString,@Spell
|
||||
syn match cParenError display ")"
|
||||
syn match cErrInParen display contained "[{}]\|<%\|%>"
|
||||
else
|
||||
syn region cParen transparent start='(' end=')' contains=ALLBUT,@cParenGroup,cCppParen,cErrInBracket,cCppBracket,cCppString,@Spell
|
||||
" cCppParen: same as cParen but ends at end-of-line; used in cDefine
|
||||
syn region cCppParen transparent start='(' skip='\\$' excludenl end=')' end='$' contained contains=ALLBUT,@cParenGroup,cErrInBracket,cParen,cBracket,cString,@Spell
|
||||
syn match cParenError display "[\])]"
|
||||
syn match cErrInParen display contained "[\]{}]\|<%\|%>"
|
||||
syn region cBracket transparent start='\[\|<::\@!' end=']\|:>' contains=ALLBUT,@cParenGroup,cErrInParen,cCppParen,cCppBracket,cCppString,@Spell
|
||||
" cCppBracket: same as cParen but ends at end-of-line; used in cDefine
|
||||
syn region cCppBracket transparent start='\[\|<::\@!' skip='\\$' excludenl end=']\|:>' end='$' contained contains=ALLBUT,@cParenGroup,cErrInParen,cParen,cBracket,cString,@Spell
|
||||
syn match cErrInBracket display contained "[){}]\|<%\|%>"
|
||||
endif
|
||||
|
||||
"integer number, or floating point number without a dot and with "f".
|
||||
syn case ignore
|
||||
syn match cNumbers display transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctalError,cOctal
|
||||
" Same, but without octal error (for comments)
|
||||
syn match cNumbersCom display contained transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctal
|
||||
syn match cNumber display contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>"
|
||||
"hex number
|
||||
syn match cNumber display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>"
|
||||
" Flag the first zero of an octal number as something special
|
||||
syn match cOctal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
|
||||
syn match cOctalZero display contained "\<0"
|
||||
syn match cFloat display contained "\d\+f"
|
||||
"floating point number, with dot, optional exponent
|
||||
syn match cFloat display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\="
|
||||
"floating point number, starting with a dot, optional exponent
|
||||
syn match cFloat display contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>"
|
||||
"floating point number, without dot, with exponent
|
||||
syn match cFloat display contained "\d\+e[-+]\=\d\+[fl]\=\>"
|
||||
if !exists("c_no_c99")
|
||||
"hexadecimal floating point number, optional leading digits, with dot, with exponent
|
||||
syn match cFloat display contained "0x\x*\.\x\+p[-+]\=\d\+[fl]\=\>"
|
||||
"hexadecimal floating point number, with leading digits, optional dot, with exponent
|
||||
syn match cFloat display contained "0x\x\+\.\=p[-+]\=\d\+[fl]\=\>"
|
||||
endif
|
||||
|
||||
" flag an octal number with wrong digits
|
||||
syn match cOctalError display contained "0\o*[89]\d*"
|
||||
syn case match
|
||||
|
||||
if exists("c_comment_strings")
|
||||
" A comment can contain cString, cCharacter and cNumber.
|
||||
" But a "*/" inside a cString in a cComment DOES end the comment! So we
|
||||
" need to use a special type of cString: cCommentString, which also ends on
|
||||
" "*/", and sees a "*" at the start of the line as comment again.
|
||||
" Unfortunately this doesn't very well work for // type of comments :-(
|
||||
syntax match cCommentSkip contained "^\s*\*\($\|\s\+\)"
|
||||
syntax region cCommentString contained start=+L\=\\\@<!"+ skip=+\\\\\|\\"+ end=+"+ end=+\*/+me=s-1 contains=cSpecial,cCommentSkip
|
||||
syntax region cComment2String contained start=+L\=\\\@<!"+ skip=+\\\\\|\\"+ end=+"+ end="$" contains=cSpecial
|
||||
syntax region cCommentL start="//" skip="\\$" end="$" keepend contains=@cCommentGroup,cComment2String,cCharacter,cNumbersCom,cSpaceError,@Spell
|
||||
if exists("c_no_comment_fold")
|
||||
" Use "extend" here to have preprocessor lines not terminate halfway a
|
||||
" comment.
|
||||
syntax region cComment matchgroup=cCommentStart start="/\*" end="\*/" contains=@cCommentGroup,cCommentStartError,cCommentString,cCharacter,cNumbersCom,cSpaceError,@Spell extend
|
||||
else
|
||||
syntax region cComment matchgroup=cCommentStart start="/\*" end="\*/" contains=@cCommentGroup,cCommentStartError,cCommentString,cCharacter,cNumbersCom,cSpaceError,@Spell fold extend
|
||||
endif
|
||||
else
|
||||
syn region cCommentL start="//" skip="\\$" end="$" keepend contains=@cCommentGroup,cSpaceError,@Spell
|
||||
if exists("c_no_comment_fold")
|
||||
syn region cComment matchgroup=cCommentStart start="/\*" end="\*/" contains=@cCommentGroup,cCommentStartError,cSpaceError,@Spell extend
|
||||
else
|
||||
syn region cComment matchgroup=cCommentStart start="/\*" end="\*/" contains=@cCommentGroup,cCommentStartError,cSpaceError,@Spell fold extend
|
||||
endif
|
||||
endif
|
||||
" keep a // comment separately, it terminates a preproc. conditional
|
||||
syntax match cCommentError display "\*/"
|
||||
syntax match cCommentStartError display "/\*"me=e-1 contained
|
||||
|
||||
syn keyword cOperator sizeof
|
||||
if exists("c_gnu")
|
||||
syn keyword cStatement __asm__
|
||||
syn keyword cOperator typeof __real__ __imag__
|
||||
endif
|
||||
syn keyword cType int long short char void
|
||||
syn keyword cType signed unsigned float double
|
||||
if !exists("c_no_ansi") || exists("c_ansi_typedefs")
|
||||
syn keyword cType size_t ssize_t off_t wchar_t ptrdiff_t sig_atomic_t fpos_t
|
||||
syn keyword cType clock_t time_t va_list jmp_buf FILE DIR div_t ldiv_t
|
||||
syn keyword cType mbstate_t wctrans_t wint_t wctype_t
|
||||
endif
|
||||
if !exists("c_no_c99") " ISO C99
|
||||
syn keyword cType bool complex
|
||||
syn keyword cType int8_t int16_t int32_t int64_t
|
||||
syn keyword cType uint8_t uint16_t uint32_t uint64_t
|
||||
syn keyword cType int_least8_t int_least16_t int_least32_t int_least64_t
|
||||
syn keyword cType uint_least8_t uint_least16_t uint_least32_t uint_least64_t
|
||||
syn keyword cType int_fast8_t int_fast16_t int_fast32_t int_fast64_t
|
||||
syn keyword cType uint_fast8_t uint_fast16_t uint_fast32_t uint_fast64_t
|
||||
syn keyword cType intptr_t uintptr_t
|
||||
syn keyword cType intmax_t uintmax_t
|
||||
endif
|
||||
if exists("c_gnu")
|
||||
syn keyword cType __label__ __complex__ __volatile__
|
||||
endif
|
||||
|
||||
syn keyword cStructure struct union enum typedef
|
||||
syn keyword cStorageClass static register auto volatile extern const
|
||||
if exists("c_gnu")
|
||||
syn keyword cStorageClass inline __attribute__
|
||||
endif
|
||||
if !exists("c_no_c99")
|
||||
syn keyword cStorageClass inline restrict
|
||||
endif
|
||||
|
||||
if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu")
|
||||
if exists("c_gnu")
|
||||
syn keyword cConstant __GNUC__ __FUNCTION__ __PRETTY_FUNCTION__ __func__
|
||||
endif
|
||||
syn keyword cConstant __LINE__ __FILE__ __DATE__ __TIME__ __STDC__
|
||||
syn keyword cConstant __STDC_VERSION__
|
||||
syn keyword cConstant CHAR_BIT MB_LEN_MAX MB_CUR_MAX
|
||||
syn keyword cConstant UCHAR_MAX UINT_MAX ULONG_MAX USHRT_MAX
|
||||
syn keyword cConstant CHAR_MIN INT_MIN LONG_MIN SHRT_MIN
|
||||
syn keyword cConstant CHAR_MAX INT_MAX LONG_MAX SHRT_MAX
|
||||
syn keyword cConstant SCHAR_MIN SINT_MIN SLONG_MIN SSHRT_MIN
|
||||
syn keyword cConstant SCHAR_MAX SINT_MAX SLONG_MAX SSHRT_MAX
|
||||
if !exists("c_no_c99")
|
||||
syn keyword cConstant __func__
|
||||
syn keyword cConstant LLONG_MIN LLONG_MAX ULLONG_MAX
|
||||
syn keyword cConstant INT8_MIN INT16_MIN INT32_MIN INT64_MIN
|
||||
syn keyword cConstant INT8_MAX INT16_MAX INT32_MAX INT64_MAX
|
||||
syn keyword cConstant UINT8_MAX UINT16_MAX UINT32_MAX UINT64_MAX
|
||||
syn keyword cConstant INT_LEAST8_MIN INT_LEAST16_MIN INT_LEAST32_MIN INT_LEAST64_MIN
|
||||
syn keyword cConstant INT_LEAST8_MAX INT_LEAST16_MAX INT_LEAST32_MAX INT_LEAST64_MAX
|
||||
syn keyword cConstant UINT_LEAST8_MAX UINT_LEAST16_MAX UINT_LEAST32_MAX UINT_LEAST64_MAX
|
||||
syn keyword cConstant INT_FAST8_MIN INT_FAST16_MIN INT_FAST32_MIN INT_FAST64_MIN
|
||||
syn keyword cConstant INT_FAST8_MAX INT_FAST16_MAX INT_FAST32_MAX INT_FAST64_MAX
|
||||
syn keyword cConstant UINT_FAST8_MAX UINT_FAST16_MAX UINT_FAST32_MAX UINT_FAST64_MAX
|
||||
syn keyword cConstant INTPTR_MIN INTPTR_MAX UINTPTR_MAX
|
||||
syn keyword cConstant INTMAX_MIN INTMAX_MAX UINTMAX_MAX
|
||||
syn keyword cConstant PTRDIFF_MIN PTRDIFF_MAX SIG_ATOMIC_MIN SIG_ATOMIC_MAX
|
||||
syn keyword cConstant SIZE_MAX WCHAR_MIN WCHAR_MAX WINT_MIN WINT_MAX
|
||||
endif
|
||||
syn keyword cConstant FLT_RADIX FLT_ROUNDS
|
||||
syn keyword cConstant FLT_DIG FLT_MANT_DIG FLT_EPSILON
|
||||
syn keyword cConstant DBL_DIG DBL_MANT_DIG DBL_EPSILON
|
||||
syn keyword cConstant LDBL_DIG LDBL_MANT_DIG LDBL_EPSILON
|
||||
syn keyword cConstant FLT_MIN FLT_MAX FLT_MIN_EXP FLT_MAX_EXP
|
||||
syn keyword cConstant FLT_MIN_10_EXP FLT_MAX_10_EXP
|
||||
syn keyword cConstant DBL_MIN DBL_MAX DBL_MIN_EXP DBL_MAX_EXP
|
||||
syn keyword cConstant DBL_MIN_10_EXP DBL_MAX_10_EXP
|
||||
syn keyword cConstant LDBL_MIN LDBL_MAX LDBL_MIN_EXP LDBL_MAX_EXP
|
||||
syn keyword cConstant LDBL_MIN_10_EXP LDBL_MAX_10_EXP
|
||||
syn keyword cConstant HUGE_VAL CLOCKS_PER_SEC NULL
|
||||
syn keyword cConstant LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY
|
||||
syn keyword cConstant LC_NUMERIC LC_TIME
|
||||
syn keyword cConstant SIG_DFL SIG_ERR SIG_IGN
|
||||
syn keyword cConstant SIGABRT SIGFPE SIGILL SIGHUP SIGINT SIGSEGV SIGTERM
|
||||
" Add POSIX signals as well...
|
||||
syn keyword cConstant SIGABRT SIGALRM SIGCHLD SIGCONT SIGFPE SIGHUP
|
||||
syn keyword cConstant SIGILL SIGINT SIGKILL SIGPIPE SIGQUIT SIGSEGV
|
||||
syn keyword cConstant SIGSTOP SIGTERM SIGTRAP SIGTSTP SIGTTIN SIGTTOU
|
||||
syn keyword cConstant SIGUSR1 SIGUSR2
|
||||
syn keyword cConstant _IOFBF _IOLBF _IONBF BUFSIZ EOF WEOF
|
||||
syn keyword cConstant FOPEN_MAX FILENAME_MAX L_tmpnam
|
||||
syn keyword cConstant SEEK_CUR SEEK_END SEEK_SET
|
||||
syn keyword cConstant TMP_MAX stderr stdin stdout
|
||||
syn keyword cConstant EXIT_FAILURE EXIT_SUCCESS RAND_MAX
|
||||
" Add POSIX errors as well
|
||||
syn keyword cConstant E2BIG EACCES EAGAIN EBADF EBADMSG EBUSY
|
||||
syn keyword cConstant ECANCELED ECHILD EDEADLK EDOM EEXIST EFAULT
|
||||
syn keyword cConstant EFBIG EILSEQ EINPROGRESS EINTR EINVAL EIO EISDIR
|
||||
syn keyword cConstant EMFILE EMLINK EMSGSIZE ENAMETOOLONG ENFILE ENODEV
|
||||
syn keyword cConstant ENOENT ENOEXEC ENOLCK ENOMEM ENOSPC ENOSYS
|
||||
syn keyword cConstant ENOTDIR ENOTEMPTY ENOTSUP ENOTTY ENXIO EPERM
|
||||
syn keyword cConstant EPIPE ERANGE EROFS ESPIPE ESRCH ETIMEDOUT EXDEV
|
||||
" math.h
|
||||
syn keyword cConstant M_E M_LOG2E M_LOG10E M_LN2 M_LN10 M_PI M_PI_2 M_PI_4
|
||||
syn keyword cConstant M_1_PI M_2_PI M_2_SQRTPI M_SQRT2 M_SQRT1_2
|
||||
endif
|
||||
if !exists("c_no_c99") " ISO C99
|
||||
syn keyword cConstant true false
|
||||
endif
|
||||
|
||||
" Accept %: for # (C99)
|
||||
syn region cPreCondit start="^\s*\(%:\|#\)\s*\(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError
|
||||
syn match cPreCondit display "^\s*\(%:\|#\)\s*\(else\|endif\)\>"
|
||||
if !exists("c_no_if0")
|
||||
if !exists("c_no_if0_fold")
|
||||
syn region cCppOut start="^\s*\(%:\|#\)\s*if\s\+0\+\>" end=".\@=\|$" contains=cCppOut2 fold
|
||||
else
|
||||
syn region cCppOut start="^\s*\(%:\|#\)\s*if\s\+0\+\>" end=".\@=\|$" contains=cCppOut2
|
||||
endif
|
||||
syn region cCppOut2 contained start="0" end="^\s*\(%:\|#\)\s*\(endif\>\|else\>\|elif\>\)" contains=cSpaceError,cCppSkip
|
||||
syn region cCppSkip contained start="^\s*\(%:\|#\)\s*\(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\(%:\|#\)\s*endif\>" contains=cSpaceError,cCppSkip
|
||||
endif
|
||||
syn region cIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+
|
||||
syn match cIncluded display contained "<[^>]*>"
|
||||
syn match cInclude display "^\s*\(%:\|#\)\s*include\>\s*["<]" contains=cIncluded
|
||||
"syn match cLineSkip "\\$"
|
||||
syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOut,cCppOut2,cCppSkip,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti
|
||||
syn region cDefine start="^\s*\(%:\|#\)\s*\(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
|
||||
syn region cPreProc start="^\s*\(%:\|#\)\s*\(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell
|
||||
|
||||
" Highlight User Labels
|
||||
syn cluster cMultiGroup contains=cIncluded,cSpecial,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cUserCont,cUserLabel,cOctalZero,cCppOut,cCppOut2,cCppSkip,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cCppParen,cCppBracket,cCppString
|
||||
syn region cMulti transparent start='?' skip='::' end=':' end=')' end=';' contains=ALLBUT,@cMultiGroup,@Spell
|
||||
" Avoid matching foo::bar() in C++ by requiring that the next char is not ':'
|
||||
|
||||
syn match cUserLabel display "\I\i*" contained
|
||||
|
||||
|
||||
if exists("c_minlines")
|
||||
let b:c_minlines = c_minlines
|
||||
else
|
||||
if !exists("c_no_if0")
|
||||
let b:c_minlines = 50 " #if 0 constructs can be long
|
||||
else
|
||||
let b:c_minlines = 15 " mostly for () constructs
|
||||
endif
|
||||
endif
|
||||
if exists("c_curly_error")
|
||||
syn sync fromstart
|
||||
else
|
||||
exec "syn sync ccomment cComment minlines=" . b:c_minlines
|
||||
endif
|
||||
|
||||
" Define the default highlighting.
|
||||
" Only used when an item doesn't have highlighting yet
|
||||
hi def link cFormat cSpecial
|
||||
hi def link cCppString cString
|
||||
hi def link cCommentL cComment
|
||||
hi def link cCommentStart cComment
|
||||
hi def link cUserLabel Label
|
||||
hi def link cRepeat Repeat
|
||||
hi def link cCharacter Character
|
||||
hi def link cSpecialCharacter cSpecial
|
||||
hi def link cNumber Number
|
||||
hi def link cOctal Number
|
||||
hi def link cOctalZero PreProc " link this to Error if you want
|
||||
hi def link cFloat Float
|
||||
hi def link cOctalError cError
|
||||
hi def link cParenError cError
|
||||
hi def link cErrInParen cError
|
||||
hi def link cErrInBracket cError
|
||||
hi def link cCommentError cError
|
||||
hi def link cCommentStartError cError
|
||||
hi def link cSpaceError cError
|
||||
hi def link cSpecialError cError
|
||||
hi def link cCurlyError cError
|
||||
hi def link cOperator Operator
|
||||
hi def link cStructure Structure
|
||||
hi def link cStorageClass StorageClass
|
||||
hi def link cInclude Include
|
||||
hi def link cPreProc PreProc
|
||||
hi def link cDefine Macro
|
||||
hi def link cIncluded cString
|
||||
hi def link cError Error
|
||||
hi def link cStatement Statement
|
||||
hi def link cPreCondit PreCondit
|
||||
hi def link cType Type
|
||||
hi def link cConstant Constant
|
||||
hi def link cCommentString cString
|
||||
hi def link cComment2String cString
|
||||
hi def link cCommentSkip cComment
|
||||
hi def link cString String
|
||||
hi def link cComment Comment
|
||||
hi def link cSpecial SpecialChar
|
||||
hi def link cTodo Todo
|
||||
hi def link cBadContinuation Error
|
||||
hi def link cCppSkip cCppOut
|
||||
hi def link cCppOut2 cCppOut
|
||||
hi def link cCppOut Comment
|
||||
|
||||
let b:current_syntax = "eel2"
|
||||
|
||||
" vim: ts=8
|
||||
+376
@@ -0,0 +1,376 @@
|
||||
%pure-parser
|
||||
%name-prefix="nseel"
|
||||
%parse-param { compileContext* context }
|
||||
%lex-param { void* scanner }
|
||||
|
||||
|
||||
/* this will prevent y.tab.c from ever calling yydestruct(), since we do not use it and it is a waste */
|
||||
%destructor {
|
||||
#define yydestruct(a,b,c,d,e)
|
||||
} VALUE
|
||||
|
||||
|
||||
%{
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "y.tab.h"
|
||||
#include "ns-eel-int.h"
|
||||
|
||||
#define scanner context->scanner
|
||||
#define YY_(x) ("")
|
||||
|
||||
%}
|
||||
|
||||
%token VALUE IDENTIFIER TOKEN_SHL TOKEN_SHR
|
||||
%token TOKEN_LTE TOKEN_GTE TOKEN_EQ TOKEN_EQ_EXACT TOKEN_NE TOKEN_NE_EXACT TOKEN_LOGICAL_AND TOKEN_LOGICAL_OR
|
||||
%token TOKEN_ADD_OP TOKEN_SUB_OP TOKEN_MOD_OP TOKEN_OR_OP TOKEN_AND_OP TOKEN_XOR_OP TOKEN_DIV_OP TOKEN_MUL_OP TOKEN_POW_OP
|
||||
%token STRING_LITERAL STRING_IDENTIFIER
|
||||
%expect 75
|
||||
%start program
|
||||
|
||||
%%
|
||||
|
||||
more_params:
|
||||
expression
|
||||
| expression ',' more_params
|
||||
{
|
||||
$$ = nseel_createMoreParametersOpcode(context,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
string:
|
||||
STRING_LITERAL
|
||||
| STRING_LITERAL string
|
||||
{
|
||||
((struct eelStringSegmentRec *)$1)->_next = (struct eelStringSegmentRec *)$2;
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
assignable_value:
|
||||
IDENTIFIER
|
||||
{
|
||||
if (!($$ = nseel_resolve_named_symbol(context, $1, -1, NULL))) /* convert from purely named to namespace-relative, etc */
|
||||
{
|
||||
yyerror(&yyloc, context, ""); if (nseelnerrs) { }
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
/* we used to have VALUE in here rather than rvalue, to allow 1=1 1+=2 etc, but silly to,
|
||||
though this breaks Vmorph, which does 1=1 for a nop, and Jonas DrumReaplacer, which does x = 0 = y = 0 */
|
||||
| '(' expression ')'
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
| IDENTIFIER '(' expression ')' '(' expression ')'
|
||||
{
|
||||
int err;
|
||||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, $6, &err)))
|
||||
{
|
||||
if (err == -1) yyerror(&yylsp[-2], context, "");
|
||||
else if (err == 0) yyerror(&yylsp[-6], context, "");
|
||||
else yyerror(&yylsp[-3], context, ""); // parameter count wrong
|
||||
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
| IDENTIFIER '(' expression ')'
|
||||
{
|
||||
int err;
|
||||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, 0, 0, 0, &err)))
|
||||
{
|
||||
if (err == 0) yyerror(&yylsp[-3], context, "");
|
||||
else yyerror(&yylsp[0], context, ""); // parameter count wrong
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
| IDENTIFIER '(' ')'
|
||||
{
|
||||
int err;
|
||||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, nseel_createCompiledValue(context,0.0), 0, 0, 0,&err)))
|
||||
{
|
||||
if (err == 0) yyerror(&yylsp[-2], context, ""); // function not found
|
||||
else yyerror(&yylsp[0], context, ""); // parameter count wrong
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
| IDENTIFIER '(' expression ',' expression ')'
|
||||
{
|
||||
int err;
|
||||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, 0, 0,&err)))
|
||||
{
|
||||
if (err == 0) yyerror(&yylsp[-5], context, "");
|
||||
else if (err == 2) yyerror(&yylsp[0], context, ""); // needs more than 2 parameters
|
||||
else yyerror(&yylsp[-2], context, ""); // less than 2
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
| IDENTIFIER '(' expression ',' expression ',' more_params ')'
|
||||
{
|
||||
int err;
|
||||
if (!($$ = nseel_setCompiledFunctionCallParameters(context,$1, $3, $5, $7, 0, &err)))
|
||||
{
|
||||
if (err == 0) yyerror(&yylsp[-7], context, "");
|
||||
else if (err==2) yyerror(&yylsp[0], context, ""); // needs more parameters
|
||||
else if (err==4) yyerror(&yylsp[-4], context, ""); // needs single parameter
|
||||
else yyerror(&yylsp[-2], context, ""); // less parm
|
||||
YYERROR;
|
||||
}
|
||||
}
|
||||
| rvalue '[' ']'
|
||||
{
|
||||
$$ = nseel_createMemoryAccess(context,$1,0);
|
||||
}
|
||||
| rvalue '[' expression ']'
|
||||
{
|
||||
$$ = nseel_createMemoryAccess(context,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
rvalue:
|
||||
VALUE
|
||||
| STRING_IDENTIFIER
|
||||
| string
|
||||
{
|
||||
$$ = nseel_eelMakeOpcodeFromStringSegments(context,(struct eelStringSegmentRec *)$1);
|
||||
}
|
||||
| assignable_value
|
||||
;
|
||||
|
||||
|
||||
assignment:
|
||||
rvalue
|
||||
| assignable_value '=' if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_ASSIGN,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_ADD_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_SUB_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_MOD_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_OR_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_OR_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_AND_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_AND_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_XOR_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_DIV_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_DIV_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_MUL_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_MUL_OP,2,$1,$3);
|
||||
}
|
||||
| assignable_value TOKEN_POW_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_POW_OP,2,$1,$3);
|
||||
}
|
||||
| STRING_IDENTIFIER '=' if_else_expr
|
||||
{
|
||||
$$ = nseel_createFunctionByName(context,"strcpy",2,$1,$3,NULL);
|
||||
}
|
||||
| STRING_IDENTIFIER TOKEN_ADD_OP if_else_expr
|
||||
{
|
||||
$$ = nseel_createFunctionByName(context,"strcat",2,$1,$3,NULL);
|
||||
}
|
||||
;
|
||||
|
||||
unary_expr:
|
||||
assignment
|
||||
| '+' unary_expr
|
||||
{
|
||||
$$ = $2;
|
||||
}
|
||||
| '-' unary_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_UMINUS,1,$2,0);
|
||||
}
|
||||
| '!' unary_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_NOT,1,$2,0);
|
||||
}
|
||||
;
|
||||
|
||||
pow_expr:
|
||||
unary_expr
|
||||
| pow_expr '^' unary_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_POW,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
mod_expr:
|
||||
pow_expr
|
||||
| mod_expr '%' pow_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_MOD,2,$1,$3);
|
||||
}
|
||||
| mod_expr TOKEN_SHL pow_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_SHL,2,$1,$3);
|
||||
}
|
||||
| mod_expr TOKEN_SHR pow_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_SHR,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
div_expr:
|
||||
mod_expr
|
||||
| div_expr '/' mod_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_DIVIDE,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
mul_expr:
|
||||
div_expr
|
||||
| mul_expr '*' div_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_MULTIPLY,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
sub_expr:
|
||||
mul_expr
|
||||
| sub_expr '-' mul_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_SUB,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
add_expr:
|
||||
sub_expr
|
||||
| add_expr '+' sub_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_ADD,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
andor_expr:
|
||||
add_expr
|
||||
| andor_expr '&' add_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_AND,2,$1,$3);
|
||||
}
|
||||
| andor_expr '|' add_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_OR,2,$1,$3);
|
||||
}
|
||||
| andor_expr '~' add_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_XOR,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
cmp_expr:
|
||||
andor_expr
|
||||
| cmp_expr '<' andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_LT,2,$1,$3);
|
||||
}
|
||||
| cmp_expr '>' andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_GT,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_LTE andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_LTE,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_GTE andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_GTE,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_EQ andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_EQ_EXACT andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_EQ_EXACT,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_NE andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_NE,2,$1,$3);
|
||||
}
|
||||
| cmp_expr TOKEN_NE_EXACT andor_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_NE_EXACT,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
logical_and_or_expr:
|
||||
cmp_expr
|
||||
| logical_and_or_expr TOKEN_LOGICAL_AND cmp_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_AND,2,$1,$3);
|
||||
}
|
||||
| logical_and_or_expr TOKEN_LOGICAL_OR cmp_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_LOGICAL_OR,2,$1,$3);
|
||||
}
|
||||
;
|
||||
|
||||
if_else_expr:
|
||||
logical_and_or_expr
|
||||
| logical_and_or_expr '?' if_else_expr ':' if_else_expr
|
||||
{
|
||||
$$ = nseel_createIfElse(context, $1, $3, $5);
|
||||
}
|
||||
| logical_and_or_expr '?' ':' if_else_expr
|
||||
{
|
||||
$$ = nseel_createIfElse(context, $1, 0, $4);
|
||||
}
|
||||
| logical_and_or_expr '?' if_else_expr
|
||||
{
|
||||
$$ = nseel_createIfElse(context, $1, $3, 0);
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
expression:
|
||||
if_else_expr
|
||||
| expression ';' if_else_expr
|
||||
{
|
||||
$$ = nseel_createSimpleCompiledFunction(context,FN_JOIN_STATEMENTS,2,$1,$3);
|
||||
}
|
||||
| expression ';'
|
||||
{
|
||||
$$ = $1;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
program:
|
||||
expression
|
||||
{
|
||||
if (@1.first_line) { }
|
||||
context->result = $1;
|
||||
}
|
||||
;
|
||||
|
||||
|
||||
%%
|
||||
@@ -0,0 +1,71 @@
|
||||
#ifndef __EEL_ATOMIC_H__
|
||||
#define __EEL_ATOMIC_H__
|
||||
|
||||
// requires these to be defined
|
||||
//#define EEL_ATOMIC_SET_SCOPE(opaque) WDL_Mutex *mutex = (opaque?&((effectProcessor *)opaque)->m_atomic_mutex:&atomic_mutex);
|
||||
//#define EEL_ATOMIC_ENTER mutex->Enter()
|
||||
//#define EEL_ATOMIC_LEAVE mutex->Leave()
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL atomic_setifeq(void *opaque, EEL_F *a, EEL_F *cmp, EEL_F *nd)
|
||||
{
|
||||
EEL_F ret;
|
||||
EEL_ATOMIC_SET_SCOPE(opaque)
|
||||
EEL_ATOMIC_ENTER;
|
||||
ret = *a;
|
||||
if (fabs(ret - *cmp) < NSEEL_CLOSEFACTOR) *a = *nd;
|
||||
EEL_ATOMIC_LEAVE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL atomic_exch(void *opaque, EEL_F *a, EEL_F *b)
|
||||
{
|
||||
EEL_F tmp;
|
||||
EEL_ATOMIC_SET_SCOPE(opaque)
|
||||
EEL_ATOMIC_ENTER;
|
||||
tmp = *b;
|
||||
*b = *a;
|
||||
*a = tmp;
|
||||
EEL_ATOMIC_LEAVE;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL atomic_add(void *opaque, EEL_F *a, EEL_F *b)
|
||||
{
|
||||
EEL_F tmp;
|
||||
EEL_ATOMIC_SET_SCOPE(opaque)
|
||||
EEL_ATOMIC_ENTER;
|
||||
tmp = (*a += *b);
|
||||
EEL_ATOMIC_LEAVE;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL atomic_set(void *opaque, EEL_F *a, EEL_F *b)
|
||||
{
|
||||
EEL_F tmp;
|
||||
EEL_ATOMIC_SET_SCOPE(opaque)
|
||||
EEL_ATOMIC_ENTER;
|
||||
tmp = *a = *b;
|
||||
EEL_ATOMIC_LEAVE;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL atomic_get(void *opaque, EEL_F *a)
|
||||
{
|
||||
EEL_F tmp;
|
||||
EEL_ATOMIC_SET_SCOPE(opaque)
|
||||
EEL_ATOMIC_ENTER;
|
||||
tmp = *a;
|
||||
EEL_ATOMIC_LEAVE;
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void EEL_atomic_register()
|
||||
{
|
||||
NSEEL_addfunc_retval("atomic_setifequal",3, NSEEL_PProc_THIS, &atomic_setifeq);
|
||||
NSEEL_addfunc_retval("atomic_exch",2, NSEEL_PProc_THIS, &atomic_exch);
|
||||
NSEEL_addfunc_retval("atomic_add",2, NSEEL_PProc_THIS, &atomic_add);
|
||||
NSEEL_addfunc_retval("atomic_set",2, NSEEL_PProc_THIS, &atomic_set);
|
||||
NSEEL_addfunc_retval("atomic_get",1, NSEEL_PProc_THIS, &atomic_get);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef _EEL_EVAL_H_
|
||||
#define _EEL_EVAL_H_
|
||||
|
||||
#ifndef EEL_EVAL_GET_CACHED
|
||||
#define EEL_EVAL_GET_CACHED(str, ch) (NULL)
|
||||
#endif
|
||||
|
||||
#ifndef EEL_EVAL_SET_CACHED
|
||||
#define EEL_EVAL_SET_CACHED(sv, ch) { NSEEL_code_free(ch); free(sv); }
|
||||
#endif
|
||||
|
||||
#ifndef EEL_EVAL_SCOPE_ENTER
|
||||
#define EEL_EVAL_SCOPE_ENTER 1
|
||||
#define EEL_EVAL_SCOPE_LEAVE
|
||||
#endif
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_eval(void *opaque, EEL_F *s)
|
||||
{
|
||||
NSEEL_VMCTX r = EEL_EVAL_GET_VMCTX(opaque);
|
||||
NSEEL_CODEHANDLE ch = NULL;
|
||||
char *sv=NULL;
|
||||
if (r)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (!str)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("eval() passed invalid string handle %f",*s);
|
||||
}
|
||||
#endif
|
||||
if (str && *str)
|
||||
{
|
||||
sv=EEL_EVAL_GET_CACHED(str,ch);
|
||||
if (!sv) sv=strdup(str);
|
||||
}
|
||||
}
|
||||
if (sv)
|
||||
{
|
||||
if (!ch) ch = NSEEL_code_compile(r,sv,0);
|
||||
if (ch)
|
||||
{
|
||||
if (EEL_EVAL_SCOPE_ENTER)
|
||||
{
|
||||
NSEEL_code_execute(ch);
|
||||
EEL_EVAL_SCOPE_LEAVE
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("eval() reentrancy limit reached");
|
||||
#endif
|
||||
}
|
||||
|
||||
EEL_EVAL_SET_CACHED(sv,ch);
|
||||
return 1.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
const char *err=NSEEL_code_getcodeerror(r);
|
||||
if (err) EEL_STRING_DEBUGOUT("eval() error: %s",err);
|
||||
#endif
|
||||
}
|
||||
free(sv);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void EEL_eval_register()
|
||||
{
|
||||
NSEEL_addfunc_retval("eval",1,NSEEL_PProc_THIS,&_eel_eval);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
static const char *eel_eval_function_reference =
|
||||
"eval\t\"code\"\tExecutes code passed in. Code can use functions, but functions created in code can't be used elsewhere.\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,396 @@
|
||||
#ifndef __EEL_FFT_H_
|
||||
#define __EEL_FFT_H_
|
||||
|
||||
#include "../fft.h"
|
||||
#if WDL_FFT_REALSIZE != EEL_F_SIZE
|
||||
#error WDL_FFT_REALSIZE -- EEL_F_SIZE size mismatch
|
||||
#endif
|
||||
|
||||
#ifndef EEL_FFT_MINBITLEN
|
||||
#define EEL_FFT_MINBITLEN 4
|
||||
#endif
|
||||
|
||||
#ifndef EEL_FFT_MAXBITLEN
|
||||
#define EEL_FFT_MAXBITLEN 15
|
||||
#endif
|
||||
|
||||
#ifndef EEL_FFT_MINBITLEN_REORDER
|
||||
#define EEL_FFT_MINBITLEN_REORDER (EEL_FFT_MINBITLEN-1)
|
||||
#endif
|
||||
|
||||
//#define EEL_SUPER_FAST_FFT_REORDERING // quite a bit faster (50-100%) than "normal", but uses a 256kb lookup
|
||||
//#define EEL_SLOW_FFT_REORDERING // 20%-80% slower than normal, alloca() use, no reason to ever use this
|
||||
|
||||
#ifdef EEL_SUPER_FAST_FFT_REORDERING
|
||||
static int *fft_reorder_table_for_bitsize(int bitsz)
|
||||
{
|
||||
static int s_tab[ (2 << EEL_FFT_MAXBITLEN) + 24*(EEL_FFT_MAXBITLEN-EEL_FFT_MINBITLEN_REORDER+1) ]; // big 256kb table, ugh
|
||||
if (bitsz<=EEL_FFT_MINBITLEN_REORDER) return s_tab;
|
||||
return s_tab + (1<<bitsz) + (bitsz-EEL_FFT_MINBITLEN_REORDER) * 24;
|
||||
}
|
||||
static void fft_make_reorder_table(int bitsz, int *tab)
|
||||
{
|
||||
const int fft_sz=1<<bitsz;
|
||||
char flag[1<<EEL_FFT_MAXBITLEN];
|
||||
int x;
|
||||
int *tabstart = tab;
|
||||
memset(flag,0,fft_sz);
|
||||
|
||||
for (x=0;x<fft_sz;x++)
|
||||
{
|
||||
int fx;
|
||||
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x)
|
||||
{
|
||||
flag[x]=1;
|
||||
*tab++ = x;
|
||||
do
|
||||
{
|
||||
flag[fx]=1;
|
||||
*tab++ = fx;
|
||||
fx = WDL_fft_permute(fft_sz, fx);
|
||||
}
|
||||
while (fx != x);
|
||||
*tab++ = 0; // delimit a run
|
||||
}
|
||||
else flag[x]=1;
|
||||
}
|
||||
*tab++ = 0; // doublenull terminated
|
||||
}
|
||||
|
||||
static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd)
|
||||
{
|
||||
const int *tab=fft_reorder_table_for_bitsize(bitsz);
|
||||
if (!fwd)
|
||||
{
|
||||
while (*tab)
|
||||
{
|
||||
const int sidx=*tab++;
|
||||
WDL_FFT_COMPLEX a=data[sidx];
|
||||
for (;;)
|
||||
{
|
||||
WDL_FFT_COMPLEX ta;
|
||||
const int idx=*tab++;
|
||||
if (!idx) break;
|
||||
ta=data[idx];
|
||||
data[idx]=a;
|
||||
a=ta;
|
||||
}
|
||||
data[sidx] = a;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*tab)
|
||||
{
|
||||
const int sidx=*tab++;
|
||||
int lidx = sidx;
|
||||
const WDL_FFT_COMPLEX sta=data[lidx];
|
||||
for (;;)
|
||||
{
|
||||
const int idx=*tab++;
|
||||
if (!idx) break;
|
||||
|
||||
data[lidx]=data[idx];
|
||||
lidx=idx;
|
||||
}
|
||||
data[lidx] = sta;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
#ifndef EEL_SLOW_FFT_REORDERING
|
||||
// moderate speed mode, minus the big 256k table
|
||||
|
||||
static void fft_reorder_buffer(int bitsz, WDL_FFT_COMPLEX *data, int fwd)
|
||||
{
|
||||
// this is a good compromise, quite a bit faster than out of place reordering, but no separate 256kb lookup required
|
||||
/*
|
||||
these generated via:
|
||||
static void fft_make_reorder_table(int bitsz)
|
||||
{
|
||||
int fft_sz=1<<bitsz,x;
|
||||
char flag[65536]={0,};
|
||||
printf("static const int tab%d[]={ ",fft_sz);
|
||||
for (x=0;x<fft_sz;x++)
|
||||
{
|
||||
int fx;
|
||||
if (!flag[x] && (fx=WDL_fft_permute(fft_sz,x))!=x)
|
||||
{
|
||||
printf("%d, ",x);
|
||||
do { flag[fx]=1; fx = WDL_fft_permute(fft_sz, fx); } while (fx != x);
|
||||
}
|
||||
flag[x]=1;
|
||||
}
|
||||
printf(" 0 };\n");
|
||||
}
|
||||
*/
|
||||
|
||||
static const int tab4_8_32[]={ 1, 0 };
|
||||
static const int tab16[]={ 1, 3, 0 };
|
||||
static const int tab64[]={ 1, 3, 9, 0 };
|
||||
static const int tab128[]={ 1, 3, 4, 9, 14, 0 };
|
||||
static const int tab256[]={ 1, 3, 6, 12, 13, 14, 19, 0 };
|
||||
static const int tab512[]={ 1, 4, 7, 9, 18, 50, 115, 0 };
|
||||
static const int tab1024[]={ 1, 3, 4, 25, 26, 77, 79, 0 };
|
||||
static const int tab2048[]={ 1, 58, 59, 106, 135, 206, 210, 212, 0 };
|
||||
static const int tab4096[]={ 1, 3, 12, 25, 54, 221, 313, 431, 453, 0 };
|
||||
static const int tab8192[]={ 1, 12, 18, 26, 30, 100, 101, 106, 113, 144, 150, 237, 244, 247, 386, 468, 513, 1210, 4839, 0 };
|
||||
static const int tab16384[]={ 1, 3, 6, 24, 1219, 0 };
|
||||
static const int tab32768[]={ 1, 3, 4, 7, 13, 18, 31, 64, 113, 145, 203, 246, 594, 956, 1871, 2439, 4959, 19175, 0 };
|
||||
const int *tab;
|
||||
|
||||
switch (bitsz)
|
||||
{
|
||||
case 1: return; // no reorder necessary
|
||||
case 2:
|
||||
case 3:
|
||||
case 5: tab = tab4_8_32; break;
|
||||
case 4: tab=tab16; break;
|
||||
case 6: tab=tab64; break;
|
||||
case 7: tab=tab128; break;
|
||||
case 8: tab=tab256; break;
|
||||
case 9: tab=tab512; break;
|
||||
case 10: tab=tab1024; break;
|
||||
case 11: tab=tab2048; break;
|
||||
case 12: tab=tab4096; break;
|
||||
case 13: tab=tab8192; break;
|
||||
case 14: tab=tab16384; break;
|
||||
case 15: tab=tab32768; break;
|
||||
default: return; // no reorder possible
|
||||
}
|
||||
|
||||
const int fft_sz=1<<bitsz;
|
||||
const int *tb2 = WDL_fft_permute_tab(fft_sz);
|
||||
if (!tb2) return; // ugh
|
||||
|
||||
if (!fwd)
|
||||
{
|
||||
while (*tab)
|
||||
{
|
||||
const int sidx=*tab++;
|
||||
WDL_FFT_COMPLEX a=data[sidx];
|
||||
int idx=sidx;
|
||||
for (;;)
|
||||
{
|
||||
WDL_FFT_COMPLEX ta;
|
||||
idx=tb2[idx];
|
||||
if (idx==sidx) break;
|
||||
ta=data[idx];
|
||||
data[idx]=a;
|
||||
a=ta;
|
||||
}
|
||||
data[sidx] = a;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while (*tab)
|
||||
{
|
||||
const int sidx=*tab++;
|
||||
int lidx = sidx;
|
||||
const WDL_FFT_COMPLEX sta=data[lidx];
|
||||
for (;;)
|
||||
{
|
||||
const int idx=tb2[lidx];
|
||||
if (idx==sidx) break;
|
||||
|
||||
data[lidx]=data[idx];
|
||||
lidx=idx;
|
||||
}
|
||||
data[lidx] = sta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // not fast ,not slow, just right
|
||||
|
||||
#endif
|
||||
|
||||
//#define TIMING
|
||||
//#include "../timing.h"
|
||||
|
||||
// 0=fw, 1=iv, 2=fwreal, 3=ireal, 4=permutec, 6=permuter
|
||||
// low bit: is inverse
|
||||
// second bit: was isreal, but no longer used
|
||||
// third bit: is permute
|
||||
static void FFT(int sizebits, EEL_F *data, int dir)
|
||||
{
|
||||
if (dir >= 4 && dir < 8)
|
||||
{
|
||||
if (dir == 4 || dir == 5)
|
||||
{
|
||||
//timingEnter(0);
|
||||
#if defined(EEL_SUPER_FAST_FFT_REORDERING) || !defined(EEL_SLOW_FFT_REORDERING)
|
||||
fft_reorder_buffer(sizebits,(WDL_FFT_COMPLEX*)data,dir==4);
|
||||
#else
|
||||
// old blech
|
||||
const int flen=1<<sizebits;
|
||||
int x;
|
||||
EEL_F *tmp=(EEL_F*)alloca(sizeof(EEL_F)*flen*2);
|
||||
const int flen2=flen+flen;
|
||||
// reorder entries, now
|
||||
memcpy(tmp,data,sizeof(EEL_F)*flen*2);
|
||||
|
||||
if (dir == 4)
|
||||
{
|
||||
for (x = 0; x < flen2; x += 2)
|
||||
{
|
||||
int y=WDL_fft_permute(flen,x/2)*2;
|
||||
data[x]=tmp[y];
|
||||
data[x+1]=tmp[y+1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (x = 0; x < flen2; x += 2)
|
||||
{
|
||||
int y=WDL_fft_permute(flen,x/2)*2;
|
||||
data[y]=tmp[x];
|
||||
data[y+1]=tmp[x+1];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
//timingLeave(0);
|
||||
}
|
||||
}
|
||||
else if (dir >= 0 && dir < 2)
|
||||
{
|
||||
WDL_fft((WDL_FFT_COMPLEX*)data,1<<sizebits,dir&1);
|
||||
}
|
||||
else if (dir >= 2 && dir < 4)
|
||||
{
|
||||
WDL_real_fft((WDL_FFT_REAL*)data,1<<sizebits,dir&1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static EEL_F * fft_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
const int offs = (int)(*start + 0.0001);
|
||||
const int itemSizeShift=(dir&2)?0:1;
|
||||
int l=(int)(*length + 0.0001);
|
||||
int bitl=0;
|
||||
int ilen;
|
||||
EEL_F *ptr;
|
||||
while (l>1 && bitl < EEL_FFT_MAXBITLEN)
|
||||
{
|
||||
bitl++;
|
||||
l>>=1;
|
||||
}
|
||||
if (bitl < ((dir&4) ? EEL_FFT_MINBITLEN_REORDER : EEL_FFT_MINBITLEN)) // smallest FFT is 16 item, smallest reorder is 8 item
|
||||
{
|
||||
return start;
|
||||
}
|
||||
ilen=1<<bitl;
|
||||
|
||||
|
||||
// check to make sure we don't cross a boundary
|
||||
if (offs/NSEEL_RAM_ITEMSPERBLOCK != (offs + (ilen<<itemSizeShift) - 1)/NSEEL_RAM_ITEMSPERBLOCK)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
|
||||
ptr=__NSEEL_RAMAlloc(blocks,offs);
|
||||
if (!ptr || ptr==&nseel_ramalloc_onfail)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
|
||||
FFT(bitl,ptr,dir);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_fft(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(0,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_ifft(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(1,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_fft_real(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(2,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_ifft_real(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(3,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_fft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(4,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_ifft_permute(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return fft_func(5,blocks,start,length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL eel_convolve_c(EEL_F **blocks,EEL_F *dest, EEL_F *src, EEL_F *lenptr)
|
||||
{
|
||||
const int dest_offs = (int)(*dest + 0.0001);
|
||||
const int src_offs = (int)(*src + 0.0001);
|
||||
const int len = ((int)(*lenptr + 0.0001)) * 2;
|
||||
EEL_F *srcptr,*destptr;
|
||||
|
||||
if (len < 1 || len > NSEEL_RAM_ITEMSPERBLOCK || dest_offs < 0 || src_offs < 0 ||
|
||||
dest_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK || src_offs >= NSEEL_RAM_BLOCKS*NSEEL_RAM_ITEMSPERBLOCK) return dest;
|
||||
if ((dest_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest;
|
||||
if ((src_offs&(NSEEL_RAM_ITEMSPERBLOCK-1)) + len > NSEEL_RAM_ITEMSPERBLOCK) return dest;
|
||||
|
||||
srcptr = __NSEEL_RAMAlloc(blocks,src_offs);
|
||||
if (!srcptr || srcptr==&nseel_ramalloc_onfail) return dest;
|
||||
destptr = __NSEEL_RAMAlloc(blocks,dest_offs);
|
||||
if (!destptr || destptr==&nseel_ramalloc_onfail) return dest;
|
||||
|
||||
|
||||
WDL_fft_complexmul((WDL_FFT_COMPLEX*)destptr,(WDL_FFT_COMPLEX*)srcptr,(len/2)&~1);
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
void EEL_fft_register()
|
||||
{
|
||||
WDL_fft_init();
|
||||
#if defined(EEL_SUPER_FAST_FFT_REORDERING)
|
||||
if (!fft_reorder_table_for_bitsize(EEL_FFT_MINBITLEN_REORDER)[0])
|
||||
{
|
||||
int x;
|
||||
for (x=EEL_FFT_MINBITLEN_REORDER;x<=EEL_FFT_MAXBITLEN;x++) fft_make_reorder_table(x,fft_reorder_table_for_bitsize(x));
|
||||
}
|
||||
#endif
|
||||
NSEEL_addfunc_retptr("convolve_c",3,NSEEL_PProc_RAM,&eel_convolve_c);
|
||||
NSEEL_addfunc_retptr("fft",2,NSEEL_PProc_RAM,&eel_fft);
|
||||
NSEEL_addfunc_retptr("ifft",2,NSEEL_PProc_RAM,&eel_ifft);
|
||||
NSEEL_addfunc_retptr("fft_real",2,NSEEL_PProc_RAM,&eel_fft_real);
|
||||
NSEEL_addfunc_retptr("ifft_real",2,NSEEL_PProc_RAM,&eel_ifft_real);
|
||||
NSEEL_addfunc_retptr("fft_permute",2,NSEEL_PProc_RAM,&eel_fft_permute);
|
||||
NSEEL_addfunc_retptr("fft_ipermute",2,NSEEL_PProc_RAM,&eel_ifft_permute);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
static const char *eel_fft_function_reference =
|
||||
"convolve_c\tdest,src,size\tMultiplies each of size complex pairs in dest by the complex pairs in src. Often used for convolution.\0"
|
||||
"fft\tbuffer,size\tPerforms a FFT on the data in the local memory buffer at the offset specified by the first parameter. The size of the FFT is specified "
|
||||
"by the second parameter, which must be 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, or 32768. The outputs are permuted, so if "
|
||||
"you plan to use them in-order, call fft_permute(buffer, size) before and fft_ipermute(buffer,size) after your in-order use. Your inputs or "
|
||||
"outputs will need to be scaled down by 1/size, if used.\n"
|
||||
"Note that fft()/ifft() require real / imaginary input pairs, so a 256 point FFT actually works with 512 items.\n"
|
||||
"Note that fft()/ifft() must NOT cross a 65,536 item boundary, so be sure to specify the offset accordingly.\0"
|
||||
"ifft\tbuffer,size\tPerform an inverse FFT. For more information see fft().\0"
|
||||
"fft_real\tbuffer,size\tPerforms an FFT, but takes size input samples and produces size/2 complex output pairs. Usually used along with fft_permute(size/2). Inputs/outputs will need to be scaled by 0.5/size.\0"
|
||||
"ifft_real\tbuffer,size\tPerforms an inverse FFT, but takes size/2 complex input pairs and produces size real output values. Usually used along with fft_ipermute(size/2).\0"
|
||||
"fft_permute\tbuffer,size\tPermute the output of fft() to have bands in-order. See fft() for more information.\0"
|
||||
"fft_ipermute\tbuffer,size\tPermute the input for ifft(), taking bands from in-order to the order ifft() requires. See fft() for more information.\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,262 @@
|
||||
#ifndef __EEL_FILES_H__
|
||||
#define __EEL_FILES_H__
|
||||
|
||||
// should include eel_strings.h before this, probably
|
||||
//#define EEL_FILE_OPEN(fn,mode) ((instance)opaque)->OpenFile(fn,mode)
|
||||
//#define EEL_FILE_GETFP(fp) ((instance)opaque)->GetFileFP(fp)
|
||||
//#define EEL_FILE_CLOSE(fpindex) ((instance)opaque)->CloseFile(fpindex)
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fopen(void *opaque, EEL_F *fn_index, EEL_F *mode_index)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *fn = EEL_STRING_GET_FOR_INDEX(*fn_index,NULL);
|
||||
const char *mode = EEL_STRING_GET_FOR_INDEX(*mode_index,NULL);
|
||||
if (!fn || !mode) return 0;
|
||||
return (EEL_F) EEL_FILE_OPEN(fn,mode);
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fclose(void *opaque, EEL_F *fpp)
|
||||
{
|
||||
EEL_F ret=EEL_FILE_CLOSE((int)*fpp);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (ret < 0) EEL_STRING_DEBUGOUT("fclose(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fgetc(void *opaque, EEL_F *fpp)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (fp) return (EEL_F)fgetc(fp);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fgetc(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_ftell(void *opaque, EEL_F *fpp)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (fp) return (EEL_F)ftell(fp);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("ftell(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fflush(void *opaque, EEL_F *fpp)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (fp) { fflush(fp); return 0.0; }
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fflush(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_feof(void *opaque, EEL_F *fpp)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (fp) return feof(fp) ? 1.0 : 0.0;
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("feof(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fseek(void *opaque, EEL_F *fpp, EEL_F *offset, EEL_F *wh)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (fp) return fseek(fp, (int) *offset, *wh<0 ? SEEK_SET : *wh > 0 ? SEEK_END : SEEK_CUR);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fseek(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fgets(void *opaque, EEL_F *fpp, EEL_F *strOut)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
EEL_STRING_STORAGECLASS *wr=NULL;
|
||||
EEL_STRING_GET_FOR_WRITE(*strOut, &wr);
|
||||
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (!fp)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fgets(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
if (wr) wr->Set("");
|
||||
return 0.0;
|
||||
}
|
||||
char buf[16384];
|
||||
buf[0]=0;
|
||||
fgets(buf,sizeof(buf),fp);
|
||||
if (wr)
|
||||
{
|
||||
wr->Set(buf);
|
||||
return (EEL_F)wr->GetLength();
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fgets: bad destination specifier passed %f, throwing away %d bytes",*strOut, (int)strlen(buf));
|
||||
#endif
|
||||
return (int)strlen(buf);
|
||||
}
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fread(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen)
|
||||
{
|
||||
int use_len = (int) *flen;
|
||||
if (use_len < 1) return 0.0;
|
||||
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
EEL_STRING_STORAGECLASS *wr=NULL;
|
||||
EEL_STRING_GET_FOR_WRITE(*strOut, &wr);
|
||||
if (!wr)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fread: bad destination specifier passed %f, not reading %d bytes",*strOut, use_len);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (!fp)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fread(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
if (wr) wr->Set("");
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
wr->SetLen(use_len);
|
||||
if (wr->GetLength() != use_len)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fread: error allocating storage for read of %d bytes", use_len);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
use_len = (int)fread((char *)wr->Get(),1,use_len,fp);
|
||||
wr->SetLen(use_len > 0 ? use_len : 0, true);
|
||||
return (EEL_F) use_len;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fwrite(void *opaque, EEL_F *fpp, EEL_F *strOut, EEL_F *flen)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
int use_len = (int) *flen;
|
||||
|
||||
EEL_STRING_STORAGECLASS *wr=NULL;
|
||||
const char *str=EEL_STRING_GET_FOR_INDEX(*strOut, &wr);
|
||||
if (!wr && !str)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fwrite: bad source specifier passed %f, not writing %d bytes",*strOut, use_len);
|
||||
#endif
|
||||
return -1.0;
|
||||
}
|
||||
if (!wr)
|
||||
{
|
||||
const int ssl = (int)strlen(str);
|
||||
if (use_len < 1 || use_len > ssl) use_len = ssl;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (use_len < 1 || use_len > wr->GetLength()) use_len = wr->GetLength();
|
||||
}
|
||||
|
||||
if (use_len < 1) return 0.0;
|
||||
|
||||
FILE *fp = EEL_FILE_GETFP((int)*fpp);
|
||||
if (!fp)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fwrite(): file handle %f not valid",*fpp);
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return (EEL_F) fwrite(str,1,use_len,fp);
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_fprintf(void *opaque, INT_PTR nparam, EEL_F **parm)
|
||||
{
|
||||
if (opaque && nparam > 1)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
FILE *fp = EEL_FILE_GETFP((int)*(parm[0]));
|
||||
if (!fp)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fprintf(): file handle %f not valid",parm[0][0]);
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
EEL_STRING_STORAGECLASS *wr_src=NULL;
|
||||
const char *fmt = EEL_STRING_GET_FOR_INDEX(*(parm[1]),&wr_src);
|
||||
if (fmt)
|
||||
{
|
||||
char buf[16384];
|
||||
const int len = eel_format_strings(opaque,fmt,wr_src?(fmt+wr_src->GetLength()) : NULL, buf,(int)sizeof(buf),(int)nparam-2,parm+2);
|
||||
|
||||
if (len >= 0)
|
||||
{
|
||||
return (EEL_F) fwrite(buf,1,len,fp);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fprintf: bad format string %s",fmt);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("fprintf: bad format specifier passed %f",*(parm[1]));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
void EEL_file_register()
|
||||
{
|
||||
NSEEL_addfunc_retval("fopen",2,NSEEL_PProc_THIS,&_eel_fopen);
|
||||
|
||||
NSEEL_addfunc_retval("fread",3,NSEEL_PProc_THIS,&_eel_fread);
|
||||
NSEEL_addfunc_retval("fgets",2,NSEEL_PProc_THIS,&_eel_fgets);
|
||||
NSEEL_addfunc_retval("fgetc",1,NSEEL_PProc_THIS,&_eel_fgetc);
|
||||
|
||||
NSEEL_addfunc_retval("fwrite",3,NSEEL_PProc_THIS,&_eel_fwrite);
|
||||
NSEEL_addfunc_varparm("fprintf",2,NSEEL_PProc_THIS,&_eel_fprintf);
|
||||
|
||||
NSEEL_addfunc_retval("fseek",3,NSEEL_PProc_THIS,&_eel_fseek);
|
||||
NSEEL_addfunc_retval("ftell",1,NSEEL_PProc_THIS,&_eel_ftell);
|
||||
NSEEL_addfunc_retval("feof",1,NSEEL_PProc_THIS,&_eel_feof);
|
||||
NSEEL_addfunc_retval("fflush",1,NSEEL_PProc_THIS,&_eel_fflush);
|
||||
NSEEL_addfunc_retval("fclose",1,NSEEL_PProc_THIS,&_eel_fclose);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
static const char *eel_file_function_reference =
|
||||
"fopen\t\"fn\",\"mode\"\tOpens a file \"fn\" with mode \"mode\". For read, use \"r\" or \"rb\", write \"w\" or \"wb\". Returns a positive integer on success.\0"
|
||||
"fclose\tfp\tCloses a file previously opened with fopen().\0"
|
||||
"fread\tfp,#str,length\tReads from file fp into #str, up to length bytes. Returns actual length read, or negative if error.\0"
|
||||
"fgets\tfp,#str\tReads a line from file fp into #str. Returns length of #str read.\0"
|
||||
"fgetc\tfp\tReads a character from file fp, returns -1 if EOF.\0"
|
||||
"fwrite\tfp,#str,len\tWrites up to len characters of #str to file fp. If len is less than 1, the full contents of #str will be written. Returns the number of bytes written to file.\0"
|
||||
"fprintf\tfp,\"format\"[,...]\tFormats a string and writes it to file fp. For more information on format specifiers, see sprintf(). Returns bytes written to file.\0"
|
||||
"fseek\tfp,offset,whence\tSeeks file fp, offset bytes from whence reference. Whence negative specifies start of file, positive whence specifies end of file, and zero whence specifies current file position.\0"
|
||||
"ftell\tfp\tRetunrs the current file position.\0"
|
||||
"feof\tfp\tReturns nonzero if the file fp is at the end of file.\0"
|
||||
"fflush\tfp\tIf file fp is open for writing, flushes out any buffered data to disk.\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
/*******************************************************************************************
|
||||
* imported EEL
|
||||
******************************************************************************************/
|
||||
|
||||
void (*NSEEL_addfunc_ret_type)(const char *name, int np, int ret_type, NSEEL_PPPROC pproc, void *fptr, eel_function_table *destination); // ret_type=-1 for bool, 1 for value, 0 for ptr
|
||||
void (*NSEEL_addfunc_varparm_ex)(const char *name, int min_np, int want_exact, NSEEL_PPPROC pproc, EEL_F (NSEEL_CGEN_CALL *fptr)(void *, INT_PTR, EEL_F **), eel_function_table *destination);
|
||||
|
||||
|
||||
NSEEL_VMCTX (*NSEEL_VM_alloc)(); // return a handle
|
||||
void (*NSEEL_VM_SetGRAM)(NSEEL_VMCTX, void **);
|
||||
void (*NSEEL_VM_free)(NSEEL_VMCTX ctx); // free when done with a VM and ALL of its code have been freed, as well
|
||||
void (*NSEEL_VM_SetFunctionTable)(NSEEL_VMCTX, eel_function_table *tab); // use NULL to use default (global) table
|
||||
EEL_F *(*NSEEL_VM_regvar)(NSEEL_VMCTX ctx, const char *name); // register a variable (before compilation)
|
||||
|
||||
void (*NSEEL_VM_SetCustomFuncThis)(NSEEL_VMCTX ctx, void *thisptr);
|
||||
NSEEL_CODEHANDLE (*NSEEL_code_compile_ex)(NSEEL_VMCTX ctx, const char *code, int lineoffs, int flags);
|
||||
void (*NSEEL_VM_set_var_resolver)(NSEEL_VMCTX ctx, EEL_F *(*res)(void *userctx, const char *name), void *userctx);
|
||||
char *(*NSEEL_code_getcodeerror)(NSEEL_VMCTX ctx);
|
||||
void (*NSEEL_code_execute)(NSEEL_CODEHANDLE code);
|
||||
void (*NSEEL_code_free)(NSEEL_CODEHANDLE code);
|
||||
EEL_F *(*nseel_int_register_var)(compileContext *ctx, const char *name, int isReg, const char **namePtrOut);
|
||||
void (*NSEEL_VM_enumallvars)(NSEEL_VMCTX ctx, int (*func)(const char *name, EEL_F *val, void *ctx), void *userctx);
|
||||
EEL_F *(*NSEEL_VM_getramptr)(NSEEL_VMCTX ctx, unsigned int offs, int *validAmt);
|
||||
void ** (*eel_gmem_attach)(const char *nm, bool is_alloc);
|
||||
void (*eel_fft_register)(eel_function_table*);
|
||||
|
||||
struct eelStringSegmentRec {
|
||||
struct eelStringSegmentRec *_next;
|
||||
const char *str_start; // escaped characters, including opening/trailing characters
|
||||
int str_len;
|
||||
};
|
||||
void (*NSEEL_VM_SetStringFunc)(NSEEL_VMCTX ctx,
|
||||
EEL_F (*onString)(void *caller_this, struct eelStringSegmentRec *list),
|
||||
EEL_F (*onNamedString)(void *caller_this, const char *name));
|
||||
|
||||
// call with NULL to calculate size, or non-null to generate to buffer (returning size used -- will not null terminate, caller responsibility)
|
||||
int (*nseel_stringsegments_tobuf)(char *bufOut, int bufout_sz, struct eelStringSegmentRec *list);
|
||||
|
||||
void *(*NSEEL_PProc_RAM)(void *data, int data_size, struct _compileContext *ctx);
|
||||
void *(*NSEEL_PProc_THIS)(void *data, int data_size, struct _compileContext *ctx);
|
||||
|
||||
void (*eel_enterfp)(int s[2]);
|
||||
void (*eel_leavefp)(int s[2]);
|
||||
|
||||
|
||||
eel_function_table g_eel_function_table;
|
||||
#define NSEEL_ADDFUNC_DESTINATION (&g_eel_function_table)
|
||||
|
||||
//
|
||||
// adds a function that returns a value (EEL_F)
|
||||
#define NSEEL_addfunc_retval(name,np,pproc,fptr) \
|
||||
NSEEL_addfunc_ret_type(name,np,1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)
|
||||
|
||||
// adds a function that returns a pointer (EEL_F*)
|
||||
#define NSEEL_addfunc_retptr(name,np,pproc,fptr) \
|
||||
NSEEL_addfunc_ret_type(name,np,0,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)
|
||||
|
||||
// adds a void or bool function
|
||||
#define NSEEL_addfunc_retbool(name,np,pproc,fptr) \
|
||||
NSEEL_addfunc_ret_type(name,np,-1,pproc,(void *)(fptr),NSEEL_ADDFUNC_DESTINATION)
|
||||
|
||||
// adds a function that takes min_np or more parameters (func sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms)
|
||||
#define NSEEL_addfunc_varparm(name, min_np, pproc, fptr) \
|
||||
NSEEL_addfunc_varparm_ex(name,min_np,0,pproc,fptr,NSEEL_ADDFUNC_DESTINATION)
|
||||
|
||||
// adds a function that takes np parameters via func: sig needs to be EEL_F func(void *ctx, INT_PTR np, EEL_F **parms)
|
||||
#define NSEEL_addfunc_exparms(name, np, pproc, fptr) \
|
||||
NSEEL_addfunc_varparm_ex(name,np,1,pproc,fptr,NSEEL_ADDFUNC_DESTINATION)
|
||||
|
||||
class eel_string_context_state;
|
||||
|
||||
#define __NS_EELINT_H__
|
||||
|
||||
#define EEL_IMPORT_ALL(IMPORT_FUNC) \
|
||||
IMPORT_FUNC(NSEEL_addfunc_ret_type) \
|
||||
IMPORT_FUNC(NSEEL_addfunc_varparm_ex) \
|
||||
IMPORT_FUNC(NSEEL_VM_free) \
|
||||
IMPORT_FUNC(NSEEL_VM_SetFunctionTable) \
|
||||
IMPORT_FUNC(NSEEL_VM_regvar) \
|
||||
IMPORT_FUNC(NSEEL_VM_SetCustomFuncThis) \
|
||||
IMPORT_FUNC(NSEEL_code_compile_ex) \
|
||||
IMPORT_FUNC(NSEEL_code_getcodeerror) \
|
||||
IMPORT_FUNC(NSEEL_code_execute) \
|
||||
IMPORT_FUNC(NSEEL_code_free) \
|
||||
IMPORT_FUNC(NSEEL_PProc_THIS) \
|
||||
IMPORT_FUNC(NSEEL_PProc_RAM) \
|
||||
IMPORT_FUNC(NSEEL_VM_SetStringFunc) \
|
||||
IMPORT_FUNC(NSEEL_VM_enumallvars) \
|
||||
IMPORT_FUNC(NSEEL_VM_getramptr) \
|
||||
IMPORT_FUNC(NSEEL_VM_SetGRAM) \
|
||||
IMPORT_FUNC(eel_gmem_attach) \
|
||||
IMPORT_FUNC(eel_fft_register) \
|
||||
IMPORT_FUNC(nseel_stringsegments_tobuf) \
|
||||
IMPORT_FUNC(nseel_int_register_var) \
|
||||
IMPORT_FUNC(eel_leavefp) \
|
||||
IMPORT_FUNC(eel_enterfp) \
|
||||
IMPORT_FUNC(NSEEL_VM_set_var_resolver) \
|
||||
IMPORT_FUNC(NSEEL_VM_alloc) /* keep NSEEL_VM_alloc last */
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************************
|
||||
* END of imported EEL
|
||||
******************************************************************************************/
|
||||
+3228
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,782 @@
|
||||
#ifndef _EEL_MDCT_H_
|
||||
#define _EEL_MDCT_H_
|
||||
|
||||
|
||||
#include "ns-eel-int.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#define EEL_DCT_MINBITLEN 5
|
||||
#define EEL_DCT_MAXBITLEN 12
|
||||
|
||||
|
||||
#define PI 3.1415926535897932384626433832795
|
||||
|
||||
typedef struct {
|
||||
int n;
|
||||
int log2n;
|
||||
EEL_F *trig;
|
||||
int *bitrev;
|
||||
EEL_F scale;
|
||||
EEL_F *window;
|
||||
} mdct_lookup;
|
||||
|
||||
static void mdct(EEL_F *in, EEL_F *out, int len)
|
||||
{
|
||||
int k;
|
||||
EEL_F pioverlen = PI * 0.5 / (EEL_F)len;
|
||||
for (k = 0; k < len / 2; k ++)
|
||||
{
|
||||
int i;
|
||||
EEL_F d = 0.0;
|
||||
for (i = 0; i < len; i ++)
|
||||
{
|
||||
d += in[i] * cos(pioverlen * (2.0 * i + 1.0 + len * 0.5) * (2.0 * k + 1.0));
|
||||
}
|
||||
out[k] = (EEL_F)d;
|
||||
}
|
||||
}
|
||||
|
||||
static void imdct(EEL_F *in, EEL_F *out, int len)
|
||||
{
|
||||
int k;
|
||||
EEL_F fourovern = 4.0 / (EEL_F)len;
|
||||
EEL_F pioverlen = PI * 0.5 / (EEL_F)len;
|
||||
for (k = 0; k < len; k ++)
|
||||
{
|
||||
int i;
|
||||
EEL_F d = 0.0;
|
||||
for (i = 0; i < len / 2; i ++)
|
||||
{
|
||||
d += in[i] * cos(pioverlen * (2.0 * k + 1.0 + len * 0.5) * (2 * i + 1.0));
|
||||
}
|
||||
out[k] = (EEL_F)(d * fourovern);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MDCT/iMDCT borrowed from Vorbis, thanks xiph!
|
||||
|
||||
|
||||
#define cPI3_8 .38268343236508977175
|
||||
#define cPI2_8 .70710678118654752441
|
||||
#define cPI1_8 .92387953251128675613
|
||||
|
||||
#define FLOAT_CONV(x) ((EEL_F) ( x ))
|
||||
#define MULT_NORM(x) (x)
|
||||
#define HALVE(x) ((x)*.5f)
|
||||
|
||||
|
||||
|
||||
/* 8 point butterfly (in place, 4 register) */
|
||||
static void mdct_butterfly_8(EEL_F *x) {
|
||||
EEL_F r0 = x[6] + x[2];
|
||||
EEL_F r1 = x[6] - x[2];
|
||||
EEL_F r2 = x[4] + x[0];
|
||||
EEL_F r3 = x[4] - x[0];
|
||||
|
||||
x[6] = r0 + r2;
|
||||
x[4] = r0 - r2;
|
||||
|
||||
r0 = x[5] - x[1];
|
||||
r2 = x[7] - x[3];
|
||||
x[0] = r1 + r0;
|
||||
x[2] = r1 - r0;
|
||||
|
||||
r0 = x[5] + x[1];
|
||||
r1 = x[7] + x[3];
|
||||
x[3] = r2 + r3;
|
||||
x[1] = r2 - r3;
|
||||
x[7] = r1 + r0;
|
||||
x[5] = r1 - r0;
|
||||
|
||||
}
|
||||
|
||||
/* 16 point butterfly (in place, 4 register) */
|
||||
static void mdct_butterfly_16(EEL_F *x) {
|
||||
EEL_F r0 = x[1] - x[9];
|
||||
EEL_F r1 = x[0] - x[8];
|
||||
|
||||
x[8] += x[0];
|
||||
x[9] += x[1];
|
||||
x[0] = MULT_NORM((r0 + r1) * cPI2_8);
|
||||
x[1] = MULT_NORM((r0 - r1) * cPI2_8);
|
||||
|
||||
r0 = x[3] - x[11];
|
||||
r1 = x[10] - x[2];
|
||||
x[10] += x[2];
|
||||
x[11] += x[3];
|
||||
x[2] = r0;
|
||||
x[3] = r1;
|
||||
|
||||
r0 = x[12] - x[4];
|
||||
r1 = x[13] - x[5];
|
||||
x[12] += x[4];
|
||||
x[13] += x[5];
|
||||
x[4] = MULT_NORM((r0 - r1) * cPI2_8);
|
||||
x[5] = MULT_NORM((r0 + r1) * cPI2_8);
|
||||
|
||||
r0 = x[14] - x[6];
|
||||
r1 = x[15] - x[7];
|
||||
x[14] += x[6];
|
||||
x[15] += x[7];
|
||||
x[6] = r0;
|
||||
x[7] = r1;
|
||||
|
||||
mdct_butterfly_8(x);
|
||||
mdct_butterfly_8(x + 8);
|
||||
}
|
||||
|
||||
/* 32 point butterfly (in place, 4 register) */
|
||||
static void mdct_butterfly_32(EEL_F *x) {
|
||||
EEL_F r0 = x[30] - x[14];
|
||||
EEL_F r1 = x[31] - x[15];
|
||||
|
||||
x[30] += x[14];
|
||||
x[31] += x[15];
|
||||
x[14] = r0;
|
||||
x[15] = r1;
|
||||
|
||||
r0 = x[28] - x[12];
|
||||
r1 = x[29] - x[13];
|
||||
x[28] += x[12];
|
||||
x[29] += x[13];
|
||||
x[12] = MULT_NORM( r0 * cPI1_8 - r1 * cPI3_8 );
|
||||
x[13] = MULT_NORM( r0 * cPI3_8 + r1 * cPI1_8 );
|
||||
|
||||
r0 = x[26] - x[10];
|
||||
r1 = x[27] - x[11];
|
||||
x[26] += x[10];
|
||||
x[27] += x[11];
|
||||
x[10] = MULT_NORM(( r0 - r1 ) * cPI2_8);
|
||||
x[11] = MULT_NORM(( r0 + r1 ) * cPI2_8);
|
||||
|
||||
r0 = x[24] - x[8];
|
||||
r1 = x[25] - x[9];
|
||||
x[24] += x[8];
|
||||
x[25] += x[9];
|
||||
x[8] = MULT_NORM( r0 * cPI3_8 - r1 * cPI1_8 );
|
||||
x[9] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 );
|
||||
|
||||
r0 = x[22] - x[6];
|
||||
r1 = x[7] - x[23];
|
||||
x[22] += x[6];
|
||||
x[23] += x[7];
|
||||
x[6] = r1;
|
||||
x[7] = r0;
|
||||
|
||||
r0 = x[4] - x[20];
|
||||
r1 = x[5] - x[21];
|
||||
x[20] += x[4];
|
||||
x[21] += x[5];
|
||||
x[4] = MULT_NORM( r1 * cPI1_8 + r0 * cPI3_8 );
|
||||
x[5] = MULT_NORM( r1 * cPI3_8 - r0 * cPI1_8 );
|
||||
|
||||
r0 = x[2] - x[18];
|
||||
r1 = x[3] - x[19];
|
||||
x[18] += x[2];
|
||||
x[19] += x[3];
|
||||
x[2] = MULT_NORM(( r1 + r0 ) * cPI2_8);
|
||||
x[3] = MULT_NORM(( r1 - r0 ) * cPI2_8);
|
||||
|
||||
r0 = x[0] - x[16];
|
||||
r1 = x[1] - x[17];
|
||||
x[16] += x[0];
|
||||
x[17] += x[1];
|
||||
x[0] = MULT_NORM( r1 * cPI3_8 + r0 * cPI1_8 );
|
||||
x[1] = MULT_NORM( r1 * cPI1_8 - r0 * cPI3_8 );
|
||||
|
||||
mdct_butterfly_16(x);
|
||||
mdct_butterfly_16(x + 16);
|
||||
|
||||
}
|
||||
|
||||
/* N point first stage butterfly (in place, 2 register) */
|
||||
static void mdct_butterfly_first(EEL_F *T,
|
||||
EEL_F *x,
|
||||
int points) {
|
||||
|
||||
EEL_F *x1 = x + points - 8;
|
||||
EEL_F *x2 = x + (points >> 1) - 8;
|
||||
EEL_F r0;
|
||||
EEL_F r1;
|
||||
|
||||
do {
|
||||
|
||||
r0 = x1[6] - x2[6];
|
||||
r1 = x1[7] - x2[7];
|
||||
x1[6] += x2[6];
|
||||
x1[7] += x2[7];
|
||||
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
|
||||
r0 = x1[4] - x2[4];
|
||||
r1 = x1[5] - x2[5];
|
||||
x1[4] += x2[4];
|
||||
x1[5] += x2[5];
|
||||
x2[4] = MULT_NORM(r1 * T[5] + r0 * T[4]);
|
||||
x2[5] = MULT_NORM(r1 * T[4] - r0 * T[5]);
|
||||
|
||||
r0 = x1[2] - x2[2];
|
||||
r1 = x1[3] - x2[3];
|
||||
x1[2] += x2[2];
|
||||
x1[3] += x2[3];
|
||||
x2[2] = MULT_NORM(r1 * T[9] + r0 * T[8]);
|
||||
x2[3] = MULT_NORM(r1 * T[8] - r0 * T[9]);
|
||||
|
||||
r0 = x1[0] - x2[0];
|
||||
r1 = x1[1] - x2[1];
|
||||
x1[0] += x2[0];
|
||||
x1[1] += x2[1];
|
||||
x2[0] = MULT_NORM(r1 * T[13] + r0 * T[12]);
|
||||
x2[1] = MULT_NORM(r1 * T[12] - r0 * T[13]);
|
||||
|
||||
x1 -= 8;
|
||||
x2 -= 8;
|
||||
T += 16;
|
||||
|
||||
} while(x2 >= x);
|
||||
}
|
||||
|
||||
/* N/stage point generic N stage butterfly (in place, 2 register) */
|
||||
static void mdct_butterfly_generic(EEL_F *T,
|
||||
EEL_F *x,
|
||||
int points,
|
||||
int trigint) {
|
||||
|
||||
EEL_F *x1 = x + points - 8;
|
||||
EEL_F *x2 = x + (points >> 1) - 8;
|
||||
EEL_F r0;
|
||||
EEL_F r1;
|
||||
|
||||
do {
|
||||
|
||||
r0 = x1[6] - x2[6];
|
||||
r1 = x1[7] - x2[7];
|
||||
x1[6] += x2[6];
|
||||
x1[7] += x2[7];
|
||||
x2[6] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
x2[7] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
|
||||
T += trigint;
|
||||
|
||||
r0 = x1[4] - x2[4];
|
||||
r1 = x1[5] - x2[5];
|
||||
x1[4] += x2[4];
|
||||
x1[5] += x2[5];
|
||||
x2[4] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
x2[5] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
|
||||
T += trigint;
|
||||
|
||||
r0 = x1[2] - x2[2];
|
||||
r1 = x1[3] - x2[3];
|
||||
x1[2] += x2[2];
|
||||
x1[3] += x2[3];
|
||||
x2[2] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
x2[3] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
|
||||
T += trigint;
|
||||
|
||||
r0 = x1[0] - x2[0];
|
||||
r1 = x1[1] - x2[1];
|
||||
x1[0] += x2[0];
|
||||
x1[1] += x2[1];
|
||||
x2[0] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
x2[1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
|
||||
T += trigint;
|
||||
x1 -= 8;
|
||||
x2 -= 8;
|
||||
|
||||
} while(x2 >= x);
|
||||
}
|
||||
|
||||
static void mdct_butterflies(mdct_lookup *init,
|
||||
EEL_F *x,
|
||||
int points) {
|
||||
|
||||
EEL_F *T = init->trig;
|
||||
int stages = init->log2n - 5;
|
||||
int i, j;
|
||||
|
||||
if(--stages > 0) {
|
||||
mdct_butterfly_first(T, x, points);
|
||||
}
|
||||
|
||||
for(i = 1; --stages > 0; i++) {
|
||||
for(j = 0; j < (1 << i); j++)
|
||||
mdct_butterfly_generic(T, x + (points >> i)*j, points >> i, 4 << i);
|
||||
}
|
||||
|
||||
for(j = 0; j < points; j += 32)
|
||||
mdct_butterfly_32(x + j);
|
||||
|
||||
}
|
||||
|
||||
static void mdct_bitreverse(mdct_lookup *init,
|
||||
EEL_F *x) {
|
||||
int n = init->n;
|
||||
int *bit = init->bitrev;
|
||||
EEL_F *w0 = x;
|
||||
EEL_F *w1 = x = w0 + (n >> 1);
|
||||
EEL_F *T = init->trig + n;
|
||||
|
||||
do {
|
||||
EEL_F *x0 = x + bit[0];
|
||||
EEL_F *x1 = x + bit[1];
|
||||
|
||||
EEL_F r0 = x0[1] - x1[1];
|
||||
EEL_F r1 = x0[0] + x1[0];
|
||||
EEL_F r2 = MULT_NORM(r1 * T[0] + r0 * T[1]);
|
||||
EEL_F r3 = MULT_NORM(r1 * T[1] - r0 * T[0]);
|
||||
|
||||
w1 -= 4;
|
||||
|
||||
r0 = HALVE(x0[1] + x1[1]);
|
||||
r1 = HALVE(x0[0] - x1[0]);
|
||||
|
||||
w0[0] = r0 + r2;
|
||||
w1[2] = r0 - r2;
|
||||
w0[1] = r1 + r3;
|
||||
w1[3] = r3 - r1;
|
||||
|
||||
x0 = x + bit[2];
|
||||
x1 = x + bit[3];
|
||||
|
||||
r0 = x0[1] - x1[1];
|
||||
r1 = x0[0] + x1[0];
|
||||
r2 = MULT_NORM(r1 * T[2] + r0 * T[3]);
|
||||
r3 = MULT_NORM(r1 * T[3] - r0 * T[2]);
|
||||
|
||||
r0 = HALVE(x0[1] + x1[1]);
|
||||
r1 = HALVE(x0[0] - x1[0]);
|
||||
|
||||
w0[2] = r0 + r2;
|
||||
w1[0] = r0 - r2;
|
||||
w0[3] = r1 + r3;
|
||||
w1[1] = r3 - r1;
|
||||
|
||||
T += 4;
|
||||
bit += 4;
|
||||
w0 += 4;
|
||||
|
||||
} while(w0 < w1);
|
||||
}
|
||||
|
||||
static void megabuf_mdct_apply_window(void *init, EEL_F *inbuf, EEL_F *outbuf)
|
||||
{
|
||||
mdct_lookup *p = (mdct_lookup *)init;
|
||||
EEL_F *w;
|
||||
int cnt;
|
||||
if (!p) return;
|
||||
|
||||
w = p->window;
|
||||
if (!w) return;
|
||||
|
||||
cnt = p->n / 2;
|
||||
while (cnt--) *outbuf++ = *inbuf++ * *w++;
|
||||
cnt = p->n / 2;
|
||||
while (cnt--) *outbuf++ = *inbuf++ * *--w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void *megabuf_mdct_init(int n) {
|
||||
mdct_lookup *lookup = (mdct_lookup *)calloc(sizeof(mdct_lookup), 1);
|
||||
int i;
|
||||
EEL_F c = (PI / (EEL_F) n);
|
||||
int *bitrev;
|
||||
EEL_F *T;
|
||||
int n2, log2n;
|
||||
if (!lookup) return 0;
|
||||
|
||||
lookup->n = n;
|
||||
lookup->window = (EEL_F *)calloc(sizeof(EEL_F), n / 2);
|
||||
if (!lookup->window) return lookup;
|
||||
|
||||
for (i = 0; i < n / 2; i ++)
|
||||
{
|
||||
lookup->window[i] = sin(c * (i + 0.5));
|
||||
}
|
||||
|
||||
if (n <= 32) return lookup;
|
||||
bitrev = (int*)calloc(sizeof(int), (n / 4));
|
||||
lookup->bitrev = bitrev;
|
||||
if (!bitrev) return lookup;
|
||||
|
||||
T = (EEL_F*)calloc(sizeof(EEL_F), (n + n / 4));
|
||||
lookup->trig = T;
|
||||
if (!T) return lookup;
|
||||
|
||||
n2 = n >> 1;
|
||||
log2n = lookup->log2n = (int)(log((double)n) / log(2.0) + 0.5);
|
||||
|
||||
/* trig lookups... */
|
||||
|
||||
for(i = 0; i < n / 4; i++) {
|
||||
T[i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i)));
|
||||
T[i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i)));
|
||||
T[n2 + i * 2] = FLOAT_CONV(cos((PI / (2 * n)) * (2 * i + 1)));
|
||||
T[n2 + i * 2 + 1] = FLOAT_CONV(sin((PI / (2 * n)) * (2 * i + 1)));
|
||||
}
|
||||
for(i = 0; i < n / 8; i++) {
|
||||
T[n + i * 2] = FLOAT_CONV(cos((PI / n) * (4 * i + 2)) * .5);
|
||||
T[n + i * 2 + 1] = FLOAT_CONV(-sin((PI / n) * (4 * i + 2)) * .5);
|
||||
}
|
||||
|
||||
/* bitreverse lookup... */
|
||||
|
||||
{
|
||||
int mask = (1 << (log2n - 1)) - 1, j;
|
||||
int msb = 1 << (log2n - 2);
|
||||
for(i = 0; i < n / 8; i++) {
|
||||
int acc = 0;
|
||||
for(j = 0; msb >> j; j++)
|
||||
if((msb >> j)&i)acc |= 1 << j;
|
||||
bitrev[i * 2] = ((~acc)&mask) - 1;
|
||||
bitrev[i * 2 + 1] = acc;
|
||||
|
||||
}
|
||||
}
|
||||
lookup->scale = FLOAT_CONV(4.f / n);
|
||||
return lookup;
|
||||
}
|
||||
|
||||
static void megabuf_mdct_backward(void *init, EEL_F *in, EEL_F *out) {
|
||||
mdct_lookup *lookup = (mdct_lookup *)init;
|
||||
int n, n2, n4;
|
||||
EEL_F *iX, *oX, *T;
|
||||
if (!lookup) return;
|
||||
n = lookup->n;
|
||||
if (n <= 32 || !lookup->bitrev || !lookup->trig)
|
||||
{
|
||||
imdct(in, out, n);
|
||||
return;
|
||||
}
|
||||
n2 = n >> 1;
|
||||
n4 = n >> 2;
|
||||
|
||||
/* rotate */
|
||||
|
||||
iX = in + n2 - 7;
|
||||
oX = out + n2 + n4;
|
||||
T = lookup->trig + n4;
|
||||
|
||||
do {
|
||||
oX -= 4;
|
||||
oX[0] = MULT_NORM(-iX[2] * T[3] - iX[0] * T[2]);
|
||||
oX[1] = MULT_NORM (iX[0] * T[3] - iX[2] * T[2]);
|
||||
oX[2] = MULT_NORM(-iX[6] * T[1] - iX[4] * T[0]);
|
||||
oX[3] = MULT_NORM (iX[4] * T[1] - iX[6] * T[0]);
|
||||
iX -= 8;
|
||||
T += 4;
|
||||
} while(iX >= in);
|
||||
|
||||
iX = in + n2 - 8;
|
||||
oX = out + n2 + n4;
|
||||
T = lookup->trig + n4;
|
||||
|
||||
do {
|
||||
T -= 4;
|
||||
oX[0] = MULT_NORM (iX[4] * T[3] + iX[6] * T[2]);
|
||||
oX[1] = MULT_NORM (iX[4] * T[2] - iX[6] * T[3]);
|
||||
oX[2] = MULT_NORM (iX[0] * T[1] + iX[2] * T[0]);
|
||||
oX[3] = MULT_NORM (iX[0] * T[0] - iX[2] * T[1]);
|
||||
iX -= 8;
|
||||
oX += 4;
|
||||
} while(iX >= in);
|
||||
|
||||
mdct_butterflies(lookup, out + n2, n2);
|
||||
mdct_bitreverse(lookup, out);
|
||||
|
||||
/* roatate + window */
|
||||
|
||||
{
|
||||
EEL_F *oX1 = out + n2 + n4;
|
||||
EEL_F *oX2 = out + n2 + n4;
|
||||
iX = out;
|
||||
T = lookup->trig + n2;
|
||||
|
||||
do {
|
||||
oX1 -= 4;
|
||||
|
||||
oX1[3] = MULT_NORM (iX[0] * T[1] - iX[1] * T[0]);
|
||||
oX2[0] = -MULT_NORM (iX[0] * T[0] + iX[1] * T[1]);
|
||||
|
||||
oX1[2] = MULT_NORM (iX[2] * T[3] - iX[3] * T[2]);
|
||||
oX2[1] = -MULT_NORM (iX[2] * T[2] + iX[3] * T[3]);
|
||||
|
||||
oX1[1] = MULT_NORM (iX[4] * T[5] - iX[5] * T[4]);
|
||||
oX2[2] = -MULT_NORM (iX[4] * T[4] + iX[5] * T[5]);
|
||||
|
||||
oX1[0] = MULT_NORM (iX[6] * T[7] - iX[7] * T[6]);
|
||||
oX2[3] = -MULT_NORM (iX[6] * T[6] + iX[7] * T[7]);
|
||||
|
||||
oX2 += 4;
|
||||
iX += 8;
|
||||
T += 8;
|
||||
} while(iX < oX1);
|
||||
|
||||
iX = out + n2 + n4;
|
||||
oX1 = out + n4;
|
||||
oX2 = oX1;
|
||||
|
||||
do {
|
||||
oX1 -= 4;
|
||||
iX -= 4;
|
||||
|
||||
oX2[0] = -(oX1[3] = iX[3]);
|
||||
oX2[1] = -(oX1[2] = iX[2]);
|
||||
oX2[2] = -(oX1[1] = iX[1]);
|
||||
oX2[3] = -(oX1[0] = iX[0]);
|
||||
|
||||
oX2 += 4;
|
||||
} while(oX2 < iX);
|
||||
|
||||
iX = out + n2 + n4;
|
||||
oX1 = out + n2 + n4;
|
||||
oX2 = out + n2;
|
||||
do {
|
||||
oX1 -= 4;
|
||||
oX1[0] = iX[3];
|
||||
oX1[1] = iX[2];
|
||||
oX1[2] = iX[1];
|
||||
oX1[3] = iX[0];
|
||||
iX += 4;
|
||||
} while(oX1 > oX2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void megabuf_mdct_forward(void *init, EEL_F *in, EEL_F *out) {
|
||||
mdct_lookup *lookup = (mdct_lookup *)init;
|
||||
int n, n2, n4, n8;
|
||||
EEL_F *w, *w2;
|
||||
if (!lookup) return;
|
||||
|
||||
n = lookup->n;
|
||||
if (n <= 32 || !lookup->bitrev || !lookup->trig)
|
||||
{
|
||||
mdct(in, out, n);
|
||||
return;
|
||||
}
|
||||
n2 = n >> 1;
|
||||
n4 = n >> 2;
|
||||
n8 = n >> 3;
|
||||
EEL_F oldw[1<<EEL_DCT_MAXBITLEN];
|
||||
w = oldw;
|
||||
w2 = w + n2;
|
||||
|
||||
/* rotate */
|
||||
|
||||
/* window + rotate + step 1 */
|
||||
|
||||
{
|
||||
EEL_F r0;
|
||||
EEL_F r1;
|
||||
EEL_F *x0 = in + n2 + n4;
|
||||
EEL_F *x1 = x0 + 1;
|
||||
EEL_F *T = lookup->trig + n2;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for(i = 0; i < n8; i += 2) {
|
||||
x0 -= 4;
|
||||
T -= 2;
|
||||
r0 = x0[2] + x1[0];
|
||||
r1 = x0[0] + x1[2];
|
||||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
x1 += 4;
|
||||
}
|
||||
|
||||
x1 = in + 1;
|
||||
|
||||
for(; i < n2 - n8; i += 2) {
|
||||
T -= 2;
|
||||
x0 -= 4;
|
||||
r0 = x0[2] - x1[0];
|
||||
r1 = x0[0] - x1[2];
|
||||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
x1 += 4;
|
||||
}
|
||||
|
||||
x0 = in + n;
|
||||
|
||||
for(; i < n2; i += 2) {
|
||||
T -= 2;
|
||||
x0 -= 4;
|
||||
r0 = -x0[2] - x1[0];
|
||||
r1 = -x0[0] - x1[2];
|
||||
w2[i] = MULT_NORM(r1 * T[1] + r0 * T[0]);
|
||||
w2[i + 1] = MULT_NORM(r1 * T[0] - r0 * T[1]);
|
||||
x1 += 4;
|
||||
}
|
||||
|
||||
|
||||
mdct_butterflies(lookup, w + n2, n2);
|
||||
mdct_bitreverse(lookup, w);
|
||||
|
||||
/* roatate + window */
|
||||
|
||||
T = lookup->trig + n2;
|
||||
x0 = out + n2;
|
||||
|
||||
for(i = 0; i < n4; i++) {
|
||||
x0--;
|
||||
out[i] = MULT_NORM((w[0] * T[0] + w[1] * T[1]) * lookup->scale);
|
||||
x0[0] = MULT_NORM((w[0] * T[1] - w[1] * T[0]) * lookup->scale);
|
||||
w += 2;
|
||||
T += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
static void dct(EEL_F *in, EEL_F *out, int len)
|
||||
{
|
||||
int k;
|
||||
EEL_F wk = sqrt(2.0 / len);
|
||||
EEL_F overtwolen = 0.5 / (EEL_F)len;
|
||||
for (k = 0; k < len; k ++)
|
||||
{
|
||||
int n;
|
||||
EEL_F d = 0.0;
|
||||
for (n = 0; n < len; n ++)
|
||||
{
|
||||
int an = n + 1;
|
||||
d += in[n] * cos(PI * (2.0 * n + 1.0) * (EEL_F)k * overtwolen);
|
||||
}
|
||||
if (!k) d /= sqrt(len);
|
||||
else d *= wk;
|
||||
out[k] = (EEL_F)d;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void idct(EEL_F *in, EEL_F *out, int len)
|
||||
{
|
||||
int n;
|
||||
EEL_F dd0 = 1.0 / sqrt(len);
|
||||
EEL_F dd1 = sqrt(2.0 / len);
|
||||
EEL_F overtwolen = 0.5 / len;
|
||||
for (n = 0; n < len; n ++)
|
||||
{
|
||||
int k;
|
||||
EEL_F d = 0.0;
|
||||
for (k = 0; k < len; k ++)
|
||||
{
|
||||
EEL_F dd;
|
||||
if (!k) dd = dd0 * in[k];
|
||||
else dd = dd1 * in[k];
|
||||
d += dd * cos(PI * (2.0 * n + 1.0) * k * overtwolen);
|
||||
}
|
||||
out[n] = (EEL_F)d;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
// 0 is megabuf blocks
|
||||
// 1 is need_free flag
|
||||
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL mdct_func(int dir, EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
int l = (int)(*length + 0.0001);
|
||||
int offs = (int)(*start + 0.0001);
|
||||
int bitl = 0;
|
||||
int ilen;
|
||||
int bidx;
|
||||
EEL_F *ptr;
|
||||
while (l > 1 && bitl < EEL_DCT_MAXBITLEN)
|
||||
{
|
||||
bitl++;
|
||||
l >>= 1;
|
||||
}
|
||||
if (bitl < EEL_DCT_MINBITLEN)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
ilen = 1 << bitl;
|
||||
|
||||
bidx = bitl - EEL_DCT_MINBITLEN;
|
||||
|
||||
|
||||
// check to make sure we don't cross a boundary
|
||||
if (offs / NSEEL_RAM_ITEMSPERBLOCK != (offs + ilen * 2 - 1) / NSEEL_RAM_ITEMSPERBLOCK)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
|
||||
ptr = __NSEEL_RAMAlloc(blocks, offs);
|
||||
if (!ptr || ptr == &nseel_ramalloc_onfail)
|
||||
{
|
||||
return start;
|
||||
}
|
||||
|
||||
if (ilen > 1)
|
||||
{
|
||||
static void *mdct_ctxs[1 + EEL_DCT_MAXBITLEN - EEL_DCT_MINBITLEN];
|
||||
|
||||
if (!mdct_ctxs[bidx])
|
||||
{
|
||||
NSEEL_HOSTSTUB_EnterMutex();
|
||||
if (!mdct_ctxs[bidx])
|
||||
mdct_ctxs[bidx] = megabuf_mdct_init(ilen);
|
||||
NSEEL_HOSTSTUB_LeaveMutex();
|
||||
}
|
||||
|
||||
if (mdct_ctxs[bidx])
|
||||
{
|
||||
EEL_F buf[1 << EEL_DCT_MAXBITLEN];
|
||||
if (dir < 0)
|
||||
{
|
||||
megabuf_mdct_backward(mdct_ctxs[bidx], ptr, buf);
|
||||
megabuf_mdct_apply_window(mdct_ctxs[bidx], buf, ptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
megabuf_mdct_apply_window(mdct_ctxs[bidx], ptr, buf);
|
||||
megabuf_mdct_forward(mdct_ctxs[bidx], buf, ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL megabuf_mdct(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return mdct_func(0, blocks, start, length);
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL megabuf_imdct(EEL_F **blocks, EEL_F *start, EEL_F *length)
|
||||
{
|
||||
return mdct_func(-1, blocks, start, length);
|
||||
}
|
||||
|
||||
void EEL_mdct_register()
|
||||
{
|
||||
NSEEL_addfunc_retptr("mdct", 2, NSEEL_PProc_RAM, &megabuf_mdct);
|
||||
NSEEL_addfunc_retptr("imdct", 2, NSEEL_PProc_RAM, &megabuf_imdct);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
static const char *eel_mdct_function_reference =
|
||||
"mdct\tbuffer,length\tPerforms a windowed modified DCT, taking length inputs and producing length/2 outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0"
|
||||
"imdct\tbuffer,length\tPerforms a windowed inverse modified DCT, taking length/2 inputs and producing length outputs. buffer must not cross a 65,536 item boundary, and length must be 64, 128, 256, 512, 2048 or 4096.\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
#ifndef _EEL_MISC_H_
|
||||
#define _EEL_MISC_H_
|
||||
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <time.h>
|
||||
#include "../time_precise.h"
|
||||
// some generic EEL functions for things like time
|
||||
|
||||
#ifndef EEL_MISC_NO_SLEEP
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_sleep(void *opaque, EEL_F *amt)
|
||||
{
|
||||
if (*amt >= 0.0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (*amt > 30000000.0) Sleep(30000000);
|
||||
else Sleep((DWORD)(*amt+0.5));
|
||||
#else
|
||||
if (*amt > 30000000.0) usleep(((useconds_t)30000000)*1000);
|
||||
else usleep((useconds_t)(*amt*1000.0+0.5));
|
||||
#endif
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL _eel_time(void *opaque, EEL_F *v)
|
||||
{
|
||||
*v = (EEL_F) time(NULL);
|
||||
return v;
|
||||
}
|
||||
|
||||
static EEL_F * NSEEL_CGEN_CALL _eel_time_precise(void *opaque, EEL_F *v)
|
||||
{
|
||||
*v = time_precise();
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
void EEL_misc_register()
|
||||
{
|
||||
#ifndef EEL_MISC_NO_SLEEP
|
||||
NSEEL_addfunc_retval("sleep",1,NSEEL_PProc_THIS,&_eel_sleep);
|
||||
#endif
|
||||
NSEEL_addfunc_retptr("time",1,NSEEL_PProc_THIS,&_eel_time);
|
||||
NSEEL_addfunc_retptr("time_precise",1,NSEEL_PProc_THIS,&_eel_time_precise);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
static const char *eel_misc_function_reference =
|
||||
#ifndef EEL_MISC_NO_SLEEP
|
||||
"sleep\tms\tYields the CPU for the millisecond count specified, calling Sleep() on Windows or usleep() on other platforms.\0"
|
||||
#endif
|
||||
"time\t[&val]\tSets the parameter (or a temporary buffer if omitted) to the number of seconds since January 1, 1970, and returns a reference to that value. "
|
||||
"The granularity of the value returned is 1 second.\0"
|
||||
"time_precise\t[&val]\tSets the parameter (or a temporary buffer if omitted) to a system-local timestamp in seconds, and returns a reference to that value. "
|
||||
"The granularity of the value returned is system defined (but generally significantly smaller than one second).\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,564 @@
|
||||
#ifndef _EEL_NET_H_
|
||||
#define _EEL_NET_H_
|
||||
|
||||
// x = tcp_listen(port[,interface, connected_ip_out]) poll this, returns connection id > 0, or <0 on error, or 0 if no new connect -- interface only valid on first call (or after tcp_listen_end(port))
|
||||
// tcp_listen_end(port);
|
||||
|
||||
// connection = tcp_connect(host, port[, block]) // connection id > 0 on ok
|
||||
// tcp_set_block(connection, block?)
|
||||
// tcp_close(connection)
|
||||
|
||||
// tcp_send(connection, string[, length]) // can return 0 if block, -1 if error, otherwise returns length sent
|
||||
// tcp_recv(connection, string[, maxlength]) // 0 on nothing, -1 on error, otherwise returns length recv'd
|
||||
|
||||
|
||||
// need:
|
||||
// #define EEL_NET_GET_CONTEXT(opaque) (((sInst *)opaque)->m_net_state)
|
||||
|
||||
// you must pass a JNL_AsyncDNS object to eel_net_state to support nonblocking connect with DNS resolution, otherwise DNS will block
|
||||
// #define EEL_NET_NO_SYNC_DNS -- never ever call gethostbyname() synchronously, may disable DNS for blocking connect, or if a JNL_IAsyncDNS is not provided.
|
||||
|
||||
#ifndef EEL_NET_MAXSEND
|
||||
#define EEL_NET_MAXSEND (EEL_STRING_MAXUSERSTRING_LENGTH_HINT+4096)
|
||||
#endif
|
||||
|
||||
|
||||
#include "../jnetlib/netinc.h"
|
||||
#define JNL_NO_IMPLEMENTATION
|
||||
#include "../jnetlib/asyncdns.h"
|
||||
|
||||
class eel_net_state
|
||||
{
|
||||
public:
|
||||
enum { STATE_FREE=0, STATE_RESOLVING, STATE_CONNECTED, STATE_ERR };
|
||||
enum { CONNECTION_ID_BASE=0x110000 };
|
||||
|
||||
eel_net_state(int max_con, JNL_IAsyncDNS *dns);
|
||||
~eel_net_state();
|
||||
|
||||
struct connection_state {
|
||||
char *hostname; // set during resolve only
|
||||
SOCKET sock;
|
||||
int state; // STATE_RESOLVING...
|
||||
int port;
|
||||
bool blockmode;
|
||||
};
|
||||
WDL_TypedBuf<connection_state> m_cons;
|
||||
WDL_IntKeyedArray<SOCKET> m_listens;
|
||||
JNL_IAsyncDNS *m_dns;
|
||||
|
||||
EEL_F onConnect(char *hostNameOwned, int port, int block);
|
||||
EEL_F onClose(void *opaque, EEL_F handle);
|
||||
EEL_F set_block(void *opaque, EEL_F handle, bool block);
|
||||
EEL_F onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut);
|
||||
|
||||
int __run_connect(connection_state *cs, unsigned int ip);
|
||||
int __run(connection_state *cs);
|
||||
int do_send(void *opaque, EEL_F h, const char *src, int len);
|
||||
int do_recv(void *opaque, EEL_F h, char *buf, int maxlen);
|
||||
#ifdef _WIN32
|
||||
bool m_had_socketlib_init;
|
||||
#endif
|
||||
};
|
||||
|
||||
eel_net_state::eel_net_state(int max_con, JNL_IAsyncDNS *dns)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_had_socketlib_init=false;
|
||||
#endif
|
||||
|
||||
m_cons.Resize(max_con);
|
||||
int x;
|
||||
for (x=0;x<m_cons.GetSize();x++)
|
||||
{
|
||||
m_cons.Get()[x].state = STATE_FREE;
|
||||
m_cons.Get()[x].sock = INVALID_SOCKET;
|
||||
m_cons.Get()[x].hostname = NULL;
|
||||
}
|
||||
m_dns=dns;
|
||||
}
|
||||
|
||||
eel_net_state::~eel_net_state()
|
||||
{
|
||||
int x;
|
||||
for (x=0;x<m_cons.GetSize();x++)
|
||||
{
|
||||
SOCKET s=m_cons.Get()[x].sock;
|
||||
if (s != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(s,SHUT_RDWR);
|
||||
closesocket(s);
|
||||
}
|
||||
free(m_cons.Get()[x].hostname);
|
||||
}
|
||||
for (x=0;x<m_listens.GetSize();x++)
|
||||
{
|
||||
SOCKET s=m_listens.Enumerate(x);
|
||||
shutdown(s, SHUT_RDWR);
|
||||
closesocket(s);
|
||||
}
|
||||
}
|
||||
EEL_F eel_net_state::onConnect(char *hostNameOwned, int port, int block)
|
||||
{
|
||||
int x;
|
||||
#ifdef _WIN32
|
||||
if (!m_had_socketlib_init)
|
||||
{
|
||||
m_had_socketlib_init=1;
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(1, 1), &wsaData);
|
||||
}
|
||||
#endif
|
||||
for(x=0;x<m_cons.GetSize();x++)
|
||||
{
|
||||
connection_state *s=m_cons.Get()+x;
|
||||
if (s->state == STATE_FREE)
|
||||
{
|
||||
unsigned int ip=inet_addr(hostNameOwned);
|
||||
if (m_dns && ip == INADDR_NONE && !block)
|
||||
{
|
||||
const int r=m_dns->resolve(hostNameOwned,&ip);
|
||||
if (r<0) break; // error!
|
||||
|
||||
if (r>0) ip = INADDR_NONE;
|
||||
}
|
||||
#ifndef EEL_NET_NO_SYNC_DNS
|
||||
else if (ip == INADDR_NONE)
|
||||
{
|
||||
struct hostent *he = gethostbyname(hostNameOwned);
|
||||
if (he) ip = *(int *)he->h_addr;
|
||||
}
|
||||
#endif
|
||||
if (hostNameOwned || ip != INADDR_NONE)
|
||||
{
|
||||
if (ip != INADDR_NONE)
|
||||
{
|
||||
free(hostNameOwned);
|
||||
hostNameOwned=NULL;
|
||||
}
|
||||
|
||||
s->state = STATE_RESOLVING;
|
||||
s->hostname = hostNameOwned;
|
||||
s->blockmode = !!block;
|
||||
s->port = port;
|
||||
if (hostNameOwned || __run_connect(s,ip)) return x + CONNECTION_ID_BASE;
|
||||
|
||||
s->state=STATE_FREE;
|
||||
s->hostname=NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(hostNameOwned);
|
||||
return -1;
|
||||
}
|
||||
|
||||
EEL_F eel_net_state::onListen(void *opaque, EEL_F handle, int mode, EEL_F *ifStr, EEL_F *ipOut)
|
||||
{
|
||||
const int port = (int) handle;
|
||||
if (port < 1 || port > 65535)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_listen(%d): invalid port specified, will never succeed",port);
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (!m_had_socketlib_init)
|
||||
{
|
||||
m_had_socketlib_init=1;
|
||||
WSADATA wsaData;
|
||||
WSAStartup(MAKEWORD(1, 1), &wsaData);
|
||||
}
|
||||
#endif
|
||||
SOCKET *sockptr = m_listens.GetPtr(port);
|
||||
if (mode<0)
|
||||
{
|
||||
if (!sockptr) return -1.0;
|
||||
|
||||
SOCKET ss=*sockptr;
|
||||
m_listens.Delete(port);
|
||||
if (ss != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(ss, SHUT_RDWR);
|
||||
closesocket(ss);
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
if (!sockptr)
|
||||
{
|
||||
struct sockaddr_in sin;
|
||||
memset((char *) &sin, 0,sizeof(sin));
|
||||
if (ifStr)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *fn = EEL_STRING_GET_FOR_INDEX(*ifStr,NULL);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (!fn) EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for second parameter (interface)",port,*ifStr);
|
||||
#endif
|
||||
if (fn && *fn) sin.sin_addr.s_addr=inet_addr(fn);
|
||||
|
||||
}
|
||||
if (!sin.sin_addr.s_addr || sin.sin_addr.s_addr==INADDR_NONE) sin.sin_addr.s_addr = INADDR_ANY;
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_port = htons( (short) port );
|
||||
SOCKET sock = socket(AF_INET,SOCK_STREAM,0);
|
||||
if (sock != INVALID_SOCKET)
|
||||
{
|
||||
SET_SOCK_DEFAULTS(sock);
|
||||
SET_SOCK_BLOCK(sock,0);
|
||||
if (bind(sock,(struct sockaddr *)&sin,sizeof(sin)) || listen(sock,8)==-1)
|
||||
{
|
||||
shutdown(sock, SHUT_RDWR);
|
||||
closesocket(sock);
|
||||
sock=INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
//if (sock == INVALID_SOCKET) EEL_STRING_DEBUGOUT("tcp_listen(%d): failed listening on port",port);
|
||||
// we report -1 to the caller, no need to error message
|
||||
#endif
|
||||
m_listens.Insert(port,sock);
|
||||
sockptr = m_listens.GetPtr(port);
|
||||
}
|
||||
if (!sockptr || *sockptr == INVALID_SOCKET) return -1;
|
||||
|
||||
struct sockaddr_in saddr;
|
||||
socklen_t length = sizeof(struct sockaddr_in);
|
||||
SOCKET newsock = accept(*sockptr, (struct sockaddr *) &saddr, &length);
|
||||
if (newsock == INVALID_SOCKET)
|
||||
{
|
||||
return 0; // nothing to report here
|
||||
}
|
||||
SET_SOCK_DEFAULTS(newsock);
|
||||
|
||||
int x;
|
||||
for(x=0;x<m_cons.GetSize();x++)
|
||||
{
|
||||
connection_state *cs=m_cons.Get()+x;
|
||||
if (cs->state == STATE_FREE)
|
||||
{
|
||||
cs->state=STATE_CONNECTED;
|
||||
free(cs->hostname);
|
||||
cs->hostname=NULL;
|
||||
cs->sock = newsock;
|
||||
cs->blockmode=true;
|
||||
cs->port=0;
|
||||
if (ipOut)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
WDL_FastString *ws=NULL;
|
||||
EEL_STRING_GET_FOR_WRITE(*ipOut,&ws);
|
||||
if (ws)
|
||||
{
|
||||
const unsigned int a = ntohl(saddr.sin_addr.s_addr);
|
||||
ws->SetFormatted(128,"%d.%d.%d.%d",(a>>24)&0xff,(a>>16)&0xff,(a>>8)&0xff,a&0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_listen(%d): bad string identifier %f for third parameter (IP-out)",port,*ipOut);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return x + CONNECTION_ID_BASE;
|
||||
}
|
||||
}
|
||||
shutdown(newsock, SHUT_RDWR);
|
||||
closesocket(newsock);
|
||||
return -1;
|
||||
|
||||
|
||||
}
|
||||
|
||||
int eel_net_state::__run_connect(connection_state *cs, unsigned int ip)
|
||||
{
|
||||
SOCKET s=socket(AF_INET,SOCK_STREAM,0);
|
||||
if (s == INVALID_SOCKET) return 0;
|
||||
SET_SOCK_DEFAULTS(s);
|
||||
|
||||
if (!cs->blockmode) SET_SOCK_BLOCK(s,0);
|
||||
|
||||
struct sockaddr_in sa={0,};
|
||||
sa.sin_family=AF_INET;
|
||||
sa.sin_addr.s_addr = ip;
|
||||
sa.sin_port = htons(cs->port);
|
||||
if (!connect(s,(struct sockaddr *)&sa,16) || (!cs->blockmode && JNL_ERRNO == JNL_EINPROGRESS))
|
||||
{
|
||||
cs->state = STATE_CONNECTED;
|
||||
cs->sock = s;
|
||||
return 1;
|
||||
}
|
||||
shutdown(s, SHUT_RDWR);
|
||||
closesocket(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eel_net_state::__run(connection_state *cs)
|
||||
{
|
||||
if (cs->sock != INVALID_SOCKET) return 0;
|
||||
|
||||
if (!cs->hostname) return -1;
|
||||
|
||||
unsigned int ip=INADDR_NONE;
|
||||
const int r=m_dns ? m_dns->resolve(cs->hostname,&ip) : -1;
|
||||
if (r>0) return 0;
|
||||
|
||||
free(cs->hostname);
|
||||
cs->hostname=NULL;
|
||||
|
||||
if (r<0 || !__run_connect(cs,ip))
|
||||
{
|
||||
cs->state = STATE_ERR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int eel_net_state::do_recv(void *opaque, EEL_F h, char *buf, int maxlen)
|
||||
{
|
||||
const int idx=(int)h-CONNECTION_ID_BASE;
|
||||
if (idx>=0 && idx<m_cons.GetSize())
|
||||
{
|
||||
connection_state *s=m_cons.Get()+idx;
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (s->sock == INVALID_SOCKET && !s->hostname)
|
||||
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is not open",h);
|
||||
#endif
|
||||
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0;
|
||||
|
||||
if (maxlen == 0) return 0;
|
||||
|
||||
const int rv=(int)recv(s->sock,buf,maxlen,0);
|
||||
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0;
|
||||
|
||||
if (!rv) return -1; // TCP, 0=connection terminated
|
||||
|
||||
return rv;
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_recv: connection identifier %f is out of range",h);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int eel_net_state::do_send(void *opaque, EEL_F h, const char *src, int len)
|
||||
{
|
||||
const int idx=(int)h-CONNECTION_ID_BASE;
|
||||
if (idx>=0 && idx<m_cons.GetSize())
|
||||
{
|
||||
connection_state *s=m_cons.Get()+idx;
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (s->sock == INVALID_SOCKET && !s->hostname)
|
||||
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f is not open",h);
|
||||
#endif
|
||||
if (__run(s) || s->sock == INVALID_SOCKET) return s->state == STATE_ERR ? -1 : 0;
|
||||
const int rv=(int)send(s->sock,src,len,0);
|
||||
if (rv < 0 && !s->blockmode && (JNL_ERRNO == JNL_EWOULDBLOCK || JNL_ERRNO == JNL_ENOTCONN)) return 0;
|
||||
return rv;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_send: connection identifier %f out of range",h);
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
EEL_F eel_net_state::set_block(void *opaque, EEL_F handle, bool block)
|
||||
{
|
||||
int idx=(int)handle-CONNECTION_ID_BASE;
|
||||
if (idx>=0 && idx<m_cons.GetSize())
|
||||
{
|
||||
connection_state *s=m_cons.Get()+idx;
|
||||
if (s->blockmode != block)
|
||||
{
|
||||
s->blockmode=block;
|
||||
if (s->sock != INVALID_SOCKET)
|
||||
{
|
||||
SET_SOCK_BLOCK(s->sock,(block?1:0));
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (!s->hostname) EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f is not open",handle);
|
||||
#endif
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_set_block: connection identifier %f out of range",handle);
|
||||
#endif
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
EEL_F eel_net_state::onClose(void *opaque, EEL_F handle)
|
||||
{
|
||||
int idx=(int)handle-CONNECTION_ID_BASE;
|
||||
if (idx>=0 && idx<m_cons.GetSize())
|
||||
{
|
||||
connection_state *s=m_cons.Get()+idx;
|
||||
const bool hadhn = !!s->hostname;
|
||||
free(s->hostname);
|
||||
s->hostname = NULL;
|
||||
s->state = STATE_ERR;
|
||||
if (s->sock != INVALID_SOCKET)
|
||||
{
|
||||
shutdown(s->sock,SHUT_RDWR);
|
||||
closesocket(s->sock);
|
||||
s->sock = INVALID_SOCKET;
|
||||
return 1.0;
|
||||
}
|
||||
else if (!hadhn)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is not open",handle);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_close: connection identifier %f is out of range",handle);
|
||||
#endif
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_connect(void *opaque, INT_PTR np, EEL_F **parms)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
|
||||
{
|
||||
char *dest=NULL;
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[0][0],NULL);
|
||||
if (fn) dest=strdup(fn);
|
||||
else
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("tcp_connect(): host string identifier %f invalid",parms[0][0]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (dest) return ctx->onConnect(dest, (int) (parms[1][0]+0.5), np < 3 || parms[2][0] >= 0.5);
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_set_block(void *opaque, EEL_F *handle, EEL_F *bl)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->set_block(opaque,*handle, *bl >= 0.5);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_close(void *opaque, EEL_F *handle)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onClose(opaque,*handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_recv(void *opaque, INT_PTR np, EEL_F **parms)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
|
||||
{
|
||||
char buf[EEL_STRING_MAXUSERSTRING_LENGTH_HINT];
|
||||
int ml = np > 2 ? (int)parms[2][0] : 4096;
|
||||
if (ml < 0 || ml > EEL_STRING_MAXUSERSTRING_LENGTH_HINT) ml = EEL_STRING_MAXUSERSTRING_LENGTH_HINT;
|
||||
|
||||
ml=ctx->do_recv(opaque,parms[0][0],buf,ml);
|
||||
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
WDL_FastString *ws=NULL;
|
||||
EEL_STRING_GET_FOR_WRITE(parms[1][0],&ws);
|
||||
if (ws)
|
||||
{
|
||||
if (ml<=0) ws->Set("");
|
||||
else ws->SetRaw(buf,ml);
|
||||
}
|
||||
}
|
||||
return ml;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_send(void *opaque, INT_PTR np, EEL_F **parms)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (np > 1 && NULL != (ctx=EEL_NET_GET_CONTEXT(opaque)))
|
||||
{
|
||||
char buf[EEL_NET_MAXSEND];
|
||||
|
||||
int l;
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
WDL_FastString *ws=NULL;
|
||||
const char *fn = EEL_STRING_GET_FOR_INDEX(parms[1][0],&ws);
|
||||
l = ws ? ws->GetLength() : (int) strlen(fn);
|
||||
if (np > 2)
|
||||
{
|
||||
int al=(int)parms[2][0];
|
||||
if (al<0) al=0;
|
||||
if (al<l) l=al;
|
||||
}
|
||||
if (l > 0) memcpy(buf,fn,l);
|
||||
}
|
||||
if (l>0) return ctx->do_send(opaque,parms[0][0],buf,l);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen(void *opaque, INT_PTR np, EEL_F **parms)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,parms[0][0],1,np>1?parms[1]:NULL,np>2?parms[2]:NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_tcp_listen_end(void *opaque, EEL_F *handle)
|
||||
{
|
||||
eel_net_state *ctx;
|
||||
if (NULL != (ctx=EEL_NET_GET_CONTEXT(opaque))) return ctx->onListen(opaque,*handle,-1,NULL,NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void EEL_tcp_register()
|
||||
{
|
||||
NSEEL_addfunc_varparm("tcp_listen",1,NSEEL_PProc_THIS,&_eel_tcp_listen);
|
||||
NSEEL_addfunc_retval("tcp_listen_end",1,NSEEL_PProc_THIS,&_eel_tcp_listen_end);
|
||||
|
||||
NSEEL_addfunc_varparm("tcp_connect",2,NSEEL_PProc_THIS,&_eel_tcp_connect);
|
||||
NSEEL_addfunc_varparm("tcp_send",2,NSEEL_PProc_THIS,&_eel_tcp_send);
|
||||
NSEEL_addfunc_varparm("tcp_recv",2,NSEEL_PProc_THIS,&_eel_tcp_recv);
|
||||
|
||||
NSEEL_addfunc_retval("tcp_set_block",2,NSEEL_PProc_THIS,&_eel_tcp_set_block);
|
||||
NSEEL_addfunc_retval("tcp_close",1,NSEEL_PProc_THIS,&_eel_tcp_close);
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
const char *eel_net_function_reference =
|
||||
"tcp_listen\tport[,\"interface\",#ip_out]\tListens on port specified. Returns less than 0 if could not listen, 0 if no new connection available, or greater than 0 (as a TCP connection ID) if a new connection was made. If a connection made and #ip_out specified, it will be set to the remote IP. interface can be empty for all interfaces, otherwise an interface IP as a string.\0"
|
||||
"tcp_listen_end\tport\tEnds listening on port specified.\0"
|
||||
"tcp_connect\t\"address\",port[,block]\tCreate a new TCP connection to address:port. If block is specified and 0, connection will be made nonblocking. Returns TCP connection ID greater than 0 on success.\0"
|
||||
"tcp_send\tconnection,\"str\"[,len]\tSends a string to connection. Returns -1 on error, 0 if connection is non-blocking and would block, otherwise returns length sent. If len is specified and not less than 1, only the first len bytes of the string parameter will be sent.\0"
|
||||
"tcp_recv\tconnection,#str[,maxlen]\tReceives data from a connection to #str. If maxlen is specified, no more than maxlen bytes will be received. If non-blocking, 0 will be returned if would block. Returns less than 0 if error.\0"
|
||||
"tcp_set_block\tconnection,block\tSets whether a connection blocks.\0"
|
||||
"tcp_close\tconnection\tCloses a TCP connection created by tcp_listen() or tcp_connect().\0"
|
||||
;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,47 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "../wdlstring.h"
|
||||
#include "../ptrlist.h"
|
||||
#include "eel_pproc.h"
|
||||
|
||||
void NSEEL_HOSTSTUB_EnterMutex() { }
|
||||
void NSEEL_HOSTSTUB_LeaveMutex() { }
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
fprintf(stderr,"Usage: %s [scriptfile | -]\n",argv[0]);
|
||||
return 1;
|
||||
}
|
||||
FILE *fp = strcmp(argv[1],"-") ? fopen(argv[1],"rb") : stdin;
|
||||
if (!fp)
|
||||
{
|
||||
fprintf(stderr,"Error: could not open %s\n",argv[1]);
|
||||
return 1;
|
||||
}
|
||||
WDL_FastString file_str, pp_str;
|
||||
for (;;)
|
||||
{
|
||||
char buf[4096];
|
||||
if (!fgets(buf,sizeof(buf),fp)) break;
|
||||
file_str.Append(buf);
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
|
||||
EEL2_PreProcessor pproc;
|
||||
const char *err = pproc.preprocess(file_str.Get(),&pp_str);
|
||||
if (err)
|
||||
{
|
||||
fprintf(stderr,"Error: %s\n",err);
|
||||
return 1;
|
||||
}
|
||||
|
||||
printf("%s",pp_str.Get());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
#ifndef _EEL2_PREPROC_H_
|
||||
#define _EEL2_PREPROC_H_
|
||||
#include "ns-eel-int.h"
|
||||
#include "../win32_utf8.h"
|
||||
|
||||
#define EEL2_PREPROCESS_OPEN_TOKEN "<?"
|
||||
|
||||
class EEL2_PreProcessor
|
||||
{
|
||||
enum { LITERAL_BASE = 100000 };
|
||||
public:
|
||||
EEL2_PreProcessor(int max_sz = 64<<20, int max_include_depth=20)
|
||||
{
|
||||
m_max_sz = max_sz;
|
||||
m_fsout = NULL;
|
||||
m_vm = NSEEL_VM_alloc();
|
||||
m_max_include_depth = max_include_depth;
|
||||
m_output_linecnt = 0;
|
||||
m_cur_depth = 0;
|
||||
NSEEL_VM_SetCustomFuncThis(m_vm, this);
|
||||
NSEEL_VM_SetStringFunc(m_vm, addStringCallback, NULL);
|
||||
if (!m_ftab.list_size)
|
||||
{
|
||||
NSEEL_addfunc_varparm_ex("printf",1,0,NSEEL_PProc_THIS,&pp_printf,&m_ftab);
|
||||
NSEEL_addfunc_varparm_ex("include",1,1,NSEEL_PProc_THIS,&pp_include,&m_ftab);
|
||||
}
|
||||
NSEEL_VM_SetFunctionTable(m_vm, &m_ftab);
|
||||
m_suppress = NSEEL_VM_regvar(m_vm, "_suppress");
|
||||
}
|
||||
|
||||
void define(const char *name, double val)
|
||||
{
|
||||
EEL_F *v = NSEEL_VM_regvar(m_vm,name);
|
||||
if (v) *v = val;
|
||||
}
|
||||
|
||||
~EEL2_PreProcessor()
|
||||
{
|
||||
for (int x = 0; x < m_code_handles.GetSize(); x ++)
|
||||
NSEEL_code_free((NSEEL_CODEHANDLE) m_code_handles.Get(x));
|
||||
m_literal_strings.Empty(true,free);
|
||||
if (m_vm) NSEEL_VM_free(m_vm);
|
||||
m_suppress = NULL;
|
||||
}
|
||||
|
||||
void clear_line_info()
|
||||
{
|
||||
m_line_tab.Resize(0);
|
||||
}
|
||||
const char *preprocess(const char *str, WDL_FastString *fs)
|
||||
{
|
||||
if (!m_vm || !m_suppress)
|
||||
return "preprocessor: memory error";
|
||||
if (!m_cur_depth)
|
||||
{
|
||||
m_line_tab.Resize(0);
|
||||
m_output_linecnt = 0;
|
||||
*m_suppress = 0.0;
|
||||
}
|
||||
int input_linecnt = 0;
|
||||
for (;;)
|
||||
{
|
||||
const bool suppress = m_suppress && *m_suppress > 0.0;
|
||||
|
||||
int lc = 0;
|
||||
const char *tag = str;
|
||||
while (*tag && strncmp(tag,EEL2_PREPROCESS_OPEN_TOKEN,2)) if (*tag++ == '\n') lc++;
|
||||
|
||||
if (lc)
|
||||
{
|
||||
input_linecnt += lc;
|
||||
if (suppress)
|
||||
add_line_inf(m_output_linecnt,lc);
|
||||
else
|
||||
m_output_linecnt += lc;
|
||||
}
|
||||
|
||||
if (!*tag)
|
||||
{
|
||||
if (!suppress) fs->Append(str);
|
||||
return NULL;
|
||||
}
|
||||
if (!suppress && tag > str) fs->Append(str,(int)(tag-str));
|
||||
|
||||
tag += 2;
|
||||
while (*tag == ' ' || *tag == '\t') tag++;
|
||||
str = tag;
|
||||
lc = 0;
|
||||
while (*str && strncmp(str,"?>",2)) if (*str++ == '\n') lc++;
|
||||
|
||||
if (!*str)
|
||||
{
|
||||
m_tmp.SetFormatted(512, "%d: unterminated preprocessor " EEL2_PREPROCESS_OPEN_TOKEN " block", input_linecnt+1);
|
||||
return m_tmp.Get();
|
||||
}
|
||||
|
||||
if (lc)
|
||||
{
|
||||
input_linecnt += lc;
|
||||
add_line_inf(m_output_linecnt,lc);
|
||||
}
|
||||
if (str > tag)
|
||||
{
|
||||
m_tmp.Set(tag,(int)(str-tag));
|
||||
NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, m_tmp.Get(), 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
|
||||
if (!ch)
|
||||
{
|
||||
const char *err = NSEEL_code_getcodeerror(m_vm);
|
||||
if (err)
|
||||
{
|
||||
const int line_ref = atoi(err);
|
||||
while (*err >= '0' && *err <= '9') err++;
|
||||
m_tmp.SetFormatted(512,"%d: preprocessor%s%s",input_linecnt+line_ref,*err && *err != ':' ? ": ":"",err);
|
||||
return m_tmp.Get();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lc = 0;
|
||||
const int oldlen = fs->GetLength();
|
||||
m_fsout = fs;
|
||||
NSEEL_code_execute(ch);
|
||||
m_fsout = NULL;
|
||||
m_code_handles.Add(ch);
|
||||
for (int x = oldlen; x < fs->GetLength(); x ++) if (fs->Get()[x] == '\n') lc++;
|
||||
if (lc)
|
||||
{
|
||||
add_line_inf(m_output_linecnt,-lc);
|
||||
m_output_linecnt += lc;
|
||||
}
|
||||
}
|
||||
}
|
||||
str += 2;
|
||||
}
|
||||
}
|
||||
|
||||
const char *translate_error_line(const char *err_line)
|
||||
{
|
||||
if (m_line_tab.GetSize()<2) return err_line;
|
||||
int l = atoi(err_line)-1;
|
||||
if (l<0) return err_line;
|
||||
|
||||
// tab is a list of pairs
|
||||
// [<output position>, delta]
|
||||
// delta>0 if input lines were skipped
|
||||
// delta<0 if output lines were added
|
||||
|
||||
int nl = l;
|
||||
for (int x = m_line_tab.GetSize()-2; x >= 0; x -= 2)
|
||||
{
|
||||
int p = m_line_tab.Get()[x];
|
||||
if (nl > p)
|
||||
{
|
||||
int delta = m_line_tab.Get()[x+1];
|
||||
nl += delta;
|
||||
if (nl < p) nl = p;
|
||||
}
|
||||
}
|
||||
|
||||
if (l == nl) return err_line;
|
||||
|
||||
while (*err_line >= '0' && *err_line <= '9') err_line++;
|
||||
if (*err_line == ':') err_line++;
|
||||
m_tmp.SetFormatted(512,"%d:%s",1 + nl,err_line);
|
||||
return m_tmp.Get();
|
||||
}
|
||||
|
||||
NSEEL_VMCTX m_vm;
|
||||
WDL_PtrList<char> m_literal_strings;
|
||||
WDL_FastString m_tmp, *m_fsout;
|
||||
WDL_TypedBuf<int> m_line_tab; // expose this in case the caller wants to keep copies around
|
||||
EEL_F *m_suppress;
|
||||
int m_max_sz;
|
||||
int m_cur_depth, m_max_include_depth;
|
||||
int m_output_linecnt;
|
||||
static eel_function_table m_ftab;
|
||||
WDL_PtrList<void> m_code_handles;
|
||||
WDL_PtrList<const char> m_include_paths;
|
||||
|
||||
void add_line_inf(int output_linecnt, int lc)
|
||||
{
|
||||
if (!m_cur_depth)
|
||||
{
|
||||
m_line_tab.Add(output_linecnt); // log lc lines of input skipped
|
||||
m_line_tab.Add(lc);
|
||||
}
|
||||
}
|
||||
|
||||
static EEL_F addStringCallback(void *opaque, struct eelStringSegmentRec *list)
|
||||
{
|
||||
EEL2_PreProcessor *_this = (EEL2_PreProcessor*)opaque;
|
||||
if (!_this) return -1.0;
|
||||
|
||||
const int sz = nseel_stringsegments_tobuf(NULL,0,list);
|
||||
char *ns = (char *)malloc(sz+1);
|
||||
if (WDL_NOT_NORMALLY(!ns)) return -1.0;
|
||||
nseel_stringsegments_tobuf(ns,sz,list);
|
||||
|
||||
const int nstr = _this->m_literal_strings.GetSize();
|
||||
for (int x=0;x<nstr;x++)
|
||||
{
|
||||
char *s = _this->m_literal_strings.Get(x);
|
||||
if (!strcmp(s,ns))
|
||||
{
|
||||
free(ns);
|
||||
return x + LITERAL_BASE;
|
||||
}
|
||||
}
|
||||
_this->m_literal_strings.Add(ns);
|
||||
return nstr + LITERAL_BASE;
|
||||
}
|
||||
|
||||
const char *GetString(EEL_F v)
|
||||
{
|
||||
if (v >= LITERAL_BASE && v < LITERAL_BASE + m_literal_strings.GetSize())
|
||||
return m_literal_strings.Get((int) (v - LITERAL_BASE));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int eel_validate_format_specifier(const char *fmt_in, char *typeOut,
|
||||
char *fmtOut, int fmtOut_sz,
|
||||
char *varOut, int varOut_sz,
|
||||
int *varOut_used
|
||||
)
|
||||
{
|
||||
const char *fmt = fmt_in+1;
|
||||
int state=0;
|
||||
if (fmt_in[0] != '%') return 0; // ugh passed a non-specifier
|
||||
|
||||
*varOut_used = 0;
|
||||
*varOut = 0;
|
||||
|
||||
if (fmtOut_sz-- < 2) return 0;
|
||||
*fmtOut++ = '%';
|
||||
|
||||
while (*fmt)
|
||||
{
|
||||
const char c = *fmt++;
|
||||
if (fmtOut_sz < 2) return 0;
|
||||
|
||||
if (c == 'f'|| c=='e' || c=='E' || c=='g' || c=='G' || c == 'd' || c == 'u' ||
|
||||
c == 'x' || c == 'X' || c == 'c' || c == 'C' || c =='s' || c=='S' || c=='i')
|
||||
{
|
||||
*typeOut = c;
|
||||
fmtOut[0] = c;
|
||||
fmtOut[1] = 0;
|
||||
return (int) (fmt - fmt_in);
|
||||
}
|
||||
else if (c == '.')
|
||||
{
|
||||
*fmtOut++ = c; fmtOut_sz--;
|
||||
if (state&(2)) break;
|
||||
state |= 2;
|
||||
}
|
||||
else if (c == '+')
|
||||
{
|
||||
*fmtOut++ = c; fmtOut_sz--;
|
||||
if (state&(32|16|8|4)) break;
|
||||
state |= 8;
|
||||
}
|
||||
else if (c == '-' || c == ' ')
|
||||
{
|
||||
*fmtOut++ = c; fmtOut_sz--;
|
||||
if (state&(32|16|8|4)) break;
|
||||
state |= 16;
|
||||
}
|
||||
else if (c >= '0' && c <= '9')
|
||||
{
|
||||
*fmtOut++ = c; fmtOut_sz--;
|
||||
state|=4;
|
||||
}
|
||||
else if (c == '{')
|
||||
{
|
||||
if (state & 64) break;
|
||||
state|=64;
|
||||
if (*fmt == '.' || (*fmt >= '0' && *fmt <= '9')) return 0; // symbol name can't start with 0-9 or .
|
||||
|
||||
while (*fmt != '}')
|
||||
{
|
||||
if ((*fmt >= 'a' && *fmt <= 'z') ||
|
||||
(*fmt >= 'A' && *fmt <= 'Z') ||
|
||||
(*fmt >= '0' && *fmt <= '9') ||
|
||||
*fmt == '_' || *fmt == '.' || *fmt == '#')
|
||||
{
|
||||
if (varOut_sz < 2) return 0;
|
||||
*varOut++ = *fmt++;
|
||||
varOut_sz -- ;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0; // bad character in variable name
|
||||
}
|
||||
}
|
||||
fmt++;
|
||||
*varOut = 0;
|
||||
*varOut_used=1;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eel_format_strings(void *opaque, const char *fmt, const char *fmt_end, char *buf, int buf_sz, int num_fmt_parms, EEL_F **fmt_parms)
|
||||
{
|
||||
EEL2_PreProcessor *_this = (EEL2_PreProcessor*)opaque;
|
||||
int fmt_parmpos = 0;
|
||||
char *op = buf;
|
||||
while ((fmt_end ? fmt < fmt_end : *fmt) && op < buf+buf_sz-128)
|
||||
{
|
||||
if (fmt[0] == '%' && fmt[1] == '%')
|
||||
{
|
||||
*op++ = '%';
|
||||
fmt+=2;
|
||||
}
|
||||
else if (fmt[0] == '%')
|
||||
{
|
||||
char ct=0;
|
||||
char fs[128];
|
||||
char varname[128];
|
||||
int varname_used=0;
|
||||
const int l=eel_validate_format_specifier(fmt,&ct,fs,sizeof(fs),varname,sizeof(varname),&varname_used);
|
||||
if (!l || !ct)
|
||||
{
|
||||
*op=0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
const EEL_F *varptr = NULL;
|
||||
if (!varname_used)
|
||||
{
|
||||
if (fmt_parmpos < num_fmt_parms) varptr = fmt_parms[fmt_parmpos];
|
||||
fmt_parmpos++;
|
||||
}
|
||||
double v = varptr ? (double)*varptr : 0.0;
|
||||
|
||||
if (ct == 's' || ct=='S')
|
||||
{
|
||||
const char *str = _this->GetString(v);
|
||||
const int maxl=(int) (buf+buf_sz - 2 - op);
|
||||
snprintf(op,maxl,fs,str ? str : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ct == 'x' || ct == 'X' || ct == 'd' || ct == 'u' || ct=='i')
|
||||
{
|
||||
snprintf(op,64,fs,(int) (v));
|
||||
}
|
||||
else if (ct == 'c')
|
||||
{
|
||||
*op++=(char) (int)v;
|
||||
*op=0;
|
||||
}
|
||||
else if (ct == 'C')
|
||||
{
|
||||
const unsigned int iv = (unsigned int) v;
|
||||
int bs = 0;
|
||||
if (iv & 0xff000000) bs=24;
|
||||
else if (iv & 0x00ff0000) bs=16;
|
||||
else if (iv & 0x0000ff00) bs=8;
|
||||
while (bs>=0)
|
||||
{
|
||||
const char c=(char) (iv>>bs);
|
||||
*op++=c?c:' ';
|
||||
bs-=8;
|
||||
}
|
||||
*op=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(op,64,fs,v);
|
||||
}
|
||||
}
|
||||
|
||||
while (*op) op++;
|
||||
|
||||
fmt += l;
|
||||
}
|
||||
else
|
||||
{
|
||||
*op++ = *fmt++;
|
||||
}
|
||||
|
||||
}
|
||||
*op=0;
|
||||
return (int) (op - buf);
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL pp_printf(void *opaque, INT_PTR num_param, EEL_F **parms)
|
||||
{
|
||||
if (num_param>0 && opaque)
|
||||
{
|
||||
EEL2_PreProcessor *_this = (EEL2_PreProcessor*)opaque;
|
||||
const char *fmt = _this->GetString(parms[0][0]);
|
||||
if (fmt)
|
||||
{
|
||||
char buf[16384];
|
||||
const int len = eel_format_strings(opaque,fmt,NULL,buf,(int)sizeof(buf), (int)num_param-1, parms+1);
|
||||
|
||||
if (len >= 0)
|
||||
{
|
||||
if (_this->m_fsout && _this->m_fsout->GetLength() < _this->m_max_sz)
|
||||
{
|
||||
_this->m_fsout->Append(buf,len);
|
||||
}
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL pp_include(void *opaque, INT_PTR num_param, EEL_F **parms)
|
||||
{
|
||||
if (num_param>0 && opaque)
|
||||
{
|
||||
EEL2_PreProcessor *_this = (EEL2_PreProcessor*)opaque;
|
||||
if (_this->m_cur_depth >= _this->m_max_include_depth) return -1.0;
|
||||
|
||||
const char *fn = _this->GetString(parms[0][0]);
|
||||
if (!fn || !*fn) return -2.0;
|
||||
|
||||
WDL_FastString fullfn;
|
||||
for (int x = _this->m_include_paths.GetSize()-1; x >= 0; x--)
|
||||
{
|
||||
const char *p = _this->m_include_paths.Get(x);
|
||||
if (p && *p)
|
||||
{
|
||||
fullfn.Set(p);
|
||||
fullfn.Append(WDL_DIRCHAR_STR);
|
||||
fullfn.Append(fn);
|
||||
FILE *fp = fopenUTF8(fullfn.Get(),"rb");
|
||||
if (fp)
|
||||
{
|
||||
double rv = 0.0;
|
||||
_this->m_cur_depth++;
|
||||
fullfn.Set("");
|
||||
while (fullfn.GetLength() < (4<<20))
|
||||
{
|
||||
char buf[512];
|
||||
if (!fgets(buf,sizeof(buf),fp)) break;
|
||||
fullfn.Append(buf);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
WDL_FastString *outp = _this->m_fsout;
|
||||
if (_this->preprocess(fullfn.Get(),outp))
|
||||
{
|
||||
rv = -3.0;
|
||||
}
|
||||
_this->m_fsout = outp;
|
||||
|
||||
_this->m_cur_depth--;
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -4.0;
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
eel_function_table EEL2_PreProcessor::m_ftab;
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,834 @@
|
||||
#ifndef _WIN32
|
||||
#include <unistd.h>
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
#include "../swell/swell.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "../wdltypes.h"
|
||||
#include "../ptrlist.h"
|
||||
#include "../wdlstring.h"
|
||||
#include "../assocarray.h"
|
||||
#include "../queue.h"
|
||||
#include "../mutex.h"
|
||||
#include "../win32_utf8.h"
|
||||
#include "ns-eel.h"
|
||||
|
||||
|
||||
#ifndef EELSCRIPT_MAX_FILE_HANDLES
|
||||
#define EELSCRIPT_MAX_FILE_HANDLES 512
|
||||
#endif
|
||||
#ifndef EELSCRIPT_FILE_HANDLE_INDEX_BASE
|
||||
#define EELSCRIPT_FILE_HANDLE_INDEX_BASE 1000000
|
||||
#endif
|
||||
#ifndef EEL_STRING_MAXUSERSTRING_LENGTH_HINT
|
||||
#define EEL_STRING_MAXUSERSTRING_LENGTH_HINT (1<<16) // 64KB per string max
|
||||
#endif
|
||||
#ifndef EEL_STRING_MAX_USER_STRINGS
|
||||
#define EEL_STRING_MAX_USER_STRINGS 32768
|
||||
#endif
|
||||
#ifndef EEL_STRING_LITERAL_BASE
|
||||
#define EEL_STRING_LITERAL_BASE 2000000
|
||||
#endif
|
||||
#ifndef EELSCRIPT_LICE_MAX_IMAGES
|
||||
#define EELSCRIPT_LICE_MAX_IMAGES 1024
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_LICE_MAX_FONTS
|
||||
#define EELSCRIPT_LICE_MAX_FONTS 128
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NET_MAXCON
|
||||
#define EELSCRIPT_NET_MAXCON 4096
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_LICE_CLASSNAME
|
||||
#define EELSCRIPT_LICE_CLASSNAME "eelscript_gfx"
|
||||
#endif
|
||||
|
||||
|
||||
// #define EELSCRIPT_NO_NET
|
||||
// #define EELSCRIPT_NO_LICE
|
||||
// #define EELSCRIPT_NO_FILE
|
||||
// #define EELSCRIPT_NO_FFT
|
||||
// #define EELSCRIPT_NO_MDCT
|
||||
// #define EELSCRIPT_NO_EVAL
|
||||
|
||||
class eel_string_context_state;
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
class eel_net_state;
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
class eel_lice_state;
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
#include "eel_pproc.h"
|
||||
#endif
|
||||
|
||||
class eelScriptInst {
|
||||
public:
|
||||
|
||||
static int init();
|
||||
|
||||
eelScriptInst();
|
||||
virtual ~eelScriptInst();
|
||||
|
||||
NSEEL_CODEHANDLE compile_code(const char *code, const char **err);
|
||||
int runcode(const char *code, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec);
|
||||
int loadfile(const char *fn, const char *callerfn, bool allowstdin);
|
||||
|
||||
NSEEL_VMCTX m_vm;
|
||||
|
||||
WDL_PtrList<void> m_code_freelist;
|
||||
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
FILE *m_handles[EELSCRIPT_MAX_FILE_HANDLES];
|
||||
virtual EEL_F OpenFile(const char *fn, const char *mode)
|
||||
{
|
||||
if (!*fn || !*mode) return 0.0;
|
||||
#ifndef EELSCRIPT_NO_STDIO
|
||||
if (!strcmp(fn,"stdin")) return 1;
|
||||
if (!strcmp(fn,"stdout")) return 2;
|
||||
if (!strcmp(fn,"stderr")) return 3;
|
||||
#endif
|
||||
|
||||
WDL_FastString fnstr(fn);
|
||||
if (!translateFilename(&fnstr,mode)) return 0.0;
|
||||
|
||||
int x;
|
||||
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES && m_handles[x];x++);
|
||||
if (x>= EELSCRIPT_MAX_FILE_HANDLES) return 0.0;
|
||||
|
||||
FILE *fp = fopenUTF8(fnstr.Get(),mode);
|
||||
if (!fp) return 0.0;
|
||||
m_handles[x]=fp;
|
||||
return x + EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
||||
}
|
||||
virtual EEL_F CloseFile(int fp_idx)
|
||||
{
|
||||
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
||||
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES && m_handles[fp_idx])
|
||||
{
|
||||
fclose(m_handles[fp_idx]);
|
||||
m_handles[fp_idx]=0;
|
||||
return 0.0;
|
||||
}
|
||||
return -1.0;
|
||||
}
|
||||
virtual FILE *GetFileFP(int fp_idx)
|
||||
{
|
||||
#ifndef EELSCRIPT_NO_STDIO
|
||||
if (fp_idx==1) return stdin;
|
||||
if (fp_idx==2) return stdout;
|
||||
if (fp_idx==3) return stderr;
|
||||
#endif
|
||||
fp_idx-=EELSCRIPT_FILE_HANDLE_INDEX_BASE;
|
||||
if (fp_idx>=0 && fp_idx<EELSCRIPT_MAX_FILE_HANDLES) return m_handles[fp_idx];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
virtual bool translateFilename(WDL_FastString *fs, const char *mode) { return true; }
|
||||
|
||||
virtual bool GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite);
|
||||
|
||||
eel_string_context_state *m_string_context;
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
eel_net_state *m_net_state;
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
eel_lice_state *m_gfx_state;
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
struct evalCacheEnt {
|
||||
char *str;
|
||||
NSEEL_CODEHANDLE ch;
|
||||
};
|
||||
int m_eval_depth;
|
||||
WDL_TypedBuf<evalCacheEnt> m_eval_cache;
|
||||
virtual char *evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch);
|
||||
virtual void evalCacheDispose(char *key, NSEEL_CODEHANDLE ch);
|
||||
WDL_Queue m_defer_eval, m_atexit_eval;
|
||||
void runCodeQ(WDL_Queue *q, const char *fname);
|
||||
void runAtExitCode()
|
||||
{
|
||||
runCodeQ(&m_atexit_eval,"atexit");
|
||||
m_atexit_eval.Clear(); // make sure nothing gets added in atexit(), in case the user called runAtExitCode before destroying
|
||||
}
|
||||
#endif
|
||||
virtual bool run_deferred(); // requires eval support to be useful
|
||||
virtual bool has_deferred();
|
||||
|
||||
|
||||
WDL_StringKeyedArray<bool> m_loaded_fnlist; // imported file list (to avoid repeats)
|
||||
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
EEL2_PreProcessor m_preproc;
|
||||
#endif
|
||||
};
|
||||
|
||||
//#define EEL_STRINGS_MUTABLE_LITERALS
|
||||
//#define EEL_STRING_WANT_MUTEX
|
||||
|
||||
|
||||
#define EEL_STRING_GET_CONTEXT_POINTER(opaque) (((eelScriptInst *)opaque)->m_string_context)
|
||||
#ifndef EEL_STRING_STDOUT_WRITE
|
||||
#ifndef EELSCRIPT_NO_STDIO
|
||||
#define EEL_STRING_STDOUT_WRITE(x,len) { fwrite(x,len,1,stdout); fflush(stdout); }
|
||||
#endif
|
||||
#endif
|
||||
#include "eel_strings.h"
|
||||
|
||||
#include "eel_misc.h"
|
||||
|
||||
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
#define EEL_FILE_OPEN(fn,mode) ((eelScriptInst*)opaque)->OpenFile(fn,mode)
|
||||
#define EEL_FILE_GETFP(fp) ((eelScriptInst*)opaque)->GetFileFP(fp)
|
||||
#define EEL_FILE_CLOSE(fpindex) ((eelScriptInst*)opaque)->CloseFile(fpindex)
|
||||
|
||||
#include "eel_files.h"
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_FFT
|
||||
#include "eel_fft.h"
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_MDCT
|
||||
#include "eel_mdct.h"
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
#define EEL_NET_GET_CONTEXT(opaque) (((eelScriptInst *)opaque)->m_net_state)
|
||||
#include "eel_net.h"
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
#ifndef EEL_LICE_WANT_STANDALONE
|
||||
#define EEL_LICE_WANT_STANDALONE
|
||||
#endif
|
||||
#ifndef EELSCRIPT_LICE_NOUPDATE
|
||||
#define EEL_LICE_WANT_STANDALONE_UPDATE // gfx_update() which runs message pump and updates screen etc
|
||||
#endif
|
||||
|
||||
#define EEL_LICE_GET_FILENAME_FOR_STRING(idx, fs, p) (((eelScriptInst*)opaque)->GetFilenameForParameter(idx,fs,p))
|
||||
#define EEL_LICE_GET_CONTEXT(opaque) ((opaque) ? (((eelScriptInst *)opaque)->m_gfx_state) : NULL)
|
||||
#include "eel_lice.h"
|
||||
#endif
|
||||
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
#define EEL_EVAL_GET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheGet(str,&(ch))
|
||||
#define EEL_EVAL_SET_CACHED(str, ch) ((eelScriptInst *)opaque)->evalCacheDispose(str,ch)
|
||||
#define EEL_EVAL_GET_VMCTX(opaque) (((eelScriptInst *)opaque)->m_vm)
|
||||
#define EEL_EVAL_SCOPE_ENTER (((eelScriptInst *)opaque)->m_eval_depth < 3 ? \
|
||||
++((eelScriptInst *)opaque)->m_eval_depth : 0)
|
||||
#define EEL_EVAL_SCOPE_LEAVE ((eelScriptInst *)opaque)->m_eval_depth--;
|
||||
#include "eel_eval.h"
|
||||
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_defer(void *opaque, EEL_F *s)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
|
||||
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow defer(0) etc
|
||||
{
|
||||
eelScriptInst *inst = (eelScriptInst *)opaque;
|
||||
if (inst->m_defer_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
|
||||
{
|
||||
inst->m_defer_eval.Add(str,strlen(str)+1);
|
||||
return 1.0;
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("defer(): too much defer() code already added, ignoring");
|
||||
#endif
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
else if (!str)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("defer(): invalid string identifier specified %f",*s);
|
||||
}
|
||||
else if (*s < EEL_STRING_MAX_USER_STRINGS)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("defer(): user string identifier %f specified but not allowed",*s);
|
||||
}
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
static EEL_F NSEEL_CGEN_CALL _eel_atexit(void *opaque, EEL_F *s)
|
||||
{
|
||||
EEL_STRING_MUTEXLOCK_SCOPE
|
||||
const char *str=EEL_STRING_GET_FOR_INDEX(*s,NULL);
|
||||
if (str && *str && *s >= EEL_STRING_MAX_USER_STRINGS) // don't allow atexit(0) etc
|
||||
{
|
||||
eelScriptInst *inst = (eelScriptInst *)opaque;
|
||||
if (inst->m_atexit_eval.Available() < EEL_STRING_MAXUSERSTRING_LENGTH_HINT)
|
||||
{
|
||||
inst->m_atexit_eval.Add(str,strlen(str)+1);
|
||||
return 1.0;
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("atexit(): too much atexit() code already added, ignoring");
|
||||
#endif
|
||||
}
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
else if (!str)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("atexit(): invalid string identifier specified %f",*s);
|
||||
}
|
||||
else if (*s < EEL_STRING_MAX_USER_STRINGS)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("atexit(): user string identifier %f specified but not allowed",*s);
|
||||
}
|
||||
#endif
|
||||
return 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#define opaque ((void *)this)
|
||||
|
||||
eelScriptInst::eelScriptInst() : m_loaded_fnlist(false)
|
||||
{
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
memset(m_handles,0,sizeof(m_handles));
|
||||
#endif
|
||||
m_vm = NSEEL_VM_alloc();
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (!m_vm) EEL_STRING_DEBUGOUT("NSEEL_VM_alloc(): failed");
|
||||
#endif
|
||||
NSEEL_VM_SetCustomFuncThis(m_vm,this);
|
||||
#ifdef NSEEL_ADDFUNC_DESTINATION
|
||||
NSEEL_VM_SetFunctionTable(m_vm,NSEEL_ADDFUNC_DESTINATION);
|
||||
#endif
|
||||
|
||||
m_string_context = new eel_string_context_state;
|
||||
eel_string_initvm(m_vm);
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
m_net_state = new eel_net_state(EELSCRIPT_NET_MAXCON,NULL);
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
m_gfx_state = new eel_lice_state(m_vm,this,EELSCRIPT_LICE_MAX_IMAGES,EELSCRIPT_LICE_MAX_FONTS);
|
||||
|
||||
m_gfx_state->resetVarsToStock();
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
m_eval_depth=0;
|
||||
#endif
|
||||
}
|
||||
|
||||
eelScriptInst::~eelScriptInst()
|
||||
{
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
if (m_atexit_eval.GetSize()>0) runAtExitCode();
|
||||
#endif
|
||||
int x;
|
||||
m_code_freelist.Empty((void (*)(void *))NSEEL_code_free);
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
for (x=0;x<m_eval_cache.GetSize();x++)
|
||||
{
|
||||
free(m_eval_cache.Get()[x].str);
|
||||
NSEEL_code_free(m_eval_cache.Get()[x].ch);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_vm) NSEEL_VM_free(m_vm);
|
||||
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
for (x=0;x<EELSCRIPT_MAX_FILE_HANDLES;x++)
|
||||
{
|
||||
if (m_handles[x]) fclose(m_handles[x]);
|
||||
m_handles[x]=0;
|
||||
}
|
||||
#endif
|
||||
delete m_string_context;
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
delete m_net_state;
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
delete m_gfx_state;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool eelScriptInst::GetFilenameForParameter(EEL_F idx, WDL_FastString *fs, int iswrite)
|
||||
{
|
||||
const char *fmt = EEL_STRING_GET_FOR_INDEX(idx,NULL);
|
||||
if (!fmt) return false;
|
||||
fs->Set(fmt);
|
||||
return translateFilename(fs,iswrite?"w":"r");
|
||||
}
|
||||
|
||||
NSEEL_CODEHANDLE eelScriptInst::compile_code(const char *code, const char **err)
|
||||
{
|
||||
if (!m_vm)
|
||||
{
|
||||
*err = "EEL VM not initialized";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
WDL_FastString str;
|
||||
if (strstr(code,EEL2_PREPROCESS_OPEN_TOKEN))
|
||||
{
|
||||
const char *pperr = m_preproc.preprocess(code,&str);
|
||||
if (pperr)
|
||||
{
|
||||
*err = pperr;
|
||||
return NULL;
|
||||
}
|
||||
code = str.Get();
|
||||
}
|
||||
else
|
||||
m_preproc.clear_line_info();
|
||||
#endif
|
||||
|
||||
NSEEL_CODEHANDLE ch = NSEEL_code_compile_ex(m_vm, code, 0, NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
|
||||
if (ch)
|
||||
{
|
||||
m_string_context->update_named_vars(m_vm);
|
||||
m_code_freelist.Add((void*)ch);
|
||||
return ch;
|
||||
}
|
||||
*err = NSEEL_code_getcodeerror(m_vm);
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
if (*err) *err = m_preproc.translate_error_line(*err);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int eelScriptInst::runcode(const char *codeptr, int showerr, const char *showerrfn, bool canfree, bool ignoreEndOfInputChk, bool doExec)
|
||||
{
|
||||
if (m_vm)
|
||||
{
|
||||
const char *err = NULL;
|
||||
NSEEL_CODEHANDLE code = NULL;
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
WDL_FastString str;
|
||||
if (strstr(codeptr,EEL2_PREPROCESS_OPEN_TOKEN))
|
||||
{
|
||||
err = m_preproc.preprocess(codeptr,&str);
|
||||
if (err) goto on_preproc_error;
|
||||
codeptr = str.Get();
|
||||
}
|
||||
else
|
||||
m_preproc.clear_line_info();
|
||||
#endif
|
||||
|
||||
code = NSEEL_code_compile_ex(m_vm,codeptr,0,canfree ? 0 : NSEEL_CODE_COMPILE_FLAG_COMMONFUNCS);
|
||||
if (code) m_string_context->update_named_vars(m_vm);
|
||||
|
||||
if (!code && (err=NSEEL_code_getcodeerror(m_vm)))
|
||||
{
|
||||
if (!ignoreEndOfInputChk && (NSEEL_code_geterror_flag(m_vm)&1)) return 1;
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
err = m_preproc.translate_error_line(err);
|
||||
on_preproc_error:
|
||||
#endif
|
||||
if (showerr)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (showerr==2)
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("Warning: %s:%s",WDL_get_filepart(showerrfn),err);
|
||||
}
|
||||
else
|
||||
{
|
||||
EEL_STRING_DEBUGOUT("%s:%s",WDL_get_filepart(showerrfn),err);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (code)
|
||||
{
|
||||
#ifdef EELSCRIPT_DO_DISASSEMBLE
|
||||
codeHandleType *p = (codeHandleType*)code;
|
||||
|
||||
char buf[512];
|
||||
buf[0]=0;
|
||||
#ifdef _WIN32
|
||||
GetTempPath(sizeof(buf)-64,buf);
|
||||
lstrcatn(buf,"jsfx-out",sizeof(buf));
|
||||
#else
|
||||
lstrcpyn_safe(buf,"/tmp/jsfx-out",sizeof(buf));
|
||||
#endif
|
||||
FILE *fp = fopenUTF8(buf,"wb");
|
||||
if (fp)
|
||||
{
|
||||
fwrite(p->code,1,p->code_size,fp);
|
||||
fclose(fp);
|
||||
char buf2[2048];
|
||||
#ifdef _WIN32
|
||||
snprintf(buf2,sizeof(buf2),"disasm \"%s\"",buf);
|
||||
#else
|
||||
#ifdef __aarch64__
|
||||
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -maarch64 \"%s\"",buf);
|
||||
#elif defined(__arm__)
|
||||
snprintf(buf2,sizeof(buf2), "objdump -D -b binary -m arm \"%s\"",buf);
|
||||
#elif defined(__LP64__)
|
||||
#ifdef __APPLE__
|
||||
snprintf(buf2,sizeof(buf2),"distorm3 --b64 \"%s\"",buf);
|
||||
#else
|
||||
snprintf(buf2,sizeof(buf2),"objdump -D -b binary -m i386:x86-64 \"%s\"",buf);
|
||||
#endif
|
||||
#else
|
||||
snprintf(buf2,sizeof(buf2),"distorm3 --b32 \"%s\"",buf);
|
||||
#endif
|
||||
#endif
|
||||
system(buf2);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (doExec) NSEEL_code_execute(code);
|
||||
if (canfree) NSEEL_code_free(code);
|
||||
else m_code_freelist.Add((void*)code);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
FILE *eelscript_resolvePath(WDL_FastString &usefn, const char *fn, const char *callerfn)
|
||||
{
|
||||
// resolve path relative to current
|
||||
int x;
|
||||
bool had_abs=false;
|
||||
for (x=0;x<2; x ++)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (!x && ((fn[0] == '\\' && fn[1] == '\\') || (fn[0] && fn[1] == ':')))
|
||||
#else
|
||||
if (!x && fn[0] == '/')
|
||||
#endif
|
||||
{
|
||||
usefn.Set(fn);
|
||||
had_abs=true;
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *fnu = fn;
|
||||
if (x)
|
||||
{
|
||||
while (*fnu) fnu++;
|
||||
while (fnu >= fn && *fnu != '\\' && *fnu != '/') fnu--;
|
||||
if (fnu < fn) break;
|
||||
fnu++;
|
||||
}
|
||||
|
||||
usefn.Set(callerfn);
|
||||
int l=usefn.GetLength();
|
||||
while (l > 0 && usefn.Get()[l-1] != '\\' && usefn.Get()[l-1] != '/') l--;
|
||||
if (l > 0)
|
||||
{
|
||||
usefn.SetLen(l);
|
||||
usefn.Append(fnu);
|
||||
}
|
||||
else
|
||||
{
|
||||
usefn.Set(fnu);
|
||||
}
|
||||
int last_slash_pos=-1;
|
||||
for (l = 0; l < usefn.GetLength(); l ++)
|
||||
{
|
||||
if (usefn.Get()[l] == '/' || usefn.Get()[l] == '\\')
|
||||
{
|
||||
if (usefn.Get()[l+1] == '.' && usefn.Get()[l+2] == '.' &&
|
||||
(usefn.Get()[l+3] == '/' || usefn.Get()[l+3] == '\\'))
|
||||
{
|
||||
if (last_slash_pos >= 0)
|
||||
usefn.DeleteSub(last_slash_pos, l+3-last_slash_pos);
|
||||
else
|
||||
usefn.DeleteSub(0,l+3+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
last_slash_pos=l;
|
||||
}
|
||||
}
|
||||
// take currentfn, remove filename part, add fnu
|
||||
}
|
||||
}
|
||||
|
||||
FILE *fp = fopenUTF8(usefn.Get(),"r");
|
||||
if (fp) return fp;
|
||||
}
|
||||
if (had_abs) usefn.Set(fn);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int eelScriptInst::loadfile(const char *fn, const char *callerfn, bool allowstdin)
|
||||
{
|
||||
WDL_FastString usefn;
|
||||
FILE *fp = NULL;
|
||||
if (!strcmp(fn,"-"))
|
||||
{
|
||||
if (callerfn)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
EEL_STRING_DEBUGOUT("@import: can't import \"-\" (stdin)");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
if (allowstdin)
|
||||
{
|
||||
fp = stdin;
|
||||
fn = "(stdin)";
|
||||
}
|
||||
}
|
||||
else if (!callerfn)
|
||||
{
|
||||
fp = fopenUTF8(fn,"r");
|
||||
if (fp) m_loaded_fnlist.Insert(fn,true);
|
||||
}
|
||||
else
|
||||
{
|
||||
fp = eelscript_resolvePath(usefn,fn,callerfn);
|
||||
if (fp)
|
||||
{
|
||||
if (m_loaded_fnlist.Get(usefn.Get()))
|
||||
{
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
m_loaded_fnlist.Insert(usefn.Get(),true);
|
||||
fn = usefn.Get();
|
||||
}
|
||||
}
|
||||
|
||||
if (!fp)
|
||||
{
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
if (callerfn)
|
||||
EEL_STRING_DEBUGOUT("Warning: @import could not open '%s'",fn);
|
||||
else
|
||||
EEL_STRING_DEBUGOUT("Error opening %s",fn);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
WDL_FastString incpath(fn);
|
||||
incpath.remove_filepart();
|
||||
m_preproc.m_include_paths.Add(incpath.Get());
|
||||
#endif
|
||||
|
||||
WDL_FastString code;
|
||||
char line[4096];
|
||||
for (;;)
|
||||
{
|
||||
line[0]=0;
|
||||
fgets(line,sizeof(line),fp);
|
||||
if (!line[0]) break;
|
||||
if (!strnicmp(line,"@import",7) && isspace_safe(line[7]))
|
||||
{
|
||||
char *p=line+7;
|
||||
while (isspace_safe(*p)) p++;
|
||||
|
||||
char *ep=p;
|
||||
while (*ep) ep++;
|
||||
while (ep>p && isspace_safe(ep[-1])) ep--;
|
||||
*ep=0;
|
||||
|
||||
if (*p) loadfile(p,fn,false);
|
||||
}
|
||||
else
|
||||
{
|
||||
code.Append(line);
|
||||
}
|
||||
}
|
||||
if (fp != stdin) fclose(fp);
|
||||
|
||||
int rv = runcode(code.Get(),callerfn ? 2 : 1, fn,false,true,!callerfn);
|
||||
|
||||
#ifndef EELSCRIPT_NO_PREPROC
|
||||
m_preproc.m_include_paths.Delete(m_preproc.m_include_paths.GetSize()-1);
|
||||
#endif
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
char *eelScriptInst::evalCacheGet(const char *str, NSEEL_CODEHANDLE *ch)
|
||||
{
|
||||
// should mutex protect if multiple threads access this eelScriptInst context
|
||||
int x=m_eval_cache.GetSize();
|
||||
while (--x >= 0)
|
||||
{
|
||||
char *ret;
|
||||
if (!strcmp(ret=m_eval_cache.Get()[x].str, str))
|
||||
{
|
||||
*ch = m_eval_cache.Get()[x].ch;
|
||||
m_eval_cache.Delete(x);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void eelScriptInst::evalCacheDispose(char *key, NSEEL_CODEHANDLE ch)
|
||||
{
|
||||
// should mutex protect if multiple threads access this eelScriptInst context
|
||||
evalCacheEnt ecc;
|
||||
ecc.str= key;
|
||||
ecc.ch = ch;
|
||||
if (m_eval_cache.GetSize() > 1024)
|
||||
{
|
||||
NSEEL_code_free(m_eval_cache.Get()->ch);
|
||||
free(m_eval_cache.Get()->str);
|
||||
m_eval_cache.Delete(0);
|
||||
}
|
||||
m_eval_cache.Add(ecc);
|
||||
}
|
||||
|
||||
int eelScriptInst::init()
|
||||
{
|
||||
EEL_string_register();
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
EEL_file_register();
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_FFT
|
||||
EEL_fft_register();
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_MDCT
|
||||
EEL_mdct_register();
|
||||
#endif
|
||||
EEL_misc_register();
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
EEL_eval_register();
|
||||
NSEEL_addfunc_retval("defer",1,NSEEL_PProc_THIS,&_eel_defer);
|
||||
NSEEL_addfunc_retval("runloop", 1, NSEEL_PProc_THIS, &_eel_defer);
|
||||
NSEEL_addfunc_retval("atexit",1,NSEEL_PProc_THIS,&_eel_atexit);
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
EEL_tcp_register();
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
eel_lice_register();
|
||||
#ifdef _WIN32
|
||||
eel_lice_register_standalone(GetModuleHandle(NULL),EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
|
||||
#else
|
||||
eel_lice_register_standalone(NULL,EELSCRIPT_LICE_CLASSNAME,NULL,NULL);
|
||||
#endif
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool eelScriptInst::has_deferred()
|
||||
{
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
return m_defer_eval.Available() && m_vm;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
void eelScriptInst::runCodeQ(WDL_Queue *q, const char *callername)
|
||||
{
|
||||
const int endptr = q->Available();
|
||||
int offs = 0;
|
||||
while (offs < endptr)
|
||||
{
|
||||
if (q->Available() < endptr) break; // should never happen, but safety first!
|
||||
|
||||
const char *ptr = (const char *)q->Get() + offs;
|
||||
offs += strlen(ptr)+1;
|
||||
|
||||
NSEEL_CODEHANDLE ch=NULL;
|
||||
char *sv=evalCacheGet(ptr,&ch);
|
||||
|
||||
if (!sv) sv=strdup(ptr);
|
||||
if (!ch) ch=NSEEL_code_compile(m_vm,sv,0);
|
||||
if (!ch)
|
||||
{
|
||||
free(sv);
|
||||
#ifdef EEL_STRING_DEBUGOUT
|
||||
const char *err = NSEEL_code_getcodeerror(m_vm);
|
||||
if (err) EEL_STRING_DEBUGOUT("%s: error in code: %s",callername,err);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
NSEEL_code_execute(ch);
|
||||
evalCacheDispose(sv,ch);
|
||||
}
|
||||
}
|
||||
q->Advance(endptr);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool eelScriptInst::run_deferred()
|
||||
{
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
if (!m_defer_eval.Available()||!m_vm) return false;
|
||||
|
||||
runCodeQ(&m_defer_eval,"defer");
|
||||
m_defer_eval.Compact();
|
||||
return m_defer_eval.Available()>0;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef EEL_WANT_DOCUMENTATION
|
||||
#include "ns-eel-func-ref.h"
|
||||
|
||||
void EELScript_GenerateFunctionList(WDL_PtrList<const char> *fs)
|
||||
{
|
||||
const char *p = nseel_builtin_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
p = eel_strings_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
p = eel_misc_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#ifndef EELSCRIPT_NO_EVAL
|
||||
fs->Add("atexit\t\"code\"\t"
|
||||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
||||
"Adds code to be executed when the script finishes."
|
||||
#endif
|
||||
);
|
||||
fs->Add("defer\t\"code\"\t"
|
||||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
||||
"Adds code which will be executed some small amount of time after the current code finishes. Identical to runloop()"
|
||||
#endif
|
||||
);
|
||||
fs->Add("runloop\t\"code\"\t"
|
||||
#ifndef EELSCRIPT_HELP_NO_DEFER_DESC
|
||||
"Adds code which will be executed some small amount of time after the current code finishes. Identical to defer()"
|
||||
#endif
|
||||
);
|
||||
|
||||
p = eel_eval_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_NET
|
||||
p = eel_net_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_FFT
|
||||
p = eel_fft_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_FILE
|
||||
p = eel_file_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_MDCT
|
||||
p = eel_mdct_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
#ifndef EELSCRIPT_NO_LICE
|
||||
p = eel_lice_function_reference;
|
||||
while (*p) { fs->Add(p); p += strlen(p) + 1; }
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#undef opaque
|
||||
@@ -0,0 +1,448 @@
|
||||
#ifndef _NSEEL_GLUE_AARCH64_H_
|
||||
#define _NSEEL_GLUE_AARCH64_H_
|
||||
|
||||
#define GLUE_MOD_IS_64
|
||||
|
||||
// x0=return value, first parm, x1-x2 parms (x3-x7 more params)
|
||||
// x8 return struct?
|
||||
// x9-x15 temporary
|
||||
// x16-x17 = PLT, linker
|
||||
// x18 reserved (TLS)
|
||||
// x19-x28 callee-saved
|
||||
// x19 = worktable
|
||||
// x20 = ramtable
|
||||
// x21 = consttab
|
||||
// x22 = worktable ptr
|
||||
// x23-x28 spare
|
||||
// x29 frame pointer
|
||||
// x30 link register
|
||||
// x31 SP/zero
|
||||
|
||||
// x0=p1
|
||||
// x1=p2
|
||||
// x2=p3
|
||||
|
||||
// d0 is return value for fp?
|
||||
// d/v/f0-7 = arguments/results
|
||||
// 8-15 callee saved
|
||||
// 16-31 temporary
|
||||
|
||||
// v8-v15 spill registers
|
||||
#define GLUE_MAX_SPILL_REGS 8
|
||||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4)
|
||||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4)
|
||||
|
||||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws)
|
||||
{
|
||||
*(unsigned int *)b = 0x1e604101 + (ws<<5); // fmov d1, d8+ws
|
||||
}
|
||||
static void GLUE_SAVE_TO_SPILL(void *b, int ws)
|
||||
{
|
||||
*(unsigned int *)b = 0x1e604008 + ws; // fmov d8+ws, d0
|
||||
}
|
||||
|
||||
|
||||
#define GLUE_HAS_FPREG2 1
|
||||
|
||||
static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = { 0x1e604001 }; // fmov d1, d0
|
||||
static unsigned int GLUE_POP_STACK_TO_FPREG2[] = {
|
||||
0xfc4107e1 // ldr d1, [sp], #16
|
||||
};
|
||||
|
||||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support
|
||||
#define GLUE_MAX_JMPSIZE ((1<<20) - 1024) // maximum relative jump size
|
||||
|
||||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction.
|
||||
// 0 = current instruction
|
||||
static void GLUE_JMP_SET_OFFSET(void *endOfInstruction, int offset)
|
||||
{
|
||||
unsigned int *a = (unsigned int*) endOfInstruction - 1;
|
||||
offset += 4;
|
||||
offset >>= 2; // as dwords
|
||||
if ((a[0] & 0xFC000000) == 0x14000000)
|
||||
{
|
||||
// NC b = 0x14 + 26 bit offset
|
||||
a[0] = 0x14000000 | (offset & 0x3FFFFFF);
|
||||
}
|
||||
else if ((a[0] & 0xFF000000) == 0x54000000)
|
||||
{
|
||||
// condb = 0x54 + 20 bit offset + 5 bit condition: 0=eq, 1=ne, b=lt, c=gt, d=le, a=ge
|
||||
a[0] = 0x54000000 | (a[0] & 0xF) | ((offset & 0x7FFFF) << 5);
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned int GLUE_JMP_NC[] = { 0x14000000 };
|
||||
|
||||
static const unsigned int GLUE_JMP_IF_P1_Z[]=
|
||||
{
|
||||
0x7100001f, // cmp w0, #0
|
||||
0x54000000, // b.eq
|
||||
};
|
||||
static const unsigned int GLUE_JMP_IF_P1_NZ[]=
|
||||
{
|
||||
0x7100001f, // cmp w0, #0
|
||||
0x54000001, // b.ne
|
||||
};
|
||||
|
||||
#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 16 // wr=-2, sets d1
|
||||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 12
|
||||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv)
|
||||
{
|
||||
static const unsigned int tab[3] = {
|
||||
0xd2800000, // mov x0, #0000 (val<<5) | reg
|
||||
0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg
|
||||
0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg
|
||||
};
|
||||
// 0xABAAA, B is register, A are bits of word
|
||||
unsigned int *p=(unsigned int *)b;
|
||||
int wvo = wv;
|
||||
if (wv<0) wv=0;
|
||||
p[0] = tab[0] | wv | ((v&0xFFFF)<<5);
|
||||
p[1] = tab[1] | wv | (((v>>16)&0xFFFF)<<5);
|
||||
p[2] = tab[2] | wv | (((v>>32)&0xFFFF)<<5);
|
||||
if (wvo == -2) p[3] = 0xfd400001; // ldr d1, [x0]
|
||||
}
|
||||
|
||||
const static unsigned int GLUE_FUNC_ENTER[2] = { 0xa9bf7bfd, 0x910003fd }; // stp x29, x30, [sp, #-16]! ; mov x29, sp
|
||||
#define GLUE_FUNC_ENTER_SIZE 4
|
||||
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop
|
||||
#define GLUE_FUNC_LEAVE_SIZE 0
|
||||
const static unsigned int GLUE_RET[]={ 0xa8c17bfd, 0xd65f03c0 }; // ldp x29,x30, [sp], #16 ; ret
|
||||
|
||||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr)
|
||||
{
|
||||
const static unsigned int GLUE_SET_WTP_FROM_R19 = 0xaa1303f6; // mov r22, r19
|
||||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R19,sizeof(GLUE_SET_WTP_FROM_R19));
|
||||
return 4;
|
||||
}
|
||||
|
||||
|
||||
const static unsigned int GLUE_PUSH_P1[1]={ 0xf81f0fe0 }; // str x0, [sp, #-16]!
|
||||
|
||||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(offs) ((offs)>=32768 ? 8 : 4)
|
||||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs)
|
||||
{
|
||||
if (offs >= 32768)
|
||||
{
|
||||
// add x1, sp, (offs/4096) lsl 12
|
||||
*(unsigned int *)b = 0x914003e1 + ((offs>>12)<<10);
|
||||
|
||||
// str x0, [x1, #offs & 4095]
|
||||
offs &= 4095;
|
||||
offs <<= 10-3;
|
||||
offs &= 0x7FFC00;
|
||||
((unsigned int *)b)[1] = 0xf9000020 + offs;
|
||||
}
|
||||
else
|
||||
{
|
||||
// str x0, [sp, #offs]
|
||||
offs <<= 10-3;
|
||||
offs &= 0x7FFC00;
|
||||
*(unsigned int *)b = 0xf90003e0 + offs;
|
||||
}
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4
|
||||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv)
|
||||
{
|
||||
// mov xX, sp
|
||||
*(unsigned int *)b = 0x910003e0 + wv;
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_STACK_SIZE 4
|
||||
static void GLUE_MOVE_STACK(void *b, int amt)
|
||||
{
|
||||
if (amt>=0)
|
||||
{
|
||||
if (amt >= 4096)
|
||||
*(unsigned int*)b = 0x914003ff | (((amt+4095)>>12)<<10);
|
||||
else
|
||||
*(unsigned int*)b = 0x910003ff | (amt << 10);
|
||||
}
|
||||
else
|
||||
{
|
||||
amt = -amt;
|
||||
if (amt >= 4096)
|
||||
*(unsigned int*)b = 0xd14003ff | (((amt+4095)>>12)<<10);
|
||||
else
|
||||
*(unsigned int*)b = 0xd10003ff | (amt << 10);
|
||||
}
|
||||
}
|
||||
|
||||
#define GLUE_POP_PX_SIZE 4
|
||||
static void GLUE_POP_PX(void *b, int wv)
|
||||
{
|
||||
((unsigned int *)b)[0] = 0xf84107e0 | wv; // ldr x, [sp], 16
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_P1_SIZE 4
|
||||
static void GLUE_SET_PX_FROM_P1(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xaa0003e0 | wv;
|
||||
}
|
||||
|
||||
|
||||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] =
|
||||
{
|
||||
0xfd400007, // ldr d7, [x0]
|
||||
0xfc1f0fe7, // str d7, [sp, #-16]!
|
||||
};
|
||||
|
||||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xfc4107e7; // ldr d7, [sp], #16
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xfd000007; // str d7, [x0]
|
||||
}
|
||||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
|
||||
}
|
||||
|
||||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xfd400007; // ldr d7, [x0]
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xfd000007; // str d7, [x0]
|
||||
}
|
||||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
#define GLUE_CALL_CODE(bp, cp, rt) do { \
|
||||
GLUE_SCR_TYPE f; \
|
||||
static const double consttab[] = { \
|
||||
NSEEL_CLOSEFACTOR, \
|
||||
0.0, \
|
||||
1.0, \
|
||||
-1.0, \
|
||||
-0.5, /* for invsqrt */ \
|
||||
1.5, \
|
||||
}; \
|
||||
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \
|
||||
!((f=glue_getscr())&(1<<24))) { \
|
||||
glue_setscr(f|(1<<24)); \
|
||||
eel_callcode64(bp, cp, rt, (void *)consttab); \
|
||||
glue_setscr(f); \
|
||||
} else eel_callcode64(bp, cp, rt, (void *)consttab);\
|
||||
} while(0)
|
||||
|
||||
#ifndef _MSC_VER
|
||||
static void eel_callcode64(INT_PTR bp, INT_PTR cp, INT_PTR rt, void *consttab)
|
||||
{
|
||||
__asm__(
|
||||
"mov x1, %2\n"
|
||||
"mov x2, %3\n"
|
||||
"mov x3, %1\n"
|
||||
"mov x0, %0\n"
|
||||
"stp x29, x30, [sp, #-64]!\n"
|
||||
"stp x18, x20, [sp, 16]\n"
|
||||
"stp x21, x19, [sp, 32]\n"
|
||||
"stp x22, x23, [sp, 48]\n"
|
||||
"mov x29, sp\n"
|
||||
"mov x19, x3\n"
|
||||
"mov x20, x1\n"
|
||||
"mov x21, x2\n"
|
||||
"blr x0\n"
|
||||
"ldp x29, x30, [sp], 16\n"
|
||||
"ldp x18, x20, [sp], 16\n"
|
||||
"ldp x21, x19, [sp], 16\n"
|
||||
"ldp x22, x23, [sp], 16\n"
|
||||
::"r" (cp), "r" (bp), "r" (rt), "r" (consttab) :"x0","x1","x2","x3","x4","x5","x6","x7",
|
||||
"x8","x9","x10","x11","x12","x13","x14","x15",
|
||||
"v8","v9","v10","v11","v12","v13","v14","v15");
|
||||
|
||||
};
|
||||
#else
|
||||
void eel_callcode64(INT_PTR bp, INT_PTR cp, INT_PTR rt, void *consttab);
|
||||
#endif
|
||||
|
||||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv)
|
||||
{
|
||||
unsigned int *p=(unsigned int *)_p;
|
||||
WDL_ASSERT(!(newv>>48));
|
||||
// 0xd2800000, // mov x0, #0000 (val<<5) | reg
|
||||
// 0xf2a00000, // movk x0, #0000, lsl 16 (val<<5) | reg
|
||||
// 0xf2c00000, // movk x0, #0000, lsl 32 (val<<5) | reg
|
||||
while (((p[0]>>5)&0xffff)!=0xdead ||
|
||||
((p[1]>>5)&0xffff)!=0xbeef ||
|
||||
((p[2]>>5)&0xffff)!=0xbeef) p++;
|
||||
|
||||
p[0] = (p[0] & 0xFFE0001F) | ((newv&0xffff)<<5);
|
||||
p[1] = (p[1] & 0xFFE0001F) | (((newv>>16)&0xffff)<<5);
|
||||
p[2] = (p[2] & 0xFFE0001F) | (((newv>>32)&0xffff)<<5);
|
||||
|
||||
return (unsigned char *)(p+2);
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int)
|
||||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xaa1603e0 + wv; // mov x, x22
|
||||
}
|
||||
|
||||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
|
||||
*bufptr++ = 0xfd000000; // str d0, [x0]
|
||||
}
|
||||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int);
|
||||
}
|
||||
|
||||
#define GLUE_POP_FPSTACK_SIZE 0
|
||||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = {
|
||||
0xfc1f0fe0, // str d0, [sp, #-16]!
|
||||
|
||||
};
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = {
|
||||
0xfc0086c0, // str d0, [x22], #8
|
||||
};
|
||||
|
||||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4
|
||||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xfd400000 + (wv<<5); // ldr d0, [xX]
|
||||
}
|
||||
|
||||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE)
|
||||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv)
|
||||
{
|
||||
GLUE_SET_PX_FROM_WTP(buf,wv);
|
||||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP));
|
||||
};
|
||||
|
||||
static const unsigned int GLUE_SET_P1_Z[] = { 0x52800000 }; // mov w0, #0
|
||||
static const unsigned int GLUE_SET_P1_NZ[] = { 0x52800020 }; // mov w0, #1
|
||||
|
||||
|
||||
static void *GLUE_realAddress(void *fn, int *size)
|
||||
{
|
||||
while ((*(int*)fn & 0xFC000000) == 0x14000000)
|
||||
{
|
||||
int offset = (*(int*)fn & 0x3FFFFFF);
|
||||
if (offset & 0x2000000)
|
||||
offset |= 0xFC000000;
|
||||
|
||||
fn = (int*)fn + offset;
|
||||
}
|
||||
static const unsigned int sig[] = {
|
||||
#ifndef _MSC_VER
|
||||
0xaa0003e0,
|
||||
#endif
|
||||
0xaa0103e1,
|
||||
#ifndef _MSC_VER
|
||||
0xaa0203e2
|
||||
#endif
|
||||
};
|
||||
unsigned char *p = (unsigned char *)fn;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
p+=sizeof(sig);
|
||||
fn = p;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
*size = p - (unsigned char *)fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define GLUE_SCR_TYPE unsigned long
|
||||
static unsigned long __attribute__((unused)) glue_getscr()
|
||||
{
|
||||
unsigned long rv;
|
||||
asm volatile ( "mrs %0, fpcr" : "=r" (rv));
|
||||
return rv;
|
||||
}
|
||||
static void __attribute__((unused)) glue_setscr(unsigned long v)
|
||||
{
|
||||
asm volatile ( "msr fpcr, %0" :: "r"(v));
|
||||
}
|
||||
#else
|
||||
#define GLUE_SCR_TYPE unsigned long long
|
||||
GLUE_SCR_TYPE glue_getscr();
|
||||
void glue_setscr(unsigned long long);
|
||||
#endif
|
||||
|
||||
void eel_enterfp(int _s[2])
|
||||
{
|
||||
GLUE_SCR_TYPE *s = (GLUE_SCR_TYPE*)_s;
|
||||
s[0] = glue_getscr();
|
||||
glue_setscr(s[0] | (1<<24));
|
||||
}
|
||||
void eel_leavefp(int _s[2])
|
||||
{
|
||||
const GLUE_SCR_TYPE *s = (GLUE_SCR_TYPE*)_s;
|
||||
glue_setscr(s[0]);
|
||||
}
|
||||
|
||||
#define GLUE_HAS_FUSE 1
|
||||
static int GLUE_FUSE(compileContext *ctx, unsigned char *code, int left_size, int right_size, int fuse_flags, int spill_reg)
|
||||
{
|
||||
if (left_size>=4 && right_size == 4)
|
||||
{
|
||||
unsigned int instr = ((unsigned int *)code)[-1];
|
||||
if (spill_reg >= 0 && (instr & 0xfffffc1f) == 0x1e604001) // fmov d1, dX
|
||||
{
|
||||
const int src_reg = (instr>>5)&0x1f;
|
||||
if (src_reg == spill_reg + 8)
|
||||
{
|
||||
instr = ((unsigned int *)code)[0];
|
||||
if ((instr & 0xffffcfff) == 0x1e600820)
|
||||
{
|
||||
((unsigned int *)code)[-1] = instr + ((src_reg-1) << 5);
|
||||
return -4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef _M_ARM64EC
|
||||
#define DEF_F1(n) static double eel_##n(double a) { return n(a); }
|
||||
#define DEF_F2(n) static double eel_##n(double a, double b) { return n(a,b); }
|
||||
DEF_F1(cos)
|
||||
#define cos eel_cos
|
||||
DEF_F1(sin)
|
||||
#define sin eel_sin
|
||||
DEF_F1(tan)
|
||||
#define tan eel_tan
|
||||
DEF_F1(log)
|
||||
#define log eel_log
|
||||
DEF_F1(log10)
|
||||
#define log10 eel_log10
|
||||
DEF_F1(acos)
|
||||
#define acos eel_acos
|
||||
DEF_F1(asin)
|
||||
#define asin eel_asin
|
||||
DEF_F1(atan)
|
||||
#define atan eel_atan
|
||||
DEF_F1(exp)
|
||||
#define exp eel_exp
|
||||
DEF_F2(pow)
|
||||
#define pow eel_pow
|
||||
DEF_F2(atan2)
|
||||
#define atan2 eel_atan2
|
||||
// ceil and floor will be wrapped by defs in nseel-compiler.c
|
||||
|
||||
#pragma comment(lib,"onecore.lib")
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,335 @@
|
||||
#ifndef _NSEEL_GLUE_ARM_H_
|
||||
#define _NSEEL_GLUE_ARM_H_
|
||||
|
||||
// r0=return value, first parm, r1-r2 parms
|
||||
// r3+ should be reserved
|
||||
// blx addr
|
||||
// stmfd sp!, {register list, lr}
|
||||
// ldmfd sp!, {register list, pc}
|
||||
|
||||
// let's make r8 = worktable
|
||||
// let's make r7 = ramtable
|
||||
// r6 = consttab
|
||||
// r5 = worktable ptr
|
||||
|
||||
// r0=p1
|
||||
// r1=p2
|
||||
// r2=p3
|
||||
|
||||
// d0 is return value?
|
||||
|
||||
|
||||
#define GLUE_HAS_FPREG2 1
|
||||
|
||||
static const unsigned int GLUE_COPY_FPSTACK_TO_FPREG2[] = {
|
||||
0xeeb01b40 // fcpyd d1, d0
|
||||
};
|
||||
|
||||
static unsigned int GLUE_POP_STACK_TO_FPREG2[] = {
|
||||
0xed9d1b00,// vldr d1, [sp]
|
||||
0xe28dd008,// add sp, sp, #8
|
||||
};
|
||||
|
||||
#define GLUE_MAX_SPILL_REGS 8
|
||||
#define GLUE_SAVE_TO_SPILL_SIZE(x) (4)
|
||||
#define GLUE_RESTORE_SPILL_TO_FPREG2_SIZE(x) (4)
|
||||
|
||||
static void GLUE_RESTORE_SPILL_TO_FPREG2(void *b, int ws)
|
||||
{
|
||||
*(unsigned int *)b = 0xeeb01b48 + ws; // fcpyd d1, d8+ws
|
||||
}
|
||||
static void GLUE_SAVE_TO_SPILL(void *b, int ws)
|
||||
{
|
||||
*(unsigned int *)b = 0xeeb08b40 + (ws<<12); // fcpyd d8+ws, d0
|
||||
}
|
||||
|
||||
|
||||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support
|
||||
#define GLUE_MAX_JMPSIZE ((1<<25) - 1024) // maximum relative jump size
|
||||
|
||||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction.
|
||||
// TODO: verify, but offset probably from next instruction (PC is ahead)
|
||||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((int *)(endOfInstruction))[-1] = (((int *)(endOfInstruction))[-1]&0xFF000000)|((((offset)>>2)-1)))
|
||||
|
||||
// /=conditional=always = 0xE
|
||||
// |/= 101(L), so 8+2+0 = 10 = A
|
||||
static const unsigned int GLUE_JMP_NC[] = { 0xEA000000 };
|
||||
|
||||
static const unsigned int GLUE_JMP_IF_P1_Z[]=
|
||||
{
|
||||
0xe1100000, // tst r0, r0
|
||||
0x0A000000, // branch if Z set
|
||||
};
|
||||
static const unsigned int GLUE_JMP_IF_P1_NZ[]=
|
||||
{
|
||||
0xe1100000, // tst r0, r0
|
||||
0x1A000000, // branch if Z clear
|
||||
};
|
||||
|
||||
#define GLUE_MOV_PX_DIRECTVALUE_TOFPREG2_SIZE 12 // wr=-2, sets d1
|
||||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8
|
||||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv)
|
||||
{
|
||||
// requires ARMv6thumb2 or later
|
||||
const unsigned int reg_add = wdl_max(wv,0) << 12;
|
||||
static const unsigned int tab[2] = {
|
||||
0xe3000000, // movw r0, #0000
|
||||
0xe3400000, // movt r0, #0000
|
||||
};
|
||||
// 0xABAAA, B is register, A are bits of word
|
||||
unsigned int *p=(unsigned int *)b;
|
||||
p[0] = tab[0] | reg_add | (v&0xfff) | ((v&0xf000)<<4);
|
||||
p[1] = tab[1] | reg_add | ((v>>16)&0xfff) | ((v&0xf0000000)>>12);
|
||||
if (wv == -2) p[2] = 0xed901b00; // fldd d1, [r0]
|
||||
}
|
||||
|
||||
const static unsigned int GLUE_FUNC_ENTER[1] = { 0xe92d4010 }; // push {r4, lr}
|
||||
#define GLUE_FUNC_ENTER_SIZE 4
|
||||
const static unsigned int GLUE_FUNC_LEAVE[1] = { 0 }; // let GLUE_RET pop
|
||||
#define GLUE_FUNC_LEAVE_SIZE 0
|
||||
const static unsigned int GLUE_RET[]={ 0xe8bd8010 }; // pop {r4, pc}
|
||||
|
||||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr)
|
||||
{
|
||||
const static unsigned int GLUE_SET_WTP_FROM_R8 = 0xe1a05008; // mov r5, r8
|
||||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R8,sizeof(GLUE_SET_WTP_FROM_R8));
|
||||
return sizeof(GLUE_SET_WTP_FROM_R8);
|
||||
}
|
||||
|
||||
|
||||
const static unsigned int GLUE_PUSH_P1[1]={ 0xe52d0008 }; // push {r0}, aligned to 8
|
||||
|
||||
|
||||
static int arm_encode_constforalu(int amt)
|
||||
{
|
||||
int nrot = 16;
|
||||
while (amt >= 0x100 && nrot > 1)
|
||||
{
|
||||
// ARM encodes integers for ALU operations as rotated right by third nibble*2
|
||||
amt = (amt + 3)>>2;
|
||||
nrot--;
|
||||
}
|
||||
return ((nrot&15) << 8) | amt;
|
||||
}
|
||||
|
||||
|
||||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) ((x)>=4096 ? 8 : 4)
|
||||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs)
|
||||
{
|
||||
if (offs >= 4096)
|
||||
{
|
||||
// add r2, sp, (offs&~4095)
|
||||
*(unsigned int *)b = 0xe28d2000 | arm_encode_constforalu(offs&~4095);
|
||||
// str r0, [r2, offs&4095]
|
||||
((unsigned int *)b)[1] = 0xe5820000 + (offs&4095);
|
||||
}
|
||||
else
|
||||
{
|
||||
// str r0, [sp, #offs]
|
||||
*(unsigned int *)b = 0xe58d0000 + offs;
|
||||
}
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4
|
||||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv)
|
||||
{
|
||||
// mov rX, sp
|
||||
*(unsigned int *)b = 0xe1a0000d + (wv<<12);
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_STACK_SIZE 4
|
||||
static void GLUE_MOVE_STACK(void *b, int amt)
|
||||
{
|
||||
unsigned int instr = 0xe28dd000;
|
||||
if (amt < 0)
|
||||
{
|
||||
instr = 0xe24dd000;
|
||||
amt=-amt;
|
||||
}
|
||||
*(unsigned int*)b = instr | arm_encode_constforalu(amt);
|
||||
}
|
||||
|
||||
#define GLUE_POP_PX_SIZE 4
|
||||
static void GLUE_POP_PX(void *b, int wv)
|
||||
{
|
||||
((unsigned int *)b)[0] = 0xe49d0008 | (wv<<12); // pop {rX}, aligned to 8
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_P1_SIZE 4
|
||||
static void GLUE_SET_PX_FROM_P1(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xe1a00000 | (wv<<12); // mov rX, r0
|
||||
}
|
||||
|
||||
|
||||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] =
|
||||
{
|
||||
0xed907b00, // fldd d7, [r0]
|
||||
0xe24dd008, // sub sp, sp, #8
|
||||
0xed8d7b00, // fstd d7, [sp]
|
||||
};
|
||||
|
||||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xed9d7b00; // fldd d7, [sp]
|
||||
*bufptr++ = 0xe28dd008; // add sp, sp, #8
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xed807b00; // fstd d7, [r0]
|
||||
}
|
||||
return 3*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
|
||||
}
|
||||
|
||||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xed907b00; // fldd d7, [r0]
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xed807b00; // fstd d7, [r0]
|
||||
}
|
||||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE;
|
||||
}
|
||||
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#define GLUE_CALL_CODE(bp, cp, rt) do { \
|
||||
unsigned int f; \
|
||||
if (!(h->compile_flags&NSEEL_CODE_COMPILE_FLAG_NOFPSTATE) && \
|
||||
!((f=glue_getscr())&(1<<24))) { \
|
||||
glue_setscr(f|(1<<24)); \
|
||||
eel_callcode32(bp, cp, rt); \
|
||||
glue_setscr(f); \
|
||||
} else eel_callcode32(bp, cp, rt);\
|
||||
} while(0)
|
||||
|
||||
static const double __consttab[] = {
|
||||
NSEEL_CLOSEFACTOR,
|
||||
0.0,
|
||||
1.0,
|
||||
-1.0,
|
||||
-0.5, // for invsqrt
|
||||
1.5,
|
||||
};
|
||||
|
||||
static void eel_callcode32(INT_PTR bp, INT_PTR cp, INT_PTR rt)
|
||||
{
|
||||
__asm__ volatile(
|
||||
"mov r7, %2\n"
|
||||
"mov r6, %3\n"
|
||||
"mov r8, %1\n"
|
||||
"mov r0, %0\n"
|
||||
"mov r1, sp\n"
|
||||
"bic sp, sp, #7\n"
|
||||
"push {r1, lr}\n"
|
||||
"blx r0\n"
|
||||
"pop {r1, lr}\n"
|
||||
"mov sp, r1\n"
|
||||
::"r" (cp), "r" (bp), "r" (rt), "r" (__consttab) :
|
||||
"r5", "r6", "r7", "r8", "r10",
|
||||
"d8","d9","d10","d11","d12","d13","d14","d15");
|
||||
};
|
||||
#endif
|
||||
|
||||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv)
|
||||
{
|
||||
unsigned int *p=(unsigned int *)_p;
|
||||
while ((p[0]&0x000F0FFF) != 0x000d0ead &&
|
||||
(p[1]&0x000F0FFF) != 0x000b0eef) p++;
|
||||
p[0] = (p[0]&0xFFF0F000) | (newv&0xFFF) | ((newv << 4) & 0xF0000);
|
||||
p[1] = (p[1]&0xFFF0F000) | ((newv>>16)&0xFFF) | ((newv >> 12)&0xF0000);
|
||||
|
||||
return (unsigned char *)(p+1);
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int)
|
||||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xe1a00005 + (wv<<12); // mov rX, r5
|
||||
}
|
||||
|
||||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
|
||||
*bufptr++ = 0xed800b00; // fstd d0, [r0]
|
||||
}
|
||||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int);
|
||||
}
|
||||
|
||||
#define GLUE_POP_FPSTACK_SIZE 0
|
||||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = {
|
||||
0xe24dd008, // sub sp, sp, #8
|
||||
0xed8d0b00, // fstd d0, [sp]
|
||||
};
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = {
|
||||
0xed850b00, // fstd d0, [r5]
|
||||
0xe2855008, // add r5, r5, #8
|
||||
};
|
||||
|
||||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4
|
||||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv)
|
||||
{
|
||||
*(unsigned int *)b = 0xed900b00 + (wv<<16); // fldd d0, [rX]
|
||||
}
|
||||
|
||||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE)
|
||||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv)
|
||||
{
|
||||
GLUE_SET_PX_FROM_WTP(buf,wv);
|
||||
memcpy(buf + GLUE_SET_PX_FROM_WTP_SIZE,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP));
|
||||
};
|
||||
|
||||
static const unsigned int GLUE_SET_P1_Z[] = { 0xe3a00000 }; // mov r0, #0
|
||||
static const unsigned int GLUE_SET_P1_NZ[] = { 0xe3a00001 }; // mov r0, #1
|
||||
|
||||
|
||||
static void *GLUE_realAddress(void *fn, int *size)
|
||||
{
|
||||
static const unsigned int sig[3] = { 0xe1a00000, 0xe1a01001, 0xe1a02002 };
|
||||
unsigned char *p = (unsigned char *)fn;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
p+=sizeof(sig);
|
||||
fn = p;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
*size = p - (unsigned char *)fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
static unsigned int __attribute__((unused)) glue_getscr()
|
||||
{
|
||||
unsigned int rv;
|
||||
asm volatile ( "fmrx %0, fpscr" : "=r" (rv));
|
||||
return rv;
|
||||
}
|
||||
static void __attribute__((unused)) glue_setscr(unsigned int v)
|
||||
{
|
||||
asm volatile ( "fmxr fpscr, %0" :: "r"(v));
|
||||
}
|
||||
|
||||
void eel_enterfp(int s[2])
|
||||
{
|
||||
s[0] = glue_getscr();
|
||||
glue_setscr(s[0] | (1<<24)); // could also do 3<<22 for RTZ
|
||||
}
|
||||
void eel_leavefp(int s[2])
|
||||
{
|
||||
glue_setscr(s[0]);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
+1029
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,285 @@
|
||||
#ifndef _NSEEL_GLUE_PPC_H_
|
||||
#define _NSEEL_GLUE_PPC_H_
|
||||
|
||||
#define GLUE_MAX_FPSTACK_SIZE 0 // no stack support
|
||||
#define GLUE_MAX_JMPSIZE 30000 // maximum relative jump size for this arch (if not defined, any jump is possible)
|
||||
|
||||
|
||||
// endOfInstruction is end of jump with relative offset, offset passed in is offset from end of dest instruction.
|
||||
// on PPC the offset needs to be from the start of the instruction (hence +4), and also the low two bits are flags so
|
||||
// we make sure they are clear (they should always be clear, anyway, since we always generate 4 byte instructions)
|
||||
#define GLUE_JMP_SET_OFFSET(endOfInstruction,offset) (((short *)(endOfInstruction))[-1] = ((offset) + 4) & 0xFFFC)
|
||||
|
||||
static const unsigned char GLUE_JMP_NC[] = { 0x48,0, 0, 0, }; // b <offset>
|
||||
|
||||
static const unsigned int GLUE_JMP_IF_P1_Z[]=
|
||||
{
|
||||
0x2f830000, //cmpwi cr7, r3, 0
|
||||
0x419e0000, // beq cr7, offset-bytes-from-startofthisinstruction
|
||||
};
|
||||
static const unsigned int GLUE_JMP_IF_P1_NZ[]=
|
||||
{
|
||||
0x2f830000, //cmpwi cr7, r3, 0
|
||||
0x409e0000, // bne cr7, offset-bytes-from-startofthisinstruction
|
||||
};
|
||||
|
||||
|
||||
#define GLUE_MOV_PX_DIRECTVALUE_SIZE 8
|
||||
static void GLUE_MOV_PX_DIRECTVALUE_GEN(void *b, INT_PTR v, int wv)
|
||||
{
|
||||
static const unsigned short tab[3][2] = {
|
||||
{0x3C60, 0x6063}, // addis r3, r0, hw -- ori r3,r3, lw
|
||||
{0x3DC0, 0x61CE}, // addis r14, r0, hw -- ori r14, r14, lw
|
||||
{0x3DE0, 0x61EF}, // addis r15, r0, hw -- oris r15, r15, lw
|
||||
};
|
||||
unsigned int uv=(unsigned int)v;
|
||||
unsigned short *p=(unsigned short *)b;
|
||||
|
||||
*p++ = tab[wv][0]; // addis rX, r0, hw
|
||||
*p++ = (uv>>16)&0xffff;
|
||||
*p++ = tab[wv][1]; // ori rX, rX, lw
|
||||
*p++ = uv&0xffff;
|
||||
}
|
||||
|
||||
|
||||
// mflr r5
|
||||
// stwu r5, -16(r1)
|
||||
const static unsigned int GLUE_FUNC_ENTER[2] = { 0x7CA802A6, 0x94A1FFF0 };
|
||||
#define GLUE_FUNC_ENTER_SIZE 8
|
||||
|
||||
// lwz r5, 0(r1)
|
||||
// addi r1, r1, 16
|
||||
// mtlr r5
|
||||
const static unsigned int GLUE_FUNC_LEAVE[3] = { 0x80A10000, 0x38210010, 0x7CA803A6 };
|
||||
#define GLUE_FUNC_LEAVE_SIZE 12
|
||||
|
||||
const static unsigned int GLUE_RET[]={0x4E800020}; // blr
|
||||
|
||||
static int GLUE_RESET_WTP(unsigned char *out, void *ptr)
|
||||
{
|
||||
const static unsigned int GLUE_SET_WTP_FROM_R17=0x7E308B78; // mr r16 (dest), r17 (src)
|
||||
if (out) memcpy(out,&GLUE_SET_WTP_FROM_R17,sizeof(GLUE_SET_WTP_FROM_R17));
|
||||
return sizeof(GLUE_SET_WTP_FROM_R17);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// stwu r3, -16(r1)
|
||||
const static unsigned int GLUE_PUSH_P1[1]={ 0x9461FFF0};
|
||||
|
||||
|
||||
#define GLUE_POP_PX_SIZE 8
|
||||
static void GLUE_POP_PX(void *b, int wv)
|
||||
{
|
||||
static const unsigned int tab[3] ={
|
||||
0x80610000, // lwz r3, 0(r1)
|
||||
0x81c10000, // lwz r14, 0(r1)
|
||||
0x81e10000, // lwz r15, 0(r1)
|
||||
};
|
||||
((unsigned int *)b)[0] = tab[wv];
|
||||
((unsigned int *)b)[1] = 0x38210010; // addi r1,r1, 16
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_P1_SIZE 4
|
||||
static void GLUE_SET_PX_FROM_P1(void *b, int wv)
|
||||
{
|
||||
static const unsigned int tab[3]={
|
||||
0x7c631b78, // never used: mr r3, r3
|
||||
0x7c6e1b78, // mr r14, r3
|
||||
0x7c6f1b78, // mr r15, r3
|
||||
};
|
||||
*(unsigned int *)b = tab[wv];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// lfd f2, 0(r3)
|
||||
// stfdu f2, -16(r1)
|
||||
static const unsigned int GLUE_PUSH_P1PTR_AS_VALUE[] = { 0xC8430000, 0xDC41FFF0 };
|
||||
|
||||
static int GLUE_POP_VALUE_TO_ADDR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
// lfd f2, 0(r1)
|
||||
// addi r1,r1,16
|
||||
// GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3)
|
||||
// stfd f2, 0(r3)
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xC8410000;
|
||||
*bufptr++ = 0x38210010;
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xd8430000;
|
||||
}
|
||||
return 2*4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4;
|
||||
}
|
||||
|
||||
static int GLUE_COPY_VALUE_AT_P1_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
// lfd f2, 0(r3)
|
||||
// GLUE_MOV_PX_DIRECTVALUE_GEN / GLUE_MOV_PX_DIRECTVALUE_SIZE (r3)
|
||||
// stfd f2, 0(r3)
|
||||
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
*bufptr++ = 0xc8430000;
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
*bufptr++ = 0xd8430000;
|
||||
}
|
||||
|
||||
return 4 + GLUE_MOV_PX_DIRECTVALUE_SIZE + 4;
|
||||
}
|
||||
|
||||
|
||||
static void GLUE_CALL_CODE(INT_PTR bp, INT_PTR cp, INT_PTR rt)
|
||||
{
|
||||
static const double consttab[] = {
|
||||
NSEEL_CLOSEFACTOR,
|
||||
4503601774854144.0 /* 0x43300000, 0x80000000, used for integer conversion*/,
|
||||
};
|
||||
// we could have r18 refer to the current user-stack pointer, someday, perhaps
|
||||
__asm__(
|
||||
"subi r1, r1, 128\n"
|
||||
"stfd f31, 8(r1)\n"
|
||||
"stfd f30, 16(r1)\n"
|
||||
"stmw r13, 32(r1)\n"
|
||||
"mtctr %0\n"
|
||||
"mr r17, %1\n"
|
||||
"mr r13, %2\n"
|
||||
"lfd f31, 0(%3)\n"
|
||||
"lfd f30, 8(%3)\n"
|
||||
"subi r17, r17, 8\n"
|
||||
"mflr r0\n"
|
||||
"stw r0, 24(r1)\n"
|
||||
"bctrl\n"
|
||||
"lwz r0, 24(r1)\n"
|
||||
"mtlr r0\n"
|
||||
"lmw r13, 32(r1)\n"
|
||||
"lfd f31, 8(r1)\n"
|
||||
"lfd f30, 16(r1)\n"
|
||||
"addi r1, r1, 128\n"
|
||||
::"r" (cp), "r" (bp), "r" (rt), "r" (consttab));
|
||||
};
|
||||
|
||||
static unsigned char *EEL_GLUE_set_immediate(void *_p, INT_PTR newv)
|
||||
{
|
||||
// 64 bit ppc would take some work
|
||||
unsigned int *p=(unsigned int *)_p;
|
||||
while ((p[0]&0x0000FFFF) != 0x0000dead &&
|
||||
(p[1]&0x0000FFFF) != 0x0000beef) p++;
|
||||
p[0] = (p[0]&0xFFFF0000) | (((newv)>>16)&0xFFFF);
|
||||
p[1] = (p[1]&0xFFFF0000) | ((newv)&0xFFFF);
|
||||
|
||||
return (unsigned char *)(p+1);
|
||||
}
|
||||
|
||||
#define GLUE_SET_PX_FROM_WTP_SIZE sizeof(int)
|
||||
static void GLUE_SET_PX_FROM_WTP(void *b, int wv)
|
||||
{
|
||||
static const unsigned int tab[3]={
|
||||
0x7e038378, // mr r3, r16
|
||||
0x7e0e8378, // mr r14, r16
|
||||
0x7e0f8378, // mr r15, r16
|
||||
};
|
||||
*(unsigned int *)b = tab[wv];
|
||||
}
|
||||
static int GLUE_POP_FPSTACK_TO_PTR(unsigned char *buf, void *destptr)
|
||||
{
|
||||
// set r3 to destptr
|
||||
// stfd f1, 0(r3)
|
||||
if (buf)
|
||||
{
|
||||
unsigned int *bufptr = (unsigned int *)buf;
|
||||
GLUE_MOV_PX_DIRECTVALUE_GEN(bufptr, (INT_PTR)destptr,0);
|
||||
bufptr += GLUE_MOV_PX_DIRECTVALUE_SIZE/4;
|
||||
|
||||
*bufptr++ = 0xD8230000; // stfd f1, 0(r3)
|
||||
}
|
||||
return GLUE_MOV_PX_DIRECTVALUE_SIZE + sizeof(int);
|
||||
}
|
||||
#define GLUE_POP_FPSTACK_SIZE 0
|
||||
static const unsigned int GLUE_POP_FPSTACK[1] = { 0 }; // no need to pop, not a stack
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TOSTACK[] = {
|
||||
0xdc21fff0, // stfdu f1, -16(r1)
|
||||
};
|
||||
|
||||
static const unsigned int GLUE_POP_FPSTACK_TO_WTP[] = {
|
||||
0xdc300008, // stfdu f1, 8(r16)
|
||||
};
|
||||
|
||||
#define GLUE_PUSH_VAL_AT_PX_TO_FPSTACK_SIZE 4
|
||||
static void GLUE_PUSH_VAL_AT_PX_TO_FPSTACK(void *b, int wv)
|
||||
{
|
||||
static const unsigned int tab[3] = {
|
||||
0xC8230000, // lfd f1, 0(r3)
|
||||
0xC82E0000, // lfd f1, 0(r14)
|
||||
0xC82F0000, // lfd f1, 0(r15)
|
||||
};
|
||||
*(unsigned int *)b = tab[wv];
|
||||
}
|
||||
|
||||
#define GLUE_POP_FPSTACK_TO_WTP_TO_PX_SIZE (sizeof(GLUE_POP_FPSTACK_TO_WTP) + GLUE_SET_PX_FROM_WTP_SIZE)
|
||||
static void GLUE_POP_FPSTACK_TO_WTP_TO_PX(unsigned char *buf, int wv)
|
||||
{
|
||||
memcpy(buf,GLUE_POP_FPSTACK_TO_WTP,sizeof(GLUE_POP_FPSTACK_TO_WTP));
|
||||
GLUE_SET_PX_FROM_WTP(buf + sizeof(GLUE_POP_FPSTACK_TO_WTP),wv); // ppc preincs the WTP, so we do this after
|
||||
};
|
||||
|
||||
static unsigned int GLUE_POP_STACK_TO_FPSTACK[1] = { 0 }; // todo
|
||||
|
||||
|
||||
static const unsigned int GLUE_SET_P1_Z[] = { 0x38600000 }; // li r3, 0
|
||||
static const unsigned int GLUE_SET_P1_NZ[] = { 0x38600001 }; // li r3, 1
|
||||
|
||||
|
||||
static void *GLUE_realAddress(void *fn, int *size)
|
||||
{
|
||||
// magic numbers: mr r0,r0 ; mr r1,r1 ; mr r2, r2
|
||||
static const unsigned char sig[12] = { 0x7c, 0x00, 0x03, 0x78, 0x7c, 0x21, 0x0b, 0x78, 0x7c, 0x42, 0x13, 0x78 };
|
||||
unsigned char *p = (unsigned char *)fn;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
p+=sizeof(sig);
|
||||
fn = p;
|
||||
|
||||
while (memcmp(p,sig,sizeof(sig))) p+=4;
|
||||
*size = p - (unsigned char *)fn;
|
||||
return fn;
|
||||
}
|
||||
|
||||
#define GLUE_STORE_P1_TO_STACK_AT_OFFS_SIZE(x) 4
|
||||
static void GLUE_STORE_P1_TO_STACK_AT_OFFS(void *b, int offs)
|
||||
{
|
||||
// limited to 32k offset
|
||||
*(unsigned int *)b = 0x90610000 + (offs&0xffff);
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_PX_STACKPTR_SIZE 4
|
||||
static void GLUE_MOVE_PX_STACKPTR_GEN(void *b, int wv)
|
||||
{
|
||||
static const unsigned int tab[3] =
|
||||
{
|
||||
0x7c230b78, // mr r3, r1
|
||||
0x7c2e0b78, // mr r14, r1
|
||||
0x7c2f0b78, // mr r15, r1
|
||||
};
|
||||
* (unsigned int *)b = tab[wv];
|
||||
}
|
||||
|
||||
#define GLUE_MOVE_STACK_SIZE 4
|
||||
static void GLUE_MOVE_STACK(void *b, int amt)
|
||||
{
|
||||
// this should be updated to allow for more than 32k moves, but no real need
|
||||
((unsigned int *)b)[0] = 0x38210000 + (amt&0xffff); // addi r1,r1, amt
|
||||
}
|
||||
|
||||
|
||||
|
||||
// end of ppc
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user