Skip to content

Commit 4d216d9

Browse files
committed
Added graphics system
1 parent efd43cf commit 4d216d9

66 files changed

Lines changed: 16352 additions & 8 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

car_sim

103 KB
Binary file not shown.

docs/Graphics/Audio/audio.md

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
[⬅ Back to Graphics Index](../index.md) | [⬅ Back to Sprite](../Sprite/sprite.md)
2+
3+
# Audio Module
4+
5+
Sound effects playback using SDL2 backend.
6+
7+
> **Note**: Requires SDL2 and the compile flag `-DPYTHONIC_ENABLE_SDL2_AUDIO -lSDL2`
8+
9+
---
10+
11+
## Quick Start
12+
13+
```cpp
14+
#include <pythonic/TerminalGraphics/Audio/Sound.hpp>
15+
using namespace Pythonic::TG;
16+
17+
SoundBuffer buffer;
18+
if (buffer.loadFromFile("jump.wav"))
19+
{
20+
Sound sound;
21+
sound.setBuffer(buffer);
22+
sound.play();
23+
}
24+
```
25+
26+
---
27+
28+
## Compilation
29+
30+
The audio module uses SDL2 for cross-platform audio playback. Enable it with:
31+
32+
```bash
33+
g++ -std=c++20 -Iinclude \
34+
-DPYTHONIC_ENABLE_SDL2_AUDIO \
35+
-o game game.cpp \
36+
-lSDL2 -pthread
37+
```
38+
39+
Without `-DPYTHONIC_ENABLE_SDL2_AUDIO`, audio calls compile but do nothing.
40+
41+
---
42+
43+
## SoundBuffer
44+
45+
Container for audio sample data. Loads WAV files via SDL2.
46+
47+
### Loading
48+
49+
| Method | Return | Description |
50+
| -------------------- | ------ | ------------- |
51+
| `loadFromFile(path)` | `bool` | Load WAV file |
52+
53+
```cpp
54+
SoundBuffer jumpBuffer;
55+
if (!jumpBuffer.loadFromFile("sounds/jump.wav"))
56+
{
57+
// Handle error
58+
}
59+
```
60+
61+
### Properties
62+
63+
| Method | Return | Description |
64+
| ------------------- | ---------------- | --------------------------- |
65+
| `getSamples()` | `const int16_t*` | Raw sample data |
66+
| `getSampleCount()` | `size_t` | Number of samples |
67+
| `getSampleRate()` | `unsigned` | Sample rate (Hz) |
68+
| `getChannelCount()` | `unsigned` | Channels (1=mono, 2=stereo) |
69+
| `getDuration()` | `float` | Audio duration in seconds |
70+
71+
```cpp
72+
float duration = buffer.getDuration();
73+
std::cout << "Sound is " << duration << " seconds\n";
74+
```
75+
76+
---
77+
78+
## Sound
79+
80+
Playable sound instance. Multiple sounds can play simultaneously.
81+
82+
### Constructor
83+
84+
| Constructor | Description |
85+
| --------------- | --------------------- |
86+
| `Sound()` | Create without buffer |
87+
| `Sound(buffer)` | Create with buffer |
88+
89+
### Buffer
90+
91+
| Method | Description |
92+
| ------------------- | ------------------ |
93+
| `setBuffer(buffer)` | Set sound buffer |
94+
| `getBuffer()` | Get buffer pointer |
95+
96+
```cpp
97+
Sound sound;
98+
sound.setBuffer(jumpBuffer);
99+
```
100+
101+
### Playback
102+
103+
| Method | Description |
104+
| ------------- | ---------------------- |
105+
| `play()` | Start/restart playback |
106+
| `pause()` | Pause playback |
107+
| `stop()` | Stop playback |
108+
| `getStatus()` | Get current state |
109+
110+
| Status | Description |
111+
| ---------------- | ----------------- |
112+
| `Sound::Stopped` | Not playing |
113+
| `Sound::Playing` | Currently playing |
114+
| `Sound::Paused` | Paused |
115+
116+
```cpp
117+
sound.play();
118+
119+
// Check status
120+
if (sound.getStatus() == Sound::Playing)
121+
{
122+
// Still playing
123+
}
124+
```
125+
126+
### Volume
127+
128+
| Method | Description |
129+
| ------------------- | ------------------------- |
130+
| `setVolume(volume)` | Set volume (0.0 to 100.0) |
131+
| `getVolume()` | Get current volume |
132+
133+
```cpp
134+
sound.setVolume(50.0f); // 50% volume
135+
```
136+
137+
### Loop
138+
139+
| Method | Description |
140+
| --------------- | ---------------------- |
141+
| `setLoop(bool)` | Enable/disable looping |
142+
| `getLoop()` | Check if looping |
143+
144+
```cpp
145+
Sound bgm(musicBuffer);
146+
bgm.setLoop(true); // Loop forever
147+
bgm.play();
148+
```
149+
150+
### Pitch
151+
152+
| Method | Description |
153+
| ----------------- | ------------------------ |
154+
| `setPitch(pitch)` | Set pitch (1.0 = normal) |
155+
| `getPitch()` | Get current pitch |
156+
157+
```cpp
158+
sound.setPitch(0.5f); // Half speed, lower pitch
159+
sound.setPitch(2.0f); // Double speed, higher pitch
160+
```
161+
162+
---
163+
164+
## Multiple Sounds
165+
166+
Multiple sounds can play simultaneously via callback-based mixing:
167+
168+
```cpp
169+
SoundBuffer jumpBuf, coinBuf, hitBuf;
170+
jumpBuf.loadFromFile("jump.wav");
171+
coinBuf.loadFromFile("coin.wav");
172+
hitBuf.loadFromFile("hit.wav");
173+
174+
Sound jump(jumpBuf);
175+
Sound coin(coinBuf);
176+
Sound hit(hitBuf);
177+
178+
// All can play at the same time
179+
jump.play();
180+
coin.play(); // Overlaps with jump
181+
// hit.play() later overlaps with both
182+
```
183+
184+
---
185+
186+
## Preloading Sounds
187+
188+
For latency-free playback, load all sounds at startup:
189+
190+
```cpp
191+
// At initialization
192+
struct GameSounds
193+
{
194+
SoundBuffer jumpBuffer;
195+
SoundBuffer coinBuffer;
196+
SoundBuffer hitBuffer;
197+
SoundBuffer gameOverBuffer;
198+
199+
Sound jump;
200+
Sound coin;
201+
Sound hit;
202+
Sound gameOver;
203+
204+
bool load()
205+
{
206+
if (!jumpBuffer.loadFromFile("jump.wav")) return false;
207+
if (!coinBuffer.loadFromFile("coin.wav")) return false;
208+
if (!hitBuffer.loadFromFile("hit.wav")) return false;
209+
if (!gameOverBuffer.loadFromFile("gameover.wav")) return false;
210+
211+
jump.setBuffer(jumpBuffer);
212+
coin.setBuffer(coinBuffer);
213+
hit.setBuffer(hitBuffer);
214+
gameOver.setBuffer(gameOverBuffer);
215+
216+
return true;
217+
}
218+
} sounds;
219+
220+
// In game code
221+
sounds.jump.play(); // Instant playback
222+
```
223+
224+
---
225+
226+
## Error Handling
227+
228+
```cpp
229+
SoundBuffer buffer;
230+
if (!buffer.loadFromFile("missing.wav"))
231+
{
232+
std::cerr << "Failed to load sound\n";
233+
// Continue without sound, or use fallback
234+
}
235+
```
236+
237+
---
238+
239+
## Complete Example: Game with Sound Effects
240+
241+
```cpp
242+
#include <pythonic/TerminalGraphics/TerminalGraphics.hpp>
243+
using namespace Pythonic::TG;
244+
245+
int main()
246+
{
247+
Canvas canvas(160, 80, RenderMode::Braille);
248+
249+
// Load sounds
250+
SoundBuffer jumpBuf, scoreBuf;
251+
jumpBuf.loadFromFile("sounds/jump.wav");
252+
scoreBuf.loadFromFile("sounds/score.wav");
253+
254+
Sound jumpSound(jumpBuf);
255+
Sound scoreSound(scoreBuf);
256+
jumpSound.setVolume(80.0f);
257+
258+
// Game state
259+
float playerY = 40;
260+
float velocityY = 0;
261+
int score = 0;
262+
bool wasSpacePressed = false;
263+
264+
Clock clock;
265+
266+
while (!Keyboard::isKeyPressed(Key::Escape))
267+
{
268+
float dt = clock.restart().asSeconds();
269+
270+
// Jump on space (one-shot)
271+
bool spacePressed = Keyboard::isKeyPressed(Key::Space);
272+
if (spacePressed && !wasSpacePressed)
273+
{
274+
velocityY = -200;
275+
jumpSound.play();
276+
}
277+
wasSpacePressed = spacePressed;
278+
279+
// Physics
280+
velocityY += 500 * dt; // Gravity
281+
playerY += velocityY * dt;
282+
283+
// Bounds
284+
if (playerY > 70)
285+
{
286+
playerY = 70;
287+
velocityY = 0;
288+
}
289+
290+
// Score (example trigger)
291+
static float scoreTimer = 0;
292+
scoreTimer += dt;
293+
if (scoreTimer >= 2.0f)
294+
{
295+
score++;
296+
scoreSound.play();
297+
scoreTimer = 0;
298+
}
299+
300+
// Draw
301+
canvas.clear();
302+
canvas.fillCircle(80, playerY, 5, Color::Yellow);
303+
Text::draw(canvas, 5, 2, "Score: " + std::to_string(score));
304+
canvas.display();
305+
306+
sleep(Time::milliseconds(16));
307+
}
308+
309+
return 0;
310+
}
311+
```
312+
313+
---
314+
315+
## Technical Notes
316+
317+
### SDL2 Backend Details
318+
319+
- Uses callback-based audio mixing for low latency
320+
- All playing sounds are mixed in real-time in `audioCallback()`
321+
- Audio device is initialized on first `Sound::play()`
322+
- Cleanup is automatic via `atexit()` handler
323+
- Thread-safe: uses mutex for sound list access
324+
325+
### Supported Formats
326+
327+
- WAV (via SDL2_LoadWAV)
328+
- Other formats require SDL2_mixer extension
329+
330+
---
331+
332+
[⬅ Back to Sprite](../Sprite/sprite.md) | [Next: Text Module →](../Text/text.md)

0 commit comments

Comments
 (0)