Skip to content

Commit dbea011

Browse files
feat: implement real-time DSP audio engine with C FFI for Flutter
- Phase-accumulator sine oscillator (f32, 48kHz) - Binaural beat generator (stereo L/R) with AM fallback - WAV rain/ambient sound loader with looping playback - Sample-accurate frequency cycle scheduler - Stereo mixer with atomic gain control - Real-time safe render path (zero alloc in audio callback) - C-compatible FFI: init, start, stop, render, gain, config update - Auto-generated C header via cbindgen - CI workflow (clippy, fmt, build, test) - Release workflow (Android, iOS, Linux artifacts + GitHub Release) - Comprehensive README with full API reference and usage examples Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent a0c665c commit dbea011

17 files changed

Lines changed: 3522 additions & 1 deletion

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
branches: [develop, main]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
RUSTFLAGS: "-Dwarnings"
12+
13+
jobs:
14+
lint:
15+
name: Lint
16+
runs-on: ubuntu-latest
17+
steps:
18+
- uses: actions/checkout@v4
19+
20+
- uses: dtolnay/rust-toolchain@stable
21+
with:
22+
components: clippy, rustfmt
23+
24+
- uses: Swatinem/rust-cache@v2
25+
26+
- name: Clippy
27+
run: cargo clippy --all-targets -- -D warnings
28+
29+
- name: Rustfmt
30+
run: cargo fmt --all -- --check
31+
32+
build-and-test:
33+
name: Build & Test
34+
runs-on: ubuntu-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
38+
- uses: dtolnay/rust-toolchain@stable
39+
40+
- uses: Swatinem/rust-cache@v2
41+
42+
- name: Build
43+
run: cargo build
44+
45+
- name: Run tests
46+
run: cargo test

