or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

composite-devices.mdindex.mdinput-devices.mdoutput-devices.mdpin-factories.mdspi-devices.mdsystem-monitoring.mdtone-system.mdtools.md

tone-system.mddocs/

0

# Tone System

1

2

Musical tone representation and manipulation system for use with TonalBuzzer and other audio devices. The tone system provides comprehensive support for musical notation, MIDI notes, and frequency representations.

3

4

## Core Imports

5

6

```python

7

from gpiozero.tones import Tone

8

from gpiozero import TonalBuzzer

9

```

10

11

## Capabilities

12

13

### Tone Class

14

15

Represents a frequency of sound in various musical notations.

16

17

```python { .api }

18

class Tone(float):

19

def __init__(self, value=None, *, frequency=None, midi=None, note=None):

20

"""

21

Construct a Tone from frequency, MIDI note, or note string.

22

23

Parameters:

24

- value: float, int, or str - Auto-detected value (frequency, MIDI, or note)

25

- frequency: float - Frequency in Hz (0 < freq <= 20000)

26

- midi: int - MIDI note number (0-127)

27

- note: str - Musical note string (e.g., 'A4', 'C#5', 'Bb3')

28

"""

29

30

@classmethod

31

def from_frequency(cls, freq: float) -> 'Tone':

32

"""

33

Construct a Tone from a frequency in Hz.

34

35

Parameters:

36

- freq: float - Frequency in Hz (0 < freq <= 20000)

37

38

Returns:

39

Tone object

40

"""

41

42

@classmethod

43

def from_midi(cls, midi_note: int) -> 'Tone':

44

"""

45

Construct a Tone from a MIDI note number.

46

47

Parameters:

48

- midi_note: int - MIDI note number (0-127)

49

50

Returns:

51

Tone object

52

"""

53

54

@classmethod

55

def from_note(cls, note: str) -> 'Tone':

56

"""

57

Construct a Tone from a musical note string.

58

59

Parameters:

60

- note: str - Musical note (A-G + optional sharp/flat + octave 0-9)

61

62

Returns:

63

Tone object

64

"""

65

66

@property

67

def frequency(self) -> float:

68

"""Return the frequency of the tone in Hz."""

69

70

@property

71

def midi(self) -> int:

72

"""Return the (nearest) MIDI note number (0-127)."""

73

74

@property

75

def note(self) -> str:

76

"""Return the (nearest) note string representation."""

77

78

def up(self, n: int = 1) -> 'Tone':

79

"""

80

Return the Tone n semi-tones above this frequency.

81

82

Parameters:

83

- n: int - Number of semi-tones to step up (default: 1)

84

85

Returns:

86

Tone object

87

"""

88

89

def down(self, n: int = 1) -> 'Tone':

90

"""

91

Return the Tone n semi-tones below this frequency.

92

93

Parameters:

94

- n: int - Number of semi-tones to step down (default: 1)

95

96

Returns:

97

Tone object

98

"""

99

```

100

101

## Musical Notation Support

102

103

### Note String Format

104

105

Musical notes are specified as strings with the format: `[A-G][accidental][octave]`

106

107

- **Note letter**: A, B, C, D, E, F, G

108

- **Accidental** (optional):

109

- `#` or `♯` for sharp (raises pitch by one semi-tone)

110

- `b` or `♭` for flat (lowers pitch by one semi-tone)

111

- `♮` for natural (no modification)

112

- **Octave**: 0-9 (4 is the octave containing middle C and concert A at 440Hz)

113

114

### Reference Notes

115

116

- **Concert A**: `A4` = 440Hz = MIDI note 69

117

- **Middle C**: `C4` = ~261.63Hz = MIDI note 60

118

- **Note range**: `A0` (~27.5Hz) to `G9` (~12.5KHz)

119

- **MIDI range**: 0-127 (approximately 8Hz to 12.5KHz)

120

121

## Usage Examples

122

123

### Basic Tone Construction

124

125

