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.
Files changed (37) hide show
  1. ay8910_wrapper-0.7.2/.github/workflows/build_and_publish.yml +66 -0
  2. ay8910_wrapper-0.7.2/.github/workflows/tests.yml +65 -0
  3. ay8910_wrapper-0.7.2/.gitignore +48 -0
  4. ay8910_wrapper-0.7.2/CMakeLists.txt +38 -0
  5. ay8910_wrapper-0.7.2/CONTRIBUTE.md +131 -0
  6. ay8910_wrapper-0.7.2/LICENSE.md +21 -0
  7. ay8910_wrapper-0.7.2/PKG-INFO +168 -0
  8. ay8910_wrapper-0.7.2/README.md +144 -0
  9. ay8910_wrapper-0.7.2/REFERENCE.md +384 -0
  10. ay8910_wrapper-0.7.2/ay8910_cpp_lib/LICENSE_MAME.md +27 -0
  11. ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8910.cpp +854 -0
  12. ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8910.h +343 -0
  13. ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8912_cap32.cpp +258 -0
  14. ay8910_wrapper-0.7.2/ay8910_cpp_lib/ay8912_cap32.h +76 -0
  15. ay8910_wrapper-0.7.2/ay8910_cpp_lib/emu.h +126 -0
  16. ay8910_wrapper-0.7.2/ay8910_cpp_lib/logmacro.h +6 -0
  17. ay8910_wrapper-0.7.2/ay8910_cpp_lib/placeholder.txt +1 -0
  18. ay8910_wrapper-0.7.2/pyproject.toml +62 -0
  19. ay8910_wrapper-0.7.2/scripts/analyze_wav.py +63 -0
  20. ay8910_wrapper-0.7.2/scripts/check_samples.py +22 -0
  21. ay8910_wrapper-0.7.2/scripts/compare_wav.py +50 -0
  22. ay8910_wrapper-0.7.2/scripts/placeholder.txt +1 -0
  23. ay8910_wrapper-0.7.2/scripts/strip_wav_metadata.py +37 -0
  24. ay8910_wrapper-0.7.2/scripts/ym_live_player.py +139 -0
  25. ay8910_wrapper-0.7.2/scripts/ym_player.py +228 -0
  26. ay8910_wrapper-0.7.2/src/ay8910_wrapper/__init__.py +26 -0
  27. ay8910_wrapper-0.7.2/src/ay8910_wrapper/direct_output.py +40 -0
  28. ay8910_wrapper-0.7.2/src/ay8910_wrapper/placeholder.txt +1 -0
  29. ay8910_wrapper-0.7.2/src/ay8910_wrapper/wrapper.cpp +56 -0
  30. ay8910_wrapper-0.7.2/src/placeholder.txt +1 -0
  31. ay8910_wrapper-0.7.2/tests/manual_test_live.py +37 -0
  32. ay8910_wrapper-0.7.2/tests/placeholder.txt +1 -0
  33. ay8910_wrapper-0.7.2/tests/test.py +1 -0
  34. ay8910_wrapper-0.7.2/tests/test_ay8910.py +259 -0
  35. ay8910_wrapper-0.7.2/tests/test_comparison.py +81 -0
  36. ay8910_wrapper-0.7.2/tests/test_registers.py +54 -0
  37. 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.