.github/workflows/release.yml

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
branches:
6+
- "release/v*"
7+
8+
permissions:
9+
contents: write
10+
11+
env:
12+
CARGO_TERM_COLOR: always
13+
RUSTFLAGS: "-Dwarnings"
14+
15+
jobs:
16+
# ── Gate: run full CI first ──────────────────────────────────────────
17+
ci-gate:
18+
name: CI Gate
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: dtolnay/rust-toolchain@stable
24+
with:
25+
components: clippy, rustfmt
26+
27+
- uses: Swatinem/rust-cache@v2
28+
29+
- name: Clippy
30+
run: cargo clippy --all-targets -- -D warnings
31+
32+
- name: Rustfmt
33+
run: cargo fmt --all -- --check
34+
35+
- name: Build
36+
run: cargo build
37+
38+
- name: Test
39+
run: cargo test
40+
41+
# ── Extract version from branch name ─────────────────────────────────
42+
version:
43+
name: Extract Version
44+
runs-on: ubuntu-latest
45+
outputs:
46+
version: ${{ steps.extract.outputs.version }}
47+
version_bare: ${{ steps.extract.outputs.version_bare }}
48+
steps:
49+
- name: Extract version from branch
50+
id: extract
51+
run: |
52+
BRANCH="${GITHUB_REF#refs/heads/}"
53+
VERSION="${BRANCH#release/}"
54+
VERSION_BARE="${VERSION#v}"
55+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
56+
echo "version_bare=${VERSION_BARE}" >> "$GITHUB_OUTPUT"
57+
echo "Branch: ${BRANCH}"
58+
echo "Version: ${VERSION}"
59+
echo "Version (bare): ${VERSION_BARE}"
60+
61+
# ── Build Android .so files ──────────────────────────────────────────
62+
build-android:
63+
name: Build Android
64+
needs: [ci-gate, version]
65+
runs-on: ubuntu-latest
66+
steps:
67+
- uses: actions/checkout@v4
68+
69+
- uses: dtolnay/rust-toolchain@stable
70+
with:
71+
targets: aarch64-linux-android,armv7-linux-androideabi,x86_64-linux-android
72+
73+
- uses: Swatinem/rust-cache@v2
74+
75+
- name: Install cargo-ndk
76+
run: cargo install cargo-ndk
77+
78+
- name: Setup Android NDK
79+
uses: android-actions/setup-android@v3
80+
81+
- name: Build Android targets
82+
run: |
83+
for target in aarch64-linux-android armv7-linux-androideabi x86_64-linux-android; do
84+
echo "Building for $target..."
85+
cargo ndk --target "$target" build --release
86+
done
87+
88+
- name: Collect jniLibs
89+
run: |
90+
mkdir -p artifacts/jniLibs/arm64-v8a
91+
mkdir -p artifacts/jniLibs/armeabi-v7a
92+
mkdir -p artifacts/jniLibs/x86_64
93+
cp target/aarch64-linux-android/release/libhexatune_dsp_ffi.so artifacts/jniLibs/arm64-v8a/
94+
cp target/armv7-linux-androideabi/release/libhexatune_dsp_ffi.so artifacts/jniLibs/armeabi-v7a/
95+
cp target/x86_64-linux-android/release/libhexatune_dsp_ffi.so artifacts/jniLibs/x86_64/
96+
97+
- name: Upload Android artifacts
98+
uses: actions/upload-artifact@v4
99+
with:
100+
name: android-libs
101+
path: artifacts/jniLibs/
102+
103+
# ── Build iOS .a files + XCFramework ─────────────────────────────────
104+
build-ios:
105+
name: Build iOS
106+
needs: [ci-gate, version]
107+
runs-on: macos-latest
108+
steps:
109+
- uses: actions/checkout@v4
110+
111+
- uses: dtolnay/rust-toolchain@stable
112+
with:
113+
targets: aarch64-apple-ios,aarch64-apple-ios-sim
114+
115+
- uses: Swatinem/rust-cache@v2
116+
117+
- name: Build iOS targets
118+
run: |
119+
cargo build --release --target aarch64-apple-ios
120+
cargo build --release --target aarch64-apple-ios-sim
121+
122+
- name: Create XCFramework
123+
run: |
124+
xcodebuild -create-xcframework \
125+
-library target/aarch64-apple-ios/release/libhexatune_dsp_ffi.a \
126+
-headers include/ \
127+
-library target/aarch64-apple-ios-sim/release/libhexatune_dsp_ffi.a \
128+
-headers include/ \
129+
-output artifacts/HexaTuneDsp.xcframework
130+
131+
- name: Upload iOS artifacts
132+
uses: actions/upload-artifact@v4
133+
with:
134+
name: ios-framework
135+
path: artifacts/HexaTuneDsp.xcframework/
136+
137+
# ── Build Linux host .so ─────────────────────────────────────────────
138+
build-linux:
139+
name: Build Linux
140+
needs: [ci-gate, version]
141+
runs-on: ubuntu-latest
142+
steps:
143+
- uses: actions/checkout@v4
144+
145+
- uses: dtolnay/rust-toolchain@stable
146+
147+
- uses: Swatinem/rust-cache@v2
148+
149+
- name: Build release
150+
run: cargo build --release
151+
152+
- name: Collect artifacts
153+
run: |
154+
mkdir -p artifacts
155+
cp target/release/libhexatune_dsp_ffi.so artifacts/
156+
cp target/release/libhexatune_dsp_ffi.a artifacts/
157+
cp include/hexatune_dsp_ffi.h artifacts/
158+
159+
- name: Upload Linux artifacts
160+
uses: actions/upload-artifact@v4
161+
with:
162+
name: linux-libs
163+
path: artifacts/
164+
165+
# ── Publish & Release ────────────────────────────────────────────────
166+
release:
167+
name: Create GitHub Release
168+
needs: [version, build-android, build-ios, build-linux]
169+
runs-on: ubuntu-latest
170+
steps:
171+
- uses: actions/checkout@v4
172+
with:
173+
fetch-depth: 0
174+
token: ${{ secrets.GITHUB_TOKEN }}
175+
176+
- uses: dtolnay/rust-toolchain@stable
177+
178+
- name: Update Cargo.toml version
179+
run: |
180+
VERSION_BARE="${{ needs.version.outputs.version_bare }}"
181+
echo "Updating version to ${VERSION_BARE}..."
182+
sed -i "s/^version = \".*\"/version = \"${VERSION_BARE}\"/" Cargo.toml
183+
echo "Version set to:"
184+
grep '^version' Cargo.toml
185+
186+
- name: Commit version bump
187+
run: |
188+
VERSION="${{ needs.version.outputs.version }}"
189+
VERSION_BARE="${{ needs.version.outputs.version_bare }}"
190+
git config user.name "github-actions[bot]"
191+
git config user.email "github-actions[bot]@users.noreply.github.com"
192+
git add Cargo.toml Cargo.lock
193+
git diff --cached --quiet || git commit -m "release: bump version to ${VERSION_BARE}"
194+
195+
- name: Download all artifacts
196+
uses: actions/download-artifact@v4
197+
with:
198+
path: dist/
199+
200+
- name: Package artifacts
201+
run: |
202+
VERSION="${{ needs.version.outputs.version }}"
203+
204+
# Android
205+
cd dist/android-libs
206+
tar czf "../hexatune-dsp-ffi-${VERSION}-android.tar.gz" .
207+
cd ../..
208+
209+
# iOS
210+
cd dist/ios-framework
211+
tar czf "../hexatune-dsp-ffi-${VERSION}-ios.tar.gz" .
212+
cd ../..
213+
214+
# Linux
215+
cd dist/linux-libs
216+
tar czf "../hexatune-dsp-ffi-${VERSION}-linux-x86_64.tar.gz" .
217+
cd ../..
218+
219+
ls -lh dist/*.tar.gz
220+
221+
- name: Generate changelog
222+
run: |
223+
VERSION="${{ needs.version.outputs.version }}"
224+
{
225+
echo "## ${VERSION}"
226+
echo ""
227+
echo "### Release Artifacts"
228+
echo ""
229+
echo "| Platform | File |"
230+
echo "|----------|------|"
231+
echo "| Android (arm64-v8a, armeabi-v7a, x86_64) | \`hexatune-dsp-ffi-${VERSION}-android.tar.gz\` |"
232+
echo "| iOS (XCFramework) | \`hexatune-dsp-ffi-${VERSION}-ios.tar.gz\` |"
233+
echo "| Linux (x86_64) | \`hexatune-dsp-ffi-${VERSION}-linux-x86_64.tar.gz\` |"
234+
echo ""
235+
echo "### Commits since last tag"
236+
echo ""
237+
PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
238+
if [ -n "$PREVIOUS_TAG" ]; then
239+
git log "${PREVIOUS_TAG}..HEAD" --oneline --no-merges | sed 's/^/- /'
240+
else
241+
git log --oneline --no-merges -20 | sed 's/^/- /'
242+
fi
243+
} > RELEASE_NOTES.md
244+
cat RELEASE_NOTES.md
245+
246+
- name: Create git tag and push
247+
run: |
248+
VERSION="${{ needs.version.outputs.version }}"
249+
git config user.name "github-actions[bot]"
250+
git config user.email "github-actions[bot]@users.noreply.github.com"
251+
git tag -a "${VERSION}" -m "Release ${VERSION}"
252+
git push origin HEAD "${VERSION}"
253+
254+
- name: Create GitHub Release
255+
uses: softprops/action-gh-release@v2
256+
with:
257+
tag_name: ${{ needs.version.outputs.version }}
258+
name: hexatune-dsp-ffi ${{ needs.version.outputs.version }}
259+
body_path: RELEASE_NOTES.md
260+
files: dist/*.tar.gz
261+
draft: false
262+
prerelease: false

AGENTS.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# AGENTS.md
2+
3+
> **This file defines mandatory rules for all AI agents working in this repository.**
4+
> Violations will result in rejected PRs and reverted changes.
5+
6+
---
7+
8+
## 🚨 ABSOLUTE REQUIREMENT: English Language Only
9+
10+
**This is the most important rule and must NEVER be violated.**
11+
12+
Everything in the project directory MUST be in English — without exception:
13+
14+
- Code, comments, documentation, commit messages
15+
- Variable names, function names, type names, file names
16+
- Error messages, log messages, test names, configuration files
17+
- Pull request titles, descriptions, and review comments
18+
- TODO comments, FIXME notes, and inline annotations
19+
20+
**Only exceptions:**
21+
22+
- Chat/conversation with users (any language is acceptable)
23+
24+
**Any PR containing non-English content in code, comments, or technical documentation will be immediately rejected.**
25+
26+
---
27+
28+
## ⚠️ CRITICAL: Commit and Push Approval
29+
30+
AI Agents **MUST** ask for explicit user approval before:
31+
32+
- Creating any git commit (even if the user requested changes)
33+
- Pushing to any remote branch
34+
35+
**Required process:**
36+
37+
1. Complete all changes
38+
2. Show a clear summary of what was changed
39+
3. Ask **"May I commit?"** → Wait for explicit **yes**
40+
4. Commit with a proper conventional commit message
41+
5. Ask **"May I push?"** → Wait for explicit **yes**
42+
6. Push to the remote
43+
44+
**Never assume permission. Always ask. No exceptions.**
45+
46+
---

0 commit comments

Comments
 (0)