```python

126

from gpiozero.tones import Tone

127

128

# All these create concert A (440Hz, MIDI note 69)

129

tone1 = Tone(440.0) # From frequency

130

tone2 = Tone(69) # From MIDI (auto-detected)

131

tone3 = Tone('A4') # From note string

132

133

# Explicit construction methods

134

tone4 = Tone.from_frequency(440)

135

tone5 = Tone.from_midi(69)

136

tone6 = Tone.from_note('A4')

137

138

# Using keyword arguments

139

tone7 = Tone(frequency=440)

140

tone8 = Tone(midi=69)

141

tone9 = Tone(note='A4')

142

```

143

144

### Musical Scale Construction

145

146

```python

147

from gpiozero.tones import Tone

148

149

# C major scale starting from middle C

150

c_major_scale = [

151

Tone('C4'), # Do

152

Tone('D4'), # Re

153

Tone('E4'), # Mi

154

Tone('F4'), # Fa

155

Tone('G4'), # Sol

156

Tone('A4'), # La

157

Tone('B4'), # Ti

158

Tone('C5'), # Do (octave)

159

]

160

161

# Or using stepping methods

162

base_note = Tone('C4')

163

c_major_scale = [

164

base_note, # C4

165

base_note.up(2), # D4

166

base_note.up(4), # E4

167

base_note.up(5), # F4

168

base_note.up(7), # G4

169

base_note.up(9), # A4

170

base_note.up(11), # B4

171

base_note.up(12), # C5

172

]

173

174

# Chromatic scale (all 12 semi-tones)

175

chromatic = [Tone('C4').up(i) for i in range(13)]

176

```

177

178

### Playing Melodies with TonalBuzzer

179

180

```python

181

from gpiozero import TonalBuzzer

182

from gpiozero.tones import Tone

183

from time import sleep

184

185

buzzer = TonalBuzzer(18)

186

187

# Simple melody - Mary Had a Little Lamb

188

melody = [

189

'E4', 'D4', 'C4', 'D4',

190

'E4', 'E4', 'E4',

191

'D4', 'D4', 'D4',

192

'E4', 'G4', 'G4',

193

'E4', 'D4', 'C4', 'D4',

194

'E4', 'E4', 'E4', 'E4',

195

'D4', 'D4', 'E4', 'D4',

196

'C4'

197

]

198

199

# Play the melody

200

for note_str in melody:

201

tone = Tone(note_str)

202

buzzer.play(tone)

203

sleep(0.5)

204

buzzer.stop()

205

sleep(0.1)

206

```

207

208

### Frequency and MIDI Conversion

209

210

```python

211

from gpiozero.tones import Tone

212

213

# Convert between different representations

214

tone = Tone('A4')

215

print(f"Note: {tone.note}") # A4

216

print(f"Frequency: {tone.frequency}") # 440.0

217

print(f"MIDI: {tone.midi}") # 69

218

219

# Work with accidentals (sharps and flats)

220

sharp_note = Tone('F#4')

221

flat_note = Tone('Gb4')

222

print(f"F# frequency: {sharp_note.frequency:.2f}Hz")

223

print(f"Gb frequency: {flat_note.frequency:.2f}Hz")

224

# Note: F# and Gb are enharmonic equivalents (same frequency)

225

226

# Stepping through notes

227

base = Tone('C4')

228

print(f"C4: {base.frequency:.2f}Hz")

229

print(f"D4: {base.up(2).frequency:.2f}Hz")

230

print(f"E4: {base.up(4).frequency:.2f}Hz")

231

print(f"B3: {base.down(1).frequency:.2f}Hz")

232

```

233

234

### Musical Intervals and Chords

235

236

```python

237

from gpiozero.tones import Tone

238

239

# Major chord construction (root, major third, perfect fifth)

240

def major_chord(root_note):

241

root = Tone(root_note)

242

third = root.up(4) # Major third (4 semi-tones)

243

fifth = root.up(7) # Perfect fifth (7 semi-tones)

244

return [root, third, fifth]

245

246

# C major chord

247

c_major = major_chord('C4')

248

print(f"C major chord: {[note.note for note in c_major]}")

249

250

# Minor chord construction (root, minor third, perfect fifth)

251

def minor_chord(root_note):

252

root = Tone(root_note)

253

third = root.up(3) # Minor third (3 semi-tones)

254

fifth = root.up(7) # Perfect fifth (7 semi-tones)

255

return [root, third, fifth]

256

257

# A minor chord

258

a_minor = minor_chord('A4')

259

print(f"A minor chord: {[note.note for note in a_minor]}")

260

```

