piwave 2.0.8__py3-none-any.whl → 2.1.0__py3-none-any.whl
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.
- piwave/__init__.py +3 -1
- piwave/__main__.py +105 -0
- piwave/backends/__init__.py +119 -0
- piwave/backends/base.py +236 -0
- piwave/backends/fm_transmitter.py +33 -0
- piwave/backends/pi_fm_rds.py +42 -0
- piwave/logger.py +99 -0
- piwave/piwave.py +153 -134
- piwave-2.1.0.dist-info/METADATA +743 -0
- piwave-2.1.0.dist-info/RECORD +13 -0
- piwave-2.0.8.dist-info/METADATA +0 -419
- piwave-2.0.8.dist-info/RECORD +0 -7
- {piwave-2.0.8.dist-info → piwave-2.1.0.dist-info}/WHEEL +0 -0
- {piwave-2.0.8.dist-info → piwave-2.1.0.dist-info}/licenses/LICENSE +0 -0
- {piwave-2.0.8.dist-info → piwave-2.1.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,743 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: piwave
|
|
3
|
+
Version: 2.1.0
|
|
4
|
+
Summary: A python module to broadcast radio waves with your Raspberry Pi.
|
|
5
|
+
Home-page: https://github.com/douxxtech/piwave
|
|
6
|
+
Author: Douxx
|
|
7
|
+
Author-email: douxx@douxx.tech
|
|
8
|
+
License: GPL-3.0-or-later
|
|
9
|
+
Project-URL: Bug Reports, https://github.com/douxxtech/piwave/issues
|
|
10
|
+
Project-URL: Source, https://github.com/douxxtech/piwave
|
|
11
|
+
Keywords: raspberry pi,radio,fm,rds,streaming,audio,broadcast
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
17
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
18
|
+
Requires-Python: >=3.7
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=6.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=2.0; extra == "dev"
|
|
24
|
+
Requires-Dist: black>=22.0; extra == "dev"
|
|
25
|
+
Requires-Dist: flake8>=4.0; extra == "dev"
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
<div align=center>
|
|
29
|
+
<img alt="PiWave image" src="https://piwave.xyz/static/img/logo.png"/>
|
|
30
|
+
<h1>PiWave</h1>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
**PiWave** is a Python module designed to manage and control your Raspberry Pi radio using multiple FM transmission backends. It provides a unified interface for broadcasting audio files with multiple backends support and RDS (Radio Data System) support.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Multi-Backend Architecture**: Supports multiple backends for different actions
|
|
38
|
+
- **Wide Frequency Support**: 1-250 MHz coverage through different backends
|
|
39
|
+
- **RDS Support**: Program Service, Radio Text, and Program Identifier broadcasting
|
|
40
|
+
- **Smart Backend Selection**: Automatically chooses the best backend to suit your needs
|
|
41
|
+
- **Audio Format Support**: Converts most audio formats (MP3, FLAC, M4A, etc.) to WAV
|
|
42
|
+
- **Real-time Settings Updates**: Change frequency, RDS data, and settings without restart
|
|
43
|
+
- **Advanced Playback Control**: Play, pause, resume, stop, and loop functionality
|
|
44
|
+
- **CLI Interface**: Command-line tools for backend management and broadcasting
|
|
45
|
+
- **Detailed Logging**: Debug mode with comprehensive error handling
|
|
46
|
+
- **Event Callbacks**: Custom handlers for track changes and errors
|
|
47
|
+
- **Non-blocking Operation**: Threading-based playback with status monitoring
|
|
48
|
+
|
|
49
|
+
## Supported Backends
|
|
50
|
+
|
|
51
|
+
### PiFmRds Backend
|
|
52
|
+
- **Frequency Range**: 80.0 - 108.0 MHz (Standard FM band)
|
|
53
|
+
- **RDS Support**: ✅ Full support (PS, RT, PI)
|
|
54
|
+
- **Repository**: [ChristopheJacquet/PiFmRds](https://github.com/ChristopheJacquet/PiFmRds)
|
|
55
|
+
- **Best For**: Standard FM broadcasting with RDS features
|
|
56
|
+
|
|
57
|
+
### FmTransmitter Backend
|
|
58
|
+
- **Frequency Range**: 1.0 - 250.0 MHz (Extended range)
|
|
59
|
+
- **RDS Support**: ❌ No RDS support
|
|
60
|
+
- **Repository**: [markondej/fm_transmitter](https://github.com/markondej/fm_transmitter)
|
|
61
|
+
- **Best For**: Non-standard frequencies and experimental broadcasting
|
|
62
|
+
|
|
63
|
+
## Hardware Installation
|
|
64
|
+
|
|
65
|
+
To use PiWave for broadcasting, you need to set up the hardware correctly:
|
|
66
|
+
|
|
67
|
+
1. **Connect the Antenna**:
|
|
68
|
+
- Attach a cable or antenna to GPIO 4 (Pin 7) on the Raspberry Pi
|
|
69
|
+
- Ensure secure connection for optimal signal quality
|
|
70
|
+
- Use appropriate antenna length for your target frequency
|
|
71
|
+
|
|
72
|
+
2. **GPIO Configuration**:
|
|
73
|
+
- GPIO 4 (Pin 7) is used for FM signal output
|
|
74
|
+
- No additional hardware modifications required
|
|
75
|
+
|
|
76
|
+
## Installation
|
|
77
|
+
|
|
78
|
+
> [!WARNING]
|
|
79
|
+
> **Legal Disclaimer**: Broadcasting radio signals may be subject to local regulations and laws. It is your responsibility to ensure compliance with all applicable legal requirements in your area. Unauthorized broadcasting may result in legal consequences, including fines or penalties.
|
|
80
|
+
>
|
|
81
|
+
> **Liability**: The author is not responsible for any damage, loss, or legal issues arising from the use of this software. Users accept all risks and liabilities associated with operation and broadcasting capabilities.
|
|
82
|
+
|
|
83
|
+
### Quick Installation (Recommended)
|
|
84
|
+
|
|
85
|
+
Use the automated installer script:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
curl -sL https://setup.piwave.xyz/ | sudo bash
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This installs PiWave dependencies and the PiFmRds backend automatically.
|
|
92
|
+
|
|
93
|
+
#### Advanced Installation Options
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Install with fm_transmitter backend (may affect system stability)
|
|
97
|
+
curl -sL https://setup.piwave.xyz/ | sudo bash -s -- --install_fmt
|
|
98
|
+
|
|
99
|
+
# Skip confirmation prompts
|
|
100
|
+
curl -sL https://setup.piwave.xyz/ | sudo bash -s -- --install_fmt --no-wait
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### Uninstallation
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
curl -sL https://setup.piwave.xyz/uninstall | sudo bash
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Manual Installation
|
|
110
|
+
|
|
111
|
+
1. **Install Dependencies**:
|
|
112
|
+
```bash
|
|
113
|
+
sudo apt update
|
|
114
|
+
sudo apt install -y python3 python3-pip ffmpeg git make libsndfile1-dev
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
2. **Install PiWave**:
|
|
118
|
+
```bash
|
|
119
|
+
# Create virtual environment (recommended)
|
|
120
|
+
python3 -m venv ~/piwave-env
|
|
121
|
+
source ~/piwave-env/bin/activate
|
|
122
|
+
|
|
123
|
+
# Install PiWave
|
|
124
|
+
pip install git+https://github.com/douxxtech/piwave.git
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
3. **Install Backends**:
|
|
128
|
+
|
|
129
|
+
**PiFmRds** (Recommended):
|
|
130
|
+
```bash
|
|
131
|
+
git clone https://github.com/ChristopheJacquet/PiFmRds /opt/PiWave/PiFmRds
|
|
132
|
+
cd /opt/PiWave/PiFmRds/src
|
|
133
|
+
make
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**FmTransmitter** (Optional):
|
|
137
|
+
```bash
|
|
138
|
+
sudo apt install -y libraspberrypi-dev
|
|
139
|
+
git clone https://github.com/markondej/fm_transmitter /opt/PiWave/fm_transmitter
|
|
140
|
+
cd /opt/PiWave/fm_transmitter
|
|
141
|
+
make
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Backend Management
|
|
145
|
+
|
|
146
|
+
### CLI Commands
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
# Search for available backends on system
|
|
150
|
+
python3 -m piwave search
|
|
151
|
+
|
|
152
|
+
# List cached backends
|
|
153
|
+
python3 -m piwave list
|
|
154
|
+
|
|
155
|
+
# Manually add backend executable path
|
|
156
|
+
python3 -m piwave add pi_fm_rds /path/to/pi_fm_rds
|
|
157
|
+
|
|
158
|
+
# Show package information
|
|
159
|
+
python3 -m piwave info
|
|
160
|
+
|
|
161
|
+
# Broadcast a file directly
|
|
162
|
+
python3 -m piwave broadcast song.mp3 --frequency 101.5 --ps "MyRadio"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Programmatic Backend Discovery
|
|
166
|
+
|
|
167
|
+
```python
|
|
168
|
+
from piwave.backends import discover_backends, list_backends, search_backends
|
|
169
|
+
|
|
170
|
+
# Load cached backends
|
|
171
|
+
discover_backends()
|
|
172
|
+
|
|
173
|
+
# Search for new backends (ignores cache)
|
|
174
|
+
search_backends()
|
|
175
|
+
|
|
176
|
+
# List available backends with details
|
|
177
|
+
backends_info = list_backends()
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Quick Start
|
|
181
|
+
|
|
182
|
+
### Basic Usage
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
from piwave import PiWave
|
|
186
|
+
|
|
187
|
+
# Initialize with automatic backend selection
|
|
188
|
+
pw = PiWave(
|
|
189
|
+
frequency=90.0,
|
|
190
|
+
ps="MyRadio",
|
|
191
|
+
rt="Playing great music",
|
|
192
|
+
pi="ABCD",
|
|
193
|
+
debug=True
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
# Play a single audio file
|
|
197
|
+
pw.play("song.mp3")
|
|
198
|
+
|
|
199
|
+
# Stop playback
|
|
200
|
+
pw.stop()
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Backend Selection
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from piwave import PiWave
|
|
207
|
+
|
|
208
|
+
# Automatic selection (recommended)
|
|
209
|
+
pw = PiWave(frequency=95.0, backend="auto")
|
|
210
|
+
|
|
211
|
+
# Force specific backend
|
|
212
|
+
pw = PiWave(frequency=95.0, backend="pi_fm_rds")
|
|
213
|
+
|
|
214
|
+
# Extended frequency range with fm_transmitter
|
|
215
|
+
pw = PiWave(frequency=150.0, backend="fm_transmitter")
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Real-time Settings Updates
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
from piwave import PiWave
|
|
222
|
+
|
|
223
|
+
pw = PiWave()
|
|
224
|
+
|
|
225
|
+
# Update multiple settings at once
|
|
226
|
+
pw.update(
|
|
227
|
+
frequency=101.5,
|
|
228
|
+
ps="NewName",
|
|
229
|
+
rt="Updated radio text",
|
|
230
|
+
debug=True
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Individual setting updates
|
|
234
|
+
pw.update(frequency=102.1)
|
|
235
|
+
pw.update(ps="Radio2024")
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Advanced Playback Control
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
from piwave import PiWave
|
|
242
|
+
|
|
243
|
+
pw = PiWave(frequency=95.0, loop=True)
|
|
244
|
+
|
|
245
|
+
# Playback control
|
|
246
|
+
pw.play("music.mp3")
|
|
247
|
+
pw.pause()
|
|
248
|
+
pw.resume()
|
|
249
|
+
pw.stop()
|
|
250
|
+
|
|
251
|
+
# Status monitoring
|
|
252
|
+
status = pw.get_status()
|
|
253
|
+
print(f"Playing: {status['is_playing']}")
|
|
254
|
+
print(f"Current backend: {status['current_backend']}")
|
|
255
|
+
print(f"Backend supports RDS: {status['backend_supports_rds']}")
|
|
256
|
+
print(f"Frequency range: {status['backend_frequency_range']}")
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Complete Examples
|
|
260
|
+
|
|
261
|
+
<details>
|
|
262
|
+
<summary>Click to expand examples</summary>
|
|
263
|
+
|
|
264
|
+
### Multi-Backend Radio Station
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from piwave import PiWave
|
|
268
|
+
import os
|
|
269
|
+
import time
|
|
270
|
+
|
|
271
|
+
def smart_radio_station():
|
|
272
|
+
"""Automatically selects best backend for each frequency"""
|
|
273
|
+
|
|
274
|
+
stations = [
|
|
275
|
+
{"freq": 88.5, "name": "Jazz FM", "file": "jazz.mp3"},
|
|
276
|
+
{"freq": 101.5, "name": "Rock Radio", "file": "rock.mp3"},
|
|
277
|
+
{"freq": 150.0, "name": "Experimental", "file": "experimental.wav"}
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
for station in stations:
|
|
281
|
+
try:
|
|
282
|
+
# PiWave automatically selects the best backend
|
|
283
|
+
pw = PiWave(
|
|
284
|
+
frequency=station["freq"],
|
|
285
|
+
ps=station["name"][:8], # Max 8 chars
|
|
286
|
+
rt=f"Broadcasting {station['name']}",
|
|
287
|
+
backend="auto" # Let PiWave choose
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
print(f"Starting {station['name']} on {station['freq']}MHz")
|
|
291
|
+
print(f"Using backend: {pw.get_status()['current_backend']}")
|
|
292
|
+
|
|
293
|
+
if os.path.exists(station["file"]):
|
|
294
|
+
pw.play(station["file"])
|
|
295
|
+
|
|
296
|
+
# Wait for completion or user interrupt
|
|
297
|
+
while pw.get_status()['is_playing']:
|
|
298
|
+
time.sleep(1)
|
|
299
|
+
|
|
300
|
+
print(f"{station['name']} completed")
|
|
301
|
+
else:
|
|
302
|
+
print(f"File {station['file']} not found")
|
|
303
|
+
|
|
304
|
+
pw.cleanup()
|
|
305
|
+
|
|
306
|
+
except Exception as e:
|
|
307
|
+
print(f"Error with {station['name']}: {e}")
|
|
308
|
+
|
|
309
|
+
if __name__ == "__main__":
|
|
310
|
+
smart_radio_station()
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Text-to-Speech Radio with Backend Selection
|
|
314
|
+
|
|
315
|
+
```python
|
|
316
|
+
from gtts import gTTS
|
|
317
|
+
from piwave import PiWave
|
|
318
|
+
from pydub import AudioSegment
|
|
319
|
+
import os
|
|
320
|
+
import sys
|
|
321
|
+
import time
|
|
322
|
+
|
|
323
|
+
def tts_radio():
|
|
324
|
+
"""Text-to-speech radio with automatic backend selection using update() method"""
|
|
325
|
+
|
|
326
|
+
print("=" * 50)
|
|
327
|
+
print("TTS Radio, original: https://git.new/SEdemCA")
|
|
328
|
+
print("=" * 50)
|
|
329
|
+
|
|
330
|
+
pw = None
|
|
331
|
+
wav_file = "tts_radio.wav"
|
|
332
|
+
|
|
333
|
+
try:
|
|
334
|
+
# Initialize PiWave once with default settings
|
|
335
|
+
print("Initializing PiWave...")
|
|
336
|
+
pw = PiWave(
|
|
337
|
+
frequency=90.0, # Default frequency
|
|
338
|
+
ps="TTS-FM",
|
|
339
|
+
rt="Text-to-Speech Radio",
|
|
340
|
+
backend="auto",
|
|
341
|
+
silent=False
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
print("PiWave initialized successfully!")
|
|
345
|
+
print(f"Initial backend: {pw.get_status()['current_backend']}")
|
|
346
|
+
print("=" * 50)
|
|
347
|
+
|
|
348
|
+
while True:
|
|
349
|
+
print("\n" + "=" * 30)
|
|
350
|
+
text = input("Text to broadcast: ").strip()
|
|
351
|
+
if not text:
|
|
352
|
+
print("No text entered, skipping...\n")
|
|
353
|
+
continue
|
|
354
|
+
|
|
355
|
+
try:
|
|
356
|
+
freq = float(input("Frequency (MHz): "))
|
|
357
|
+
except ValueError:
|
|
358
|
+
print("Invalid frequency, please enter a number.\n")
|
|
359
|
+
continue
|
|
360
|
+
|
|
361
|
+
# Let user choose backend or use auto
|
|
362
|
+
backend_choice = input("Backend (auto/pi_fm_rds/fm_transmitter) [auto]: ").strip()
|
|
363
|
+
if not backend_choice:
|
|
364
|
+
backend_choice = "auto"
|
|
365
|
+
|
|
366
|
+
# Generate TTS
|
|
367
|
+
print("Generating speech...")
|
|
368
|
+
mp3_file = "temp_tts.mp3"
|
|
369
|
+
tts = gTTS(text=text, lang="en", slow=False)
|
|
370
|
+
tts.save(mp3_file)
|
|
371
|
+
|
|
372
|
+
# Convert to WAV
|
|
373
|
+
sound = AudioSegment.from_mp3(mp3_file)
|
|
374
|
+
sound.export(wav_file, format="wav")
|
|
375
|
+
os.remove(mp3_file)
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
# update pw settings
|
|
379
|
+
|
|
380
|
+
update_params = {
|
|
381
|
+
'frequency': freq,
|
|
382
|
+
'rt': text[:64]
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
update_params['backend'] = backend_choice
|
|
386
|
+
|
|
387
|
+
print("Updating broadcast settings...")
|
|
388
|
+
pw.update(**update_params)
|
|
389
|
+
|
|
390
|
+
status = pw.get_status()
|
|
391
|
+
print(f"\nBroadcast Configuration:")
|
|
392
|
+
print(f"Frequency: {freq}MHz")
|
|
393
|
+
print(f"Backend: {status['current_backend']}")
|
|
394
|
+
print(f"RDS Support: {'Yes' if status['backend_supports_rds'] else 'No'}")
|
|
395
|
+
print(f"Text: {text}")
|
|
396
|
+
print("=" * 50)
|
|
397
|
+
|
|
398
|
+
pw.play(wav_file)
|
|
399
|
+
print("Broadcasting! Press Ctrl+C to stop...\n")
|
|
400
|
+
|
|
401
|
+
# Wait for completion
|
|
402
|
+
while pw.get_status()['is_playing']:
|
|
403
|
+
time.sleep(0.5)
|
|
404
|
+
|
|
405
|
+
print("Broadcast completed!\n")
|
|
406
|
+
|
|
407
|
+
except Exception as e:
|
|
408
|
+
print(f"Update/Broadcast error: {e}")
|
|
409
|
+
print("Continuing with current settings...\n")
|
|
410
|
+
continue
|
|
411
|
+
|
|
412
|
+
except KeyboardInterrupt:
|
|
413
|
+
print("\nStopped by user.")
|
|
414
|
+
except Exception as e:
|
|
415
|
+
print(f"Initialization error: {e}")
|
|
416
|
+
print("Make sure you're running on a Raspberry Pi as root with PiWave dependencies installed.")
|
|
417
|
+
finally:
|
|
418
|
+
if pw:
|
|
419
|
+
pw.cleanup()
|
|
420
|
+
|
|
421
|
+
# Cleanup temp files
|
|
422
|
+
for temp_file in [wav_file, "temp_tts.mp3"]:
|
|
423
|
+
if os.path.exists(temp_file):
|
|
424
|
+
os.remove(temp_file)
|
|
425
|
+
|
|
426
|
+
print("Cleanup completed.")
|
|
427
|
+
|
|
428
|
+
if __name__ == "__main__":
|
|
429
|
+
tts_radio()
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
</details>
|
|
433
|
+
|
|
434
|
+
## API Reference
|
|
435
|
+
|
|
436
|
+
<details>
|
|
437
|
+
<summary>Click to expand API reference</summary>
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
### PiWave Class
|
|
441
|
+
|
|
442
|
+
#### Initialization
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
PiWave(
|
|
446
|
+
frequency=90.0, # Broadcast frequency (1.0-250.0 MHz)
|
|
447
|
+
ps="PiWave", # Program Service name (max 8 chars)
|
|
448
|
+
rt="PiWave: ...", # Radio Text (max 64 chars)
|
|
449
|
+
pi="FFFF", # Program Identifier (4 hex digits)
|
|
450
|
+
debug=False, # Enable debug logging
|
|
451
|
+
silent=False, # Disable all logging
|
|
452
|
+
loop=False, # Loop current track continuously
|
|
453
|
+
backend="auto", # Backend selection ("auto", "pi_fm_rds", "fm_transmitter")
|
|
454
|
+
on_track_change=None, # Callback for track changes
|
|
455
|
+
on_error=None # Callback for errors
|
|
456
|
+
)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### Core Methods
|
|
460
|
+
|
|
461
|
+
##### `play(file_path: str) -> bool`
|
|
462
|
+
Start playing an audio file with automatic format conversion.
|
|
463
|
+
|
|
464
|
+
```python
|
|
465
|
+
pw.play("song.mp3") # Returns True if started successfully
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
##### `stop()`
|
|
469
|
+
Stop all playback and clean up processes.
|
|
470
|
+
|
|
471
|
+
```python
|
|
472
|
+
pw.stop()
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
##### `pause()` / `resume()`
|
|
476
|
+
Pause and resume playback control.
|
|
477
|
+
|
|
478
|
+
```python
|
|
479
|
+
pw.pause()
|
|
480
|
+
pw.resume()
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
##### `update(**kwargs)`
|
|
484
|
+
Update settings in real-time. Accepts any initialization parameter.
|
|
485
|
+
|
|
486
|
+
```python
|
|
487
|
+
pw.update(frequency=101.5, ps="NewName", backend="fm_transmitter")
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
##### `get_status() -> dict`
|
|
491
|
+
Get comprehensive status information.
|
|
492
|
+
|
|
493
|
+
```python
|
|
494
|
+
status = pw.get_status()
|
|
495
|
+
# Returns:
|
|
496
|
+
{
|
|
497
|
+
'is_playing': bool,
|
|
498
|
+
'frequency': float,
|
|
499
|
+
'current_file': str|None,
|
|
500
|
+
'current_backend': str,
|
|
501
|
+
'backend_frequency_range': str,
|
|
502
|
+
'backend_supports_rds': bool,
|
|
503
|
+
'available_backends': list,
|
|
504
|
+
'ps': str,
|
|
505
|
+
'rt': str,
|
|
506
|
+
'pi': str,
|
|
507
|
+
'loop': bool
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
##### `cleanup()`
|
|
512
|
+
Clean up resources and temporary files.
|
|
513
|
+
|
|
514
|
+
```python
|
|
515
|
+
pw.cleanup() # Called automatically on object destruction
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Backend Management
|
|
519
|
+
|
|
520
|
+
#### Discovery Functions
|
|
521
|
+
|
|
522
|
+
```python
|
|
523
|
+
from piwave.backends import discover_backends, search_backends, list_backends
|
|
524
|
+
|
|
525
|
+
# Load cached backend availability
|
|
526
|
+
discover_backends()
|
|
527
|
+
|
|
528
|
+
# Perform fresh search (updates cache)
|
|
529
|
+
search_backends()
|
|
530
|
+
|
|
531
|
+
# List available backends with details
|
|
532
|
+
backend_info = list_backends()
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
#### Backend Selection
|
|
536
|
+
|
|
537
|
+
```python
|
|
538
|
+
from piwave.backends import get_best_backend
|
|
539
|
+
|
|
540
|
+
# Get best backend for specific frequency
|
|
541
|
+
backend_name = get_best_backend("file_broadcast", 95.0)
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
### Command Line Interface
|
|
545
|
+
|
|
546
|
+
```bash
|
|
547
|
+
# Backend management
|
|
548
|
+
python3 -m piwave search # Search for backends
|
|
549
|
+
python3 -m piwave list # List cached backends
|
|
550
|
+
python3 -m piwave add pi_fm_rds /path/exe # Add backend path
|
|
551
|
+
python3 -m piwave info # Package information
|
|
552
|
+
|
|
553
|
+
# Direct broadcasting
|
|
554
|
+
python3 -m piwave broadcast file.mp3 \
|
|
555
|
+
--frequency 101.5 \
|
|
556
|
+
--ps "MyRadio" \
|
|
557
|
+
--rt "Great Music" \
|
|
558
|
+
--pi "ABCD" \
|
|
559
|
+
--backend auto \
|
|
560
|
+
--loop \
|
|
561
|
+
--debug
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## Error Handling
|
|
565
|
+
|
|
566
|
+
PiWave includes comprehensive error handling:
|
|
567
|
+
|
|
568
|
+
- **Environment Validation**: Raspberry Pi and root access checks
|
|
569
|
+
- **Backend Validation**: Automatic detection and compatibility verification
|
|
570
|
+
- **Frequency Validation**: Ensures frequency is within backend's supported range
|
|
571
|
+
- **File Validation**: Checks file existence and format compatibility
|
|
572
|
+
- **Process Management**: Clean process termination and resource cleanup
|
|
573
|
+
- **Exception Callbacks**: Custom error handlers for applications
|
|
574
|
+
|
|
575
|
+
### Common Error Scenarios
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
from piwave import PiWave, PiWaveError
|
|
579
|
+
|
|
580
|
+
try:
|
|
581
|
+
pw = PiWave(frequency=50.0, backend="pi_fm_rds") # Outside range
|
|
582
|
+
except PiWaveError as e:
|
|
583
|
+
print(f"Configuration error: {e}")
|
|
584
|
+
# Try with auto backend selection
|
|
585
|
+
pw = PiWave(frequency=50.0, backend="auto")
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
## Backend Development
|
|
589
|
+
|
|
590
|
+
### Creating Custom Backends
|
|
591
|
+
|
|
592
|
+
```python
|
|
593
|
+
from piwave.backends.base import Backend
|
|
594
|
+
|
|
595
|
+
class CustomBackend(Backend):
|
|
596
|
+
@property
|
|
597
|
+
def name(self):
|
|
598
|
+
return "custom_backend"
|
|
599
|
+
|
|
600
|
+
@property
|
|
601
|
+
def frequency_range(self):
|
|
602
|
+
return (50.0, 200.0) # MHz range
|
|
603
|
+
|
|
604
|
+
@property
|
|
605
|
+
def supports_rds(self):
|
|
606
|
+
return True # RDS capability
|
|
607
|
+
|
|
608
|
+
def _get_executable_name(self):
|
|
609
|
+
return "my_transmitter"
|
|
610
|
+
|
|
611
|
+
def _get_search_paths(self):
|
|
612
|
+
return ["/opt", "/usr/local/bin", "/usr/bin"]
|
|
613
|
+
|
|
614
|
+
def build_command(self, wav_file: str):
|
|
615
|
+
cmd = ['sudo', self.required_executable, '-f', str(self.frequency)]
|
|
616
|
+
if self.supports_rds and self.ps:
|
|
617
|
+
cmd.extend(['-ps', self.ps])
|
|
618
|
+
cmd.append(wav_file)
|
|
619
|
+
return cmd
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Backend Registration
|
|
623
|
+
|
|
624
|
+
```python
|
|
625
|
+
from piwave.backends import backend_classes
|
|
626
|
+
|
|
627
|
+
# Register custom backend
|
|
628
|
+
backend_classes["custom_backend"] = CustomBackend
|
|
629
|
+
|
|
630
|
+
# Re-discover backends
|
|
631
|
+
from piwave.backends import discover_backends
|
|
632
|
+
discover_backends()
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
</details>
|
|
636
|
+
|
|
637
|
+
## Troubleshooting
|
|
638
|
+
|
|
639
|
+
### Common Issues
|
|
640
|
+
|
|
641
|
+
1. **"No suitable backend found"**
|
|
642
|
+
```bash
|
|
643
|
+
python3 -m piwave search # Refresh backend cache
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
2. **"Backend doesn't support frequency"**
|
|
647
|
+
```python
|
|
648
|
+
# Check supported ranges
|
|
649
|
+
python3 -m piwave list
|
|
650
|
+
|
|
651
|
+
# Use auto selection
|
|
652
|
+
pw = PiWave(frequency=your_freq, backend="auto")
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
3. **"Process failed to start"**
|
|
656
|
+
- Ensure running as root: `sudo python3 your_script.py`
|
|
657
|
+
- Verify backend installation: `python3 -m piwave list`
|
|
658
|
+
- Check executable permissions
|
|
659
|
+
|
|
660
|
+
4. **Audio conversion issues**
|
|
661
|
+
- Install FFmpeg: `sudo apt install ffmpeg`
|
|
662
|
+
- Check file format support
|
|
663
|
+
- Verify file permissions
|
|
664
|
+
|
|
665
|
+
### Debug Mode
|
|
666
|
+
|
|
667
|
+
Enable comprehensive logging:
|
|
668
|
+
|
|
669
|
+
```python
|
|
670
|
+
pw = PiWave(debug=True)
|
|
671
|
+
# or
|
|
672
|
+
pw.update(debug=True)
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
### Backend Path Issues
|
|
676
|
+
|
|
677
|
+
Manually specify backend paths:
|
|
678
|
+
|
|
679
|
+
```bash
|
|
680
|
+
# Find backend executable
|
|
681
|
+
sudo find /opt -name "pi_fm_rds" -type f
|
|
682
|
+
|
|
683
|
+
# Add to PiWave
|
|
684
|
+
python3 -m piwave add pi_fm_rds /opt/PiWave/PiFmRds/src/pi_fm_rds
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
## Performance Notes
|
|
688
|
+
|
|
689
|
+
- **Backend Selection**: `pi_fm_rds` generally provides better stability for standard FM frequencies
|
|
690
|
+
- **Audio Conversion**: WAV files play immediately; other formats require conversion time
|
|
691
|
+
- **Memory Usage**: Large audio files are streamed, not loaded entirely into memory
|
|
692
|
+
- **CPU Impact**: FM transmission is CPU-intensive; avoid other heavy processes during broadcast
|
|
693
|
+
|
|
694
|
+
## Requirements
|
|
695
|
+
|
|
696
|
+
- Raspberry Pi (any model with GPIO)
|
|
697
|
+
- Root access (`sudo`)
|
|
698
|
+
- Python 3.7+
|
|
699
|
+
- FFmpeg for audio conversion
|
|
700
|
+
- At least one backend installed (PiFmRds or fm_transmitter)
|
|
701
|
+
|
|
702
|
+
### System Dependencies
|
|
703
|
+
|
|
704
|
+
```bash
|
|
705
|
+
sudo apt install -y python3 python3-pip ffmpeg git make libsndfile1-dev
|
|
706
|
+
```
|
|
707
|
+
|
|
708
|
+
### Optional Dependencies
|
|
709
|
+
|
|
710
|
+
```bash
|
|
711
|
+
# For extended frequency range
|
|
712
|
+
sudo apt install -y libraspberrypi-dev
|
|
713
|
+
|
|
714
|
+
# For Python virtual environment (recommended)
|
|
715
|
+
sudo apt install -y python3-venv
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
## License
|
|
719
|
+
|
|
720
|
+
PiWave is licensed under the GNU General Public License (GPL) v3.0. See the [LICENSE](LICENSE) file for details.
|
|
721
|
+
|
|
722
|
+
## Contributing
|
|
723
|
+
|
|
724
|
+
Contributions are welcome! Areas for contribution:
|
|
725
|
+
|
|
726
|
+
- Additional backend implementations
|
|
727
|
+
- Improved frequency range detection
|
|
728
|
+
- Enhanced RDS functionality
|
|
729
|
+
- Performance optimizations
|
|
730
|
+
- Documentation improvements
|
|
731
|
+
|
|
732
|
+
Please submit pull requests or open issues on [GitHub](https://github.com/douxxtech/piwave/issues).
|
|
733
|
+
|
|
734
|
+
## Acknowledgments
|
|
735
|
+
|
|
736
|
+
- [ChristopheJacquet/PiFmRds](https://github.com/ChristopheJacquet/PiFmRds) - Primary FM/RDS backend
|
|
737
|
+
- [markondej/fm_transmitter](https://github.com/markondej/fm_transmitter) - Extended frequency backend
|
|
738
|
+
|
|
739
|
+
---
|
|
740
|
+
|
|
741
|
+
**PiWave** - FM Broadcasting module for Raspberry Pi
|
|
742
|
+
|
|
743
|
+

|