ay8910-wrapper 0.7.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ay8910_wrapper-0.7.2/.github/workflows/build_and_publish.yml +66 -0
- ay8910_wrapper-0.7.2/.github/workflows/tests.yml +65 -0
- ay8910_wrapper-0.7.2/.gitignore +48 -0
- ay8910_wrapper-0.7.2/CMakeLists.txt +38 -0
- ay8910_wrapper-0.7.2/CONTRIBUTE.md +131 -0
- ay8910_wrapper-0.7.2/LICENSE.md +21 -0
- ay8910_wrapper-0.7.2/PKG-INFO +168 -0
- ay8910_wrapper-0.7.2/README.md +144 -0
- ay8910_wrapper-0.7.2/REFERENCE.md +384 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/LICENSE_MAME.md +27 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8910.cpp +854 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8910.h +343 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8912_cap32.cpp +258 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8912_cap32.h +76 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/emu.h +126 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/logmacro.h +6 -0
- ay8910_wrapper-0.7.2/ay8910_cpp_lib/placeholder.txt +1 -0
- ay8910_wrapper-0.7.2/pyproject.toml +62 -0
- ay8910_wrapper-0.7.2/scripts/analyze_wav.py +63 -0
- ay8910_wrapper-0.7.2/scripts/check_samples.py +22 -0
- ay8910_wrapper-0.7.2/scripts/compare_wav.py +50 -0
- ay8910_wrapper-0.7.2/scripts/placeholder.txt +1 -0
- ay8910_wrapper-0.7.2/scripts/strip_wav_metadata.py +37 -0
- ay8910_wrapper-0.7.2/scripts/ym_live_player.py +139 -0
- ay8910_wrapper-0.7.2/scripts/ym_player.py +228 -0
- ay8910_wrapper-0.7.2/src/ay8910_wrapper/__init__.py +26 -0
- ay8910_wrapper-0.7.2/src/ay8910_wrapper/direct_output.py +40 -0
- ay8910_wrapper-0.7.2/src/ay8910_wrapper/placeholder.txt +1 -0
- ay8910_wrapper-0.7.2/src/ay8910_wrapper/wrapper.cpp +56 -0
- ay8910_wrapper-0.7.2/src/placeholder.txt +1 -0
- ay8910_wrapper-0.7.2/tests/manual_test_live.py +37 -0
- ay8910_wrapper-0.7.2/tests/placeholder.txt +1 -0
- ay8910_wrapper-0.7.2/tests/test.py +1 -0
- ay8910_wrapper-0.7.2/tests/test_ay8910.py +259 -0
- ay8910_wrapper-0.7.2/tests/test_comparison.py +81 -0
- ay8910_wrapper-0.7.2/tests/test_registers.py +54 -0
- ay8910_wrapper-0.7.2/uv.lock +1493 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
name: Build and Publish Wheels
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
env:
|
|
10
|
+
# Force GitHub Actions to use Node.js 24 as per the deprecation warning
|
|
11
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
build_wheels:
|
|
15
|
+
name: Build wheels on ${{ matrix.os }}
|
|
16
|
+
runs-on: ${{ matrix.os }}
|
|
17
|
+
strategy:
|
|
18
|
+
matrix:
|
|
19
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- name: Build wheels
|
|
25
|
+
uses: pypa/cibuildwheel@v3.4.1
|
|
26
|
+
env:
|
|
27
|
+
CIBW_PRERELEASE_PYTHONS: True
|
|
28
|
+
|
|
29
|
+
- uses: actions/upload-artifact@v4
|
|
30
|
+
with:
|
|
31
|
+
name: cibw-wheels-${{ matrix.os }}-${{ strategy.job-index }}
|
|
32
|
+
path: ./wheelhouse/*.whl
|
|
33
|
+
|
|
34
|
+
build_sdist:
|
|
35
|
+
name: Build source distribution
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
|
|
40
|
+
- name: Build sdist
|
|
41
|
+
run: pipx run build --sdist
|
|
42
|
+
|
|
43
|
+
- uses: actions/upload-artifact@v4
|
|
44
|
+
with:
|
|
45
|
+
name: cibw-sdist
|
|
46
|
+
path: dist/*.tar.gz
|
|
47
|
+
|
|
48
|
+
publish:
|
|
49
|
+
name: Publish to PyPI
|
|
50
|
+
needs: [build_wheels, build_sdist]
|
|
51
|
+
runs-on: ubuntu-latest
|
|
52
|
+
# Required for Trusted Publishing
|
|
53
|
+
permissions:
|
|
54
|
+
id-token: write
|
|
55
|
+
steps:
|
|
56
|
+
- name: Download all artifacts
|
|
57
|
+
uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
pattern: cibw-*
|
|
60
|
+
path: dist
|
|
61
|
+
merge-multiple: true
|
|
62
|
+
|
|
63
|
+
- name: Publish package distributions to PyPI
|
|
64
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
65
|
+
with:
|
|
66
|
+
verbose: true
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
name: Tests & Linting
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, master ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main, master ]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
|
|
10
|
+
env:
|
|
11
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
lint:
|
|
15
|
+
name: Python Linting
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Set up Python
|
|
21
|
+
uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: "3.12"
|
|
24
|
+
cache: "pip"
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: |
|
|
28
|
+
python -m pip install --upgrade pip
|
|
29
|
+
python -m pip install ruff
|
|
30
|
+
|
|
31
|
+
- name: Run Ruff
|
|
32
|
+
run: ruff check .
|
|
33
|
+
|
|
34
|
+
test:
|
|
35
|
+
name: Build & Test on ${{ matrix.os }} (Py ${{ matrix.python-version }})
|
|
36
|
+
runs-on: ${{ matrix.os }}
|
|
37
|
+
needs: lint
|
|
38
|
+
strategy:
|
|
39
|
+
fail-fast: false
|
|
40
|
+
matrix:
|
|
41
|
+
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
42
|
+
python-version: ["3.8", "3.13"]
|
|
43
|
+
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
|
|
47
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
48
|
+
uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: ${{ matrix.python-version }}
|
|
51
|
+
cache: "pip"
|
|
52
|
+
|
|
53
|
+
- name: Install system dependencies (Ubuntu)
|
|
54
|
+
if: matrix.os == 'ubuntu-latest'
|
|
55
|
+
run: |
|
|
56
|
+
sudo apt-get update
|
|
57
|
+
sudo apt-get install -y libportaudio2
|
|
58
|
+
|
|
59
|
+
- name: Install Python dependencies
|
|
60
|
+
run: |
|
|
61
|
+
python -m pip install --upgrade pip
|
|
62
|
+
python -m pip install .[test]
|
|
63
|
+
|
|
64
|
+
- name: Run tests
|
|
65
|
+
run: pytest tests/
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# --- SECURITY (CRITICAL) ---
|
|
2
|
+
.env
|
|
3
|
+
.env.local
|
|
4
|
+
.env.*.local
|
|
5
|
+
*.pem
|
|
6
|
+
*.key
|
|
7
|
+
.secrets.baseline
|
|
8
|
+
|
|
9
|
+
# --- PYTHON ---
|
|
10
|
+
__pycache__/
|
|
11
|
+
*.py[cod]
|
|
12
|
+
*$py.class
|
|
13
|
+
.venv
|
|
14
|
+
venv/
|
|
15
|
+
ENV/
|
|
16
|
+
env/
|
|
17
|
+
build/
|
|
18
|
+
dist/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
|
|
21
|
+
# --- TESTS & QUALITY ---
|
|
22
|
+
.pytest_cache/
|
|
23
|
+
.tox/
|
|
24
|
+
.nox/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
.mypy_cache/
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
|
|
30
|
+
# --- DOCKER ---
|
|
31
|
+
.docker/
|
|
32
|
+
# We keep the Dockerfile, but we mostly forget the persistent data
|
|
33
|
+
data/
|
|
34
|
+
db_data/
|
|
35
|
+
|
|
36
|
+
# --- IDE (VS Code, PyCharm) ---
|
|
37
|
+
.vscode/
|
|
38
|
+
.idea/
|
|
39
|
+
*.swp
|
|
40
|
+
*.swo
|
|
41
|
+
|
|
42
|
+
# --- OS ---
|
|
43
|
+
.DS_Store
|
|
44
|
+
Thumbs.db
|
|
45
|
+
|
|
46
|
+
# --- PROJECT SPECIFICS --
|
|
47
|
+
YM example files/
|
|
48
|
+
*.wav
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.15)
|
|
2
|
+
|
|
3
|
+
# Silence the pybind11 deprecation warning
|
|
4
|
+
cmake_policy(SET CMP0148 NEW)
|
|
5
|
+
|
|
6
|
+
project(ay8910_wrapper CXX)
|
|
7
|
+
|
|
8
|
+
# --- C++ Standard ---
|
|
9
|
+
# The MAME code uses C++17 features (like if-initializers)
|
|
10
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
11
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
12
|
+
|
|
13
|
+
# --- C++ Library ---
|
|
14
|
+
# Define our standalone C++ library
|
|
15
|
+
add_library(ay8910_cpp_lib
|
|
16
|
+
ay8910_cpp_lib/ay8910.cpp
|
|
17
|
+
ay8910_cpp_lib/ay8912_cap32.cpp
|
|
18
|
+
)
|
|
19
|
+
# Specify its include directory
|
|
20
|
+
target_include_directories(ay8910_cpp_lib PUBLIC ay8910_cpp_lib)
|
|
21
|
+
|
|
22
|
+
# Enable Position Independent Code (-fPIC) for the static library,
|
|
23
|
+
# required to link it into the shared Python module on Linux.
|
|
24
|
+
set_target_properties(ay8910_cpp_lib PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
|
25
|
+
|
|
26
|
+
# --- Python Module ---
|
|
27
|
+
# Find pybind11 (provided by scikit-build-core)
|
|
28
|
+
find_package(pybind11 CONFIG REQUIRED)
|
|
29
|
+
|
|
30
|
+
# Define the Python module using our wrapper and linking to our C++ library
|
|
31
|
+
pybind11_add_module(ay8910_wrapper
|
|
32
|
+
src/ay8910_wrapper/wrapper.cpp
|
|
33
|
+
)
|
|
34
|
+
target_link_libraries(ay8910_wrapper PRIVATE ay8910_cpp_lib)
|
|
35
|
+
|
|
36
|
+
# --- Installation ---
|
|
37
|
+
# This tells scikit-build where to install the final .pyd file
|
|
38
|
+
install(TARGETS ay8910_wrapper DESTINATION ay8910_wrapper)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Contributing Guide
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in `ay8910_wrapper`! This document explains how to contribute to the project.
|
|
4
|
+
|
|
5
|
+
## GitHub Flow
|
|
6
|
+
|
|
7
|
+
We follow the standard **GitHub Flow** for development. Here is a concrete example of the commands to use:
|
|
8
|
+
|
|
9
|
+
1. **Fork the repository** on GitHub by clicking the "Fork" button.
|
|
10
|
+
2. **Clone your fork** locally:
|
|
11
|
+
```powershell
|
|
12
|
+
git clone https://github.com/YOUR_USERNAME/ay8910.git
|
|
13
|
+
cd ay8910
|
|
14
|
+
```
|
|
15
|
+
3. **Keep your fork up to date** (to be done before each new modification):
|
|
16
|
+
If you haven't added the **original** (upstream) repository yet:
|
|
17
|
+
```powershell
|
|
18
|
+
git remote add upstream https://github.com/devfred78/ay8910.git
|
|
19
|
+
```
|
|
20
|
+
Then, synchronize your `main` branch:
|
|
21
|
+
```powershell
|
|
22
|
+
git checkout main
|
|
23
|
+
git fetch upstream
|
|
24
|
+
git merge upstream/main
|
|
25
|
+
git push origin main
|
|
26
|
+
```
|
|
27
|
+
4. **Create a descriptive branch** for your modification:
|
|
28
|
+
```powershell
|
|
29
|
+
git checkout -b fix-envelope-bug
|
|
30
|
+
```
|
|
31
|
+
5. **Make your changes** and commit them:
|
|
32
|
+
```powershell
|
|
33
|
+
# After editing files
|
|
34
|
+
git add .
|
|
35
|
+
git commit -m "Fix: correct envelope period calculation in ay8912_cap32"
|
|
36
|
+
```
|
|
37
|
+
6. **Push** your branch to your fork:
|
|
38
|
+
```powershell
|
|
39
|
+
git push origin fix-envelope-bug
|
|
40
|
+
```
|
|
41
|
+
7. **Open a Pull Request (PR)**: Go to the original repository on GitHub. You should see a "Compare & pull request" button for your recently pushed branch.
|
|
42
|
+
|
|
43
|
+
### Handling Merge Conflicts
|
|
44
|
+
|
|
45
|
+
Sometimes, GitHub may detect conflicts in your Pull Request because someone else modified the same lines of code in the `main` branch. To resolve them, you need to synchronize your local repository with the **original** (upstream) repository (as described in step 3 above) and merge it into your branch:
|
|
46
|
+
|
|
47
|
+
1. **Update your local `main` branch from upstream** (see step 3 for details).
|
|
48
|
+
|
|
49
|
+
2. **Merge `main` into your feature branch**:
|
|
50
|
+
```powershell
|
|
51
|
+
git checkout fix-envelope-bug
|
|
52
|
+
git merge main
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
3. **Resolve conflicts**: Git will mark the files with conflicts. Open them in your editor, look for `<<<<<<<`, `=======`, and `>>>>>>>` markers, and choose which version to keep.
|
|
56
|
+
|
|
57
|
+
4. **Commit the resolution**:
|
|
58
|
+
```powershell
|
|
59
|
+
git add .
|
|
60
|
+
git commit -m "Merge main and resolve conflicts"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
5. **Push the updated branch**:
|
|
64
|
+
```powershell
|
|
65
|
+
git push origin fix-envelope-bug
|
|
66
|
+
```
|
|
67
|
+
The Pull Request will be updated automatically on GitHub.
|
|
68
|
+
|
|
69
|
+
## Development Environment Setup
|
|
70
|
+
|
|
71
|
+
We recommend using **[uv](https://github.com/astral-sh/uv)** for fast and efficient management of Python virtual environments.
|
|
72
|
+
|
|
73
|
+
### 1. Install `uv`
|
|
74
|
+
If you don't have `uv` yet, install it:
|
|
75
|
+
- **Windows**: `powershell -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
|
76
|
+
- **macOS/Linux**: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
77
|
+
|
|
78
|
+
### 2. Initialize the Environment
|
|
79
|
+
From the project root:
|
|
80
|
+
```powershell
|
|
81
|
+
# Create the virtual environment and install development dependencies
|
|
82
|
+
uv sync --all-extras
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
This command automatically installs the dependencies needed for:
|
|
86
|
+
- Compiling the C++ extension
|
|
87
|
+
- Running tests (`pytest`)
|
|
88
|
+
- Linting (`ruff`)
|
|
89
|
+
|
|
90
|
+
## Development and Compilation
|
|
91
|
+
|
|
92
|
+
As the project contains a C++ extension, you need to compile it to test your modifications:
|
|
93
|
+
|
|
94
|
+
```powershell
|
|
95
|
+
# Install in editable mode (compiles the C++ module)
|
|
96
|
+
uv pip install -e .
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
If you modify the C++ code (`src/ay8910_wrapper/wrapper.cpp` or files in `ay8910_cpp_lib/`), you will need to rerun this command for the changes to take effect in Python.
|
|
100
|
+
|
|
101
|
+
## Unit Tests
|
|
102
|
+
|
|
103
|
+
### Run Tests Locally
|
|
104
|
+
Before submitting a PR, make sure all tests pass:
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
# Run all tests with pytest
|
|
108
|
+
uv run pytest tests/
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Linting
|
|
112
|
+
We use `ruff` to ensure code quality:
|
|
113
|
+
```powershell
|
|
114
|
+
# Check code style
|
|
115
|
+
uv run ruff check .
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Continuous Integration (GitHub Actions)
|
|
119
|
+
|
|
120
|
+
When a Pull Request is opened, the following actions are automatically launched on GitHub:
|
|
121
|
+
|
|
122
|
+
1. **Linting**: Code style check with `ruff`.
|
|
123
|
+
2. **Build & Test**:
|
|
124
|
+
- Compilation of the project on **Linux, Windows, and macOS**.
|
|
125
|
+
- Execution of unit tests on **Python 3.8 and 3.13** versions.
|
|
126
|
+
|
|
127
|
+
Additionally, when a **version tag** (starting with `v`) is pushed to the repository, the **Build and Publish** workflow is triggered to automatically build wheels for all platforms and publish them to **PyPI**.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
Once your modification is ready and tested, feel free to submit your Pull Request!
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 devfred78
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
|
+
Name: ay8910_wrapper
|
|
3
|
+
Version: 0.7.2
|
|
4
|
+
Summary: A Python wrapper for the AY-3-8910 and AY-3-8912 sound chip emulators, featuring real-time audio playback and cycle-accurate synthesis.
|
|
5
|
+
Author-Email: devfred78 <devfred78@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Classifier: Development Status :: 4 - Beta
|
|
8
|
+
Classifier: Intended Audience :: Developers
|
|
9
|
+
Classifier: Topic :: Multimedia :: Sound/Audio
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: C++
|
|
12
|
+
Project-URL: Homepage, https://github.com/devfred78/ay8910
|
|
13
|
+
Requires-Python: >=3.8
|
|
14
|
+
Requires-Dist: lhafile
|
|
15
|
+
Requires-Dist: numpy>=1.24.4
|
|
16
|
+
Requires-Dist: sounddevice>=0.5.5
|
|
17
|
+
Provides-Extra: test
|
|
18
|
+
Requires-Dist: pytest; extra == "test"
|
|
19
|
+
Requires-Dist: pytest-cov; extra == "test"
|
|
20
|
+
Provides-Extra: dev
|
|
21
|
+
Requires-Dist: ruff; extra == "dev"
|
|
22
|
+
Requires-Dist: mypy; extra == "dev"
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
|
|
25
|
+
# AY-3-8910 Standalone Library and Python Wrapper
|
|
26
|
+
|
|
27
|
+
A Python wrapper for the AY-3-8910 and AY-3-8912 sound chip emulators, featuring real-time audio playback and cycle-accurate synthesis.
|
|
28
|
+
|
|
29
|
+
> **A Note on this Project's Origin**
|
|
30
|
+
>
|
|
31
|
+
> This project is primarily the result of a series of experiments using various IA Code Assists for code generation and error handling. Rather than using it on academic examples, it seemed more interesting to apply it to a project that could meet a real practical need.
|
|
32
|
+
>
|
|
33
|
+
> This, therefore, is the reason for `AY8910`'s existence: you can dissect the code to see how Gemini and Junie (with my guidance) went about building it, or you can ignore all that and just use this library for your own needs!
|
|
34
|
+
|
|
35
|
+
This project contains a standalone C++ library for the AY-3-8910 sound chip. It features two emulation engines:
|
|
36
|
+
- **Caprice32-based (`ay8912_cap32`)**: The **recommended** engine for all new projects. It offers superior accuracy, stereo mixing, and integrated live audio support.
|
|
37
|
+
- **MAME-based (`ay8910`)**: Kept primarily for **historical reasons** and legacy compatibility.
|
|
38
|
+
|
|
39
|
+
It also includes a Python wrapper to make these emulators accessible from Python scripts, allowing for programmatic chiptune generation and `.ym` file playback.
|
|
40
|
+
|
|
41
|
+
## Quick Start (Live Audio)
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import ay8910_wrapper as ay
|
|
45
|
+
import time
|
|
46
|
+
|
|
47
|
+
# Initialize
|
|
48
|
+
psg = ay.ay8912_cap32(1000000, 44100)
|
|
49
|
+
psg.set_stereo_mix(255, 13, 170, 170, 13, 255)
|
|
50
|
+
|
|
51
|
+
# Start live playback!
|
|
52
|
+
psg.play()
|
|
53
|
+
|
|
54
|
+
# Set registers - sound changes immediately
|
|
55
|
+
psg.set_register(0, 254) # Tone A Fine
|
|
56
|
+
psg.set_register(1, 0) # Tone A Coarse
|
|
57
|
+
psg.set_register(7, 0x3E) # Enable Channel A
|
|
58
|
+
psg.set_register(8, 15) # Max volume
|
|
59
|
+
|
|
60
|
+
time.sleep(1)
|
|
61
|
+
psg.stop()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```sh
|
|
65
|
+
# Play a .ym file using the new live script
|
|
66
|
+
python scripts\ym_live_player.py PATH\TO\YM_FILE.YM
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Installation
|
|
70
|
+
|
|
71
|
+
You can install the library directly from [PyPI](https://pypi.org/project/ay8910-wrapper/):
|
|
72
|
+
|
|
73
|
+
```sh
|
|
74
|
+
pip install ay8910_wrapper
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
This will automatically install the necessary dependencies (`lhafile`, `numpy`, `sounddevice`).
|
|
78
|
+
|
|
79
|
+
For developers who want to compile from source or contribute, please refer to the [Contributing Guide](CONTRIBUTE.md).
|
|
80
|
+
|
|
81
|
+
## Basic Usage in Python (Legacy MAME engine)
|
|
82
|
+
|
|
83
|
+
> **Note**: This section demonstrates the legacy `ay8910` class. For modern applications, please refer to the **Quick Start** section or the **Caprice32** section below.
|
|
84
|
+
|
|
85
|
+
```python
|
|
86
|
+
import ay8910_wrapper as ay
|
|
87
|
+
import wave
|
|
88
|
+
import struct
|
|
89
|
+
|
|
90
|
+
# Helper to write WAV files
|
|
91
|
+
def write_wav(filename, samples, sample_rate):
|
|
92
|
+
with wave.open(filename, 'wb') as f:
|
|
93
|
+
f.setnchannels(1)
|
|
94
|
+
f.setsampwidth(2)
|
|
95
|
+
f.setframerate(sample_rate)
|
|
96
|
+
packed_samples = struct.pack('<' + 'h' * len(samples), *samples)
|
|
97
|
+
f.writeframes(packed_samples)
|
|
98
|
+
|
|
99
|
+
# --- Main Program ---
|
|
100
|
+
|
|
101
|
+
# 1. Initialize the emulator
|
|
102
|
+
clock = 2000000 # 2 MHz, a common clock for this chip
|
|
103
|
+
sample_rate = 44100
|
|
104
|
+
psg = ay.ay8910(ay.psg_type.PSG_TYPE_AY, clock, 1, 0)
|
|
105
|
+
psg.set_flags(ay.AY8910_LEGACY_OUTPUT)
|
|
106
|
+
psg.start()
|
|
107
|
+
psg.reset()
|
|
108
|
+
|
|
109
|
+
# 2. Program the chip registers
|
|
110
|
+
# Enable Tone on Channel A, disable everything else
|
|
111
|
+
psg.address_w(7)
|
|
112
|
+
psg.data_w(0b00111110)
|
|
113
|
+
|
|
114
|
+
# Set Channel A frequency to Middle C (261.63 Hz)
|
|
115
|
+
period = int(clock / (16 * 261.63))
|
|
116
|
+
psg.address_w(0) # Fine tune
|
|
117
|
+
psg.data_w(period & 0xFF)
|
|
118
|
+
psg.address_w(1) # Coarse tune
|
|
119
|
+
psg.data_w((period >> 8) & 0x0F)
|
|
120
|
+
|
|
121
|
+
# Set Channel A volume to max
|
|
122
|
+
psg.address_w(8)
|
|
123
|
+
psg.data_w(15)
|
|
124
|
+
|
|
125
|
+
# 3. Generate audio
|
|
126
|
+
# Generate 2 seconds of audio
|
|
127
|
+
num_samples = sample_rate * 2
|
|
128
|
+
samples = psg.generate(num_samples, sample_rate)
|
|
129
|
+
|
|
130
|
+
# 4. Save the result
|
|
131
|
+
write_wav("tone_output.wav", samples, sample_rate)
|
|
132
|
+
print("Generated 'tone_output.wav'")
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
For a complete list of all available functions, classes, and constants, please see the [API Reference](REFERENCE.md).
|
|
137
|
+
|
|
138
|
+
## Recommended: Caprice32 (Amstrad CPC) Emulation
|
|
139
|
+
|
|
140
|
+
The **Caprice32** engine is the preferred choice for most users. It provides more authentic sound synthesis and advanced features like stereo panning.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# Initialize the Caprice32-style emulator
|
|
144
|
+
psg_cpc = ay.ay8912_cap32(clock, sample_rate)
|
|
145
|
+
# Set standard CPC stereo mix (Channel A=Left, B=Center, C=Right)
|
|
146
|
+
psg_cpc.set_stereo_mix(255, 13, 170, 170, 13, 255)
|
|
147
|
+
|
|
148
|
+
# Generate stereo audio (interleaved)
|
|
149
|
+
stereo_samples = psg_cpc.generate(num_samples)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Contributing Guide
|
|
153
|
+
|
|
154
|
+
If you wish to contribute to the project or compile it from source, please refer to our **[Contributing Guide](CONTRIBUTE.md)**.
|
|
155
|
+
|
|
156
|
+
It contains all the necessary information about:
|
|
157
|
+
- The development workflow (**GitHub Flow**)
|
|
158
|
+
- Using **uv** for environment management
|
|
159
|
+
- Compilation and test commands
|
|
160
|
+
- Continuous integration processes (GitHub Actions)
|
|
161
|
+
|
|
162
|
+
## Acknowledgments
|
|
163
|
+
|
|
164
|
+
This project relies on the incredible work of the following open-source projects:
|
|
165
|
+
|
|
166
|
+
- **[MAME](https://github.com/mamedev/mame)**: The original AY-3-8910 and YM2149 emulation cores were derived from the MAME project. Their commitment to accuracy and historical preservation is a cornerstone of this library.
|
|
167
|
+
- **[Caprice32](https://github.com/ColinPitrat/caprice32)**: The Amstrad CPC-specific PSG emulation logic and amplitude tables were integrated from the Caprice32 project, providing authentic sound for CPC-related audio tasks.
|
|
168
|
+
- **[Sergey Bulba](http://bulba.at.gz.ru/)**: Special thanks for the AY/YM amplitude tables used in the Caprice32 engine, which are essential for reproducing the characteristic sound of these chips.
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# AY-3-8910 Standalone Library and Python Wrapper
|
|
2
|
+
|
|
3
|
+
A Python wrapper for the AY-3-8910 and AY-3-8912 sound chip emulators, featuring real-time audio playback and cycle-accurate synthesis.
|
|
4
|
+
|
|
5
|
+
> **A Note on this Project's Origin**
|
|
6
|
+
>
|
|
7
|
+
> This project is primarily the result of a series of experiments using various IA Code Assists for code generation and error handling. Rather than using it on academic examples, it seemed more interesting to apply it to a project that could meet a real practical need.
|
|
8
|
+
>
|
|
9
|
+
> This, therefore, is the reason for `AY8910`'s existence: you can dissect the code to see how Gemini and Junie (with my guidance) went about building it, or you can ignore all that and just use this library for your own needs!
|
|
10
|
+
|
|
11
|
+
This project contains a standalone C++ library for the AY-3-8910 sound chip. It features two emulation engines:
|
|
12
|
+
- **Caprice32-based (`ay8912_cap32`)**: The **recommended** engine for all new projects. It offers superior accuracy, stereo mixing, and integrated live audio support.
|
|
13
|
+
- **MAME-based (`ay8910`)**: Kept primarily for **historical reasons** and legacy compatibility.
|
|
14
|
+
|
|
15
|
+
It also includes a Python wrapper to make these emulators accessible from Python scripts, allowing for programmatic chiptune generation and `.ym` file playback.
|
|
16
|
+
|
|
17
|
+
## Quick Start (Live Audio)
|
|
18
|
+
|
|
19
|
+
```python
|
|
20
|
+
import ay8910_wrapper as ay
|
|
21
|
+
import time
|
|
22
|
+
|
|
23
|
+
# Initialize
|
|
24
|
+
psg = ay.ay8912_cap32(1000000, 44100)
|
|
25
|
+
psg.set_stereo_mix(255, 13, 170, 170, 13, 255)
|
|
26
|
+
|
|
27
|
+
# Start live playback!
|
|
28
|
+
psg.play()
|
|
29
|
+
|
|
30
|
+
# Set registers - sound changes immediately
|
|
31
|
+
psg.set_register(0, 254) # Tone A Fine
|
|
32
|
+
psg.set_register(1, 0) # Tone A Coarse
|
|
33
|
+
psg.set_register(7, 0x3E) # Enable Channel A
|
|
34
|
+
psg.set_register(8, 15) # Max volume
|
|
35
|
+
|
|
36
|
+
time.sleep(1)
|
|
37
|
+
psg.stop()
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
# Play a .ym file using the new live script
|
|
42
|
+
python scripts\ym_live_player.py PATH\TO\YM_FILE.YM
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
You can install the library directly from [PyPI](https://pypi.org/project/ay8910-wrapper/):
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
pip install ay8910_wrapper
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
This will automatically install the necessary dependencies (`lhafile`, `numpy`, `sounddevice`).
|
|
54
|
+
|
|
55
|
+
For developers who want to compile from source or contribute, please refer to the [Contributing Guide](CONTRIBUTE.md).
|
|
56
|
+
|
|
57
|
+
## Basic Usage in Python (Legacy MAME engine)
|
|
58
|
+
|
|
59
|
+
> **Note**: This section demonstrates the legacy `ay8910` class. For modern applications, please refer to the **Quick Start** section or the **Caprice32** section below.
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import ay8910_wrapper as ay
|
|
63
|
+
import wave
|
|
64
|
+
import struct
|
|
65
|
+
|
|
66
|
+
# Helper to write WAV files
|
|
67
|
+
def write_wav(filename, samples, sample_rate):
|
|
68
|
+
with wave.open(filename, 'wb') as f:
|
|
69
|
+
f.setnchannels(1)
|
|
70
|
+
f.setsampwidth(2)
|
|
71
|
+
f.setframerate(sample_rate)
|
|
72
|
+
packed_samples = struct.pack('<' + 'h' * len(samples), *samples)
|
|
73
|
+
f.writeframes(packed_samples)
|
|
74
|
+
|
|
75
|
+
# --- Main Program ---
|
|
76
|
+
|
|
77
|
+
# 1. Initialize the emulator
|
|
78
|
+
clock = 2000000 # 2 MHz, a common clock for this chip
|
|
79
|
+
sample_rate = 44100
|
|
80
|
+
psg = ay.ay8910(ay.psg_type.PSG_TYPE_AY, clock, 1, 0)
|
|
81
|
+
psg.set_flags(ay.AY8910_LEGACY_OUTPUT)
|
|
82
|
+
psg.start()
|
|
83
|
+
psg.reset()
|
|
84
|
+
|
|
85
|
+
# 2. Program the chip registers
|
|
86
|
+
# Enable Tone on Channel A, disable everything else
|
|
87
|
+
psg.address_w(7)
|
|
88
|
+
psg.data_w(0b00111110)
|
|
89
|
+
|
|
90
|
+
# Set Channel A frequency to Middle C (261.63 Hz)
|
|
91
|
+
period = int(clock / (16 * 261.63))
|
|
92
|
+
psg.address_w(0) # Fine tune
|
|
93
|
+
psg.data_w(period & 0xFF)
|
|
94
|
+
psg.address_w(1) # Coarse tune
|
|
95
|
+
psg.data_w((period >> 8) & 0x0F)
|
|
96
|
+
|
|
97
|
+
# Set Channel A volume to max
|
|
98
|
+
psg.address_w(8)
|
|
99
|
+
psg.data_w(15)
|
|
100
|
+
|
|
101
|
+
# 3. Generate audio
|
|
102
|
+
# Generate 2 seconds of audio
|
|
103
|
+
num_samples = sample_rate * 2
|
|
104
|
+
samples = psg.generate(num_samples, sample_rate)
|
|
105
|
+
|
|
106
|
+
# 4. Save the result
|
|
107
|
+
write_wav("tone_output.wav", samples, sample_rate)
|
|
108
|
+
print("Generated 'tone_output.wav'")
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
For a complete list of all available functions, classes, and constants, please see the [API Reference](REFERENCE.md).
|
|
113
|
+
|
|
114
|
+
## Recommended: Caprice32 (Amstrad CPC) Emulation
|
|
115
|
+
|
|
116
|
+
The **Caprice32** engine is the preferred choice for most users. It provides more authentic sound synthesis and advanced features like stereo panning.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
# Initialize the Caprice32-style emulator
|
|
120
|
+
psg_cpc = ay.ay8912_cap32(clock, sample_rate)
|
|
121
|
+
# Set standard CPC stereo mix (Channel A=Left, B=Center, C=Right)
|
|
122
|
+
psg_cpc.set_stereo_mix(255, 13, 170, 170, 13, 255)
|
|
123
|
+
|
|
124
|
+
# Generate stereo audio (interleaved)
|
|
125
|
+
stereo_samples = psg_cpc.generate(num_samples)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Contributing Guide
|
|
129
|
+
|
|
130
|
+
If you wish to contribute to the project or compile it from source, please refer to our **[Contributing Guide](CONTRIBUTE.md)**.
|
|
131
|
+
|
|
132
|
+
It contains all the necessary information about:
|
|
133
|
+
- The development workflow (**GitHub Flow**)
|
|
134
|
+
- Using **uv** for environment management
|
|
135
|
+
- Compilation and test commands
|
|
136
|
+
- Continuous integration processes (GitHub Actions)
|
|
137
|
+
|
|
138
|
+
## Acknowledgments
|
|
139
|
+
|
|
140
|
+
This project relies on the incredible work of the following open-source projects:
|
|
141
|
+
|
|
142
|
+
- **[MAME](https://github.com/mamedev/mame)**: The original AY-3-8910 and YM2149 emulation cores were derived from the MAME project. Their commitment to accuracy and historical preservation is a cornerstone of this library.
|
|
143
|
+
- **[Caprice32](https://github.com/ColinPitrat/caprice32)**: The Amstrad CPC-specific PSG emulation logic and amplitude tables were integrated from the Caprice32 project, providing authentic sound for CPC-related audio tasks.
|
|
144
|
+
- **[Sergey Bulba](http://bulba.at.gz.ru/)**: Special thanks for the AY/YM amplitude tables used in the Caprice32 engine, which are essential for reproducing the characteristic sound of these chips.
|