261

262

### Advanced Musical Programming

263

264

```python

265

from gpiozero import TonalBuzzer

266

from gpiozero.tones import Tone

267

from time import sleep

268

import random

269

270

buzzer = TonalBuzzer(18)

271

272

# Pentatonic scale for improvisation

273

def pentatonic_scale(root_note):

274

"""Generate pentatonic scale from root note."""

275

root = Tone(root_note)

276

intervals = [0, 2, 4, 7, 9, 12] # Pentatonic intervals

277

return [root.up(interval) for interval in intervals]

278

279

# Generate random melody from pentatonic scale

280

scale = pentatonic_scale('C4')

281

melody_length = 16

282

283

print("Playing random pentatonic melody...")

284

for _ in range(melody_length):

285

note = random.choice(scale)

286

duration = random.choice([0.25, 0.5, 0.75])

287

288

buzzer.play(note)

289

sleep(duration)

290

buzzer.stop()

291

sleep(0.1)

292

293

# Arpeggio pattern

294

def play_arpeggio(chord_notes, pattern='up', repeats=2):

295

"""Play chord notes in arpeggio pattern."""

296

if pattern == 'up':

297

notes = chord_notes

298

elif pattern == 'down':

299

notes = list(reversed(chord_notes))

300

elif pattern == 'up_down':

301

notes = chord_notes + list(reversed(chord_notes[1:-1]))

302

303

for _ in range(repeats):

304

for note in notes:

305

buzzer.play(note)

306

sleep(0.3)

307

buzzer.stop()

308

sleep(0.05)

309

310

# Play C major arpeggio

311

c_major = major_chord('C4')

312

play_arpeggio(c_major, pattern='up_down')

313

```

314

315

### Error Handling

316

317

```python

318

from gpiozero.tones import Tone

319

from gpiozero.exc import AmbiguousTone

320

import warnings

321

322

# Handle ambiguous tone warnings

323

try:

324

# Numbers below 128 may trigger ambiguity warnings

325

tone = Tone(60) # Could be 60Hz or MIDI note 60

326

except Exception as e:

327

print(f"Error: {e}")

328

329

# Suppress warnings for known values

330

with warnings.catch_warnings():

331

warnings.simplefilter("ignore", AmbiguousTone)

332

tone = Tone(60) # Treated as MIDI note 60 (middle C)

333

334

# Handle invalid inputs

335

try:

336

invalid_note = Tone('H4') # H is not a valid note

337

except ValueError as e:

338

print(f"Invalid note: {e}")

339

340

try:

341

invalid_freq = Tone(frequency=30000) # Above 20kHz limit

342

except ValueError as e:

343

print(f"Invalid frequency: {e}")

344

345

try:

346

invalid_midi = Tone(midi=200) # MIDI notes are 0-127

347

except ValueError as e:

348

print(f"Invalid MIDI note: {e}")

349

```

350

351

## Integration with GPIO Zero Devices

352

353

The Tone system integrates seamlessly with GPIO Zero's audio output devices:

354

355

```python

356

from gpiozero import TonalBuzzer, Button

357

from gpiozero.tones import Tone

358

from signal import pause

359

360

buzzer = TonalBuzzer(18)

361

button = Button(2)

362

363

# Play different notes on button press

364

notes = ['C4', 'D4', 'E4', 'F4', 'G4', 'A4', 'B4', 'C5']

365

current_note = 0

366

367

def play_next_note():

368

global current_note

369

tone = Tone(notes[current_note])

370

buzzer.play(tone)

371

current_note = (current_note + 1) % len(notes)

372

373

def stop_note():

374

buzzer.stop()

375

376

button.when_pressed = play_next_note

377

button.when_released = stop_note

378

379

pause()

380

```

381

382

The Tone system provides a powerful and intuitive way to work with musical data in GPIO Zero, supporting everything from simple note playback to complex musical composition and real-time audio synthesis.