Source code for toio.cube.api.sound
# -*- coding: utf-8 -*-
# ************************************************************
#
# sound.py
#
# Copyright 2022 Sony Interactive Entertainment Inc.
#
# ************************************************************
import struct
from dataclasses import dataclass
from enum import IntEnum
from typing import Union
from toio.cube.api.base_class import CubeCharacteristic, CubeCommand
from toio.device_interface import CubeInterface, GattReadData
from toio.logger import get_toio_logger
from toio.toio_uuid import TOIO_UUID_SOUND_CTRL
from toio.utility import clip
logger = get_toio_logger(__name__)
[docs]class SoundId(IntEnum):
"""
Sound effect ID
References:
https://toio.github.io/toio-spec/en/docs/ble_sound#sound-effect-id
"""
Enter = 0
Selected = 1
Cancel = 2
Cursor = 3
MatIn = 4
MatOut = 5
Get1 = 6
Get2 = 7
Get3 = 8
Effect1 = 9
Effect2 = 10
[docs]class Note(IntEnum):
"""
Midi notes
References:
https://toio.github.io/toio-spec/en/docs/ble_sound#midi-note-number-and-note-name
"""
C0 = 0
CS0 = 1
D0 = 2
DS0 = 3
E0 = 4
F0 = 5
FS0 = 6
G0 = 7
GS0 = 8
A0 = 9
AS0 = 10
B0 = 11
C1 = 12
CS1 = 13
D1 = 14
DS1 = 15
E1 = 16
F1 = 17
FS1 = 18
G1 = 19
GS1 = 20
A1 = 21
AS1 = 22
B1 = 23
C2 = 24
CS2 = 25
D2 = 26
DS2 = 27
E2 = 28
F2 = 29
FS2 = 30
G2 = 31
GS2 = 32
A2 = 33
AS2 = 34
B2 = 35
C3 = 36
CS3 = 37
D3 = 38
DS3 = 39
E3 = 40
F3 = 41
FS3 = 42
G3 = 43
GS3 = 44
A3 = 45
AS3 = 46
B3 = 47
C4 = 48
CS4 = 49
D4 = 50
DS4 = 51
E4 = 52
F4 = 53
FS4 = 54
G4 = 55
GS4 = 56
A4 = 57
AS4 = 58
B4 = 59
C5 = 60
CS5 = 61
D5 = 62
DS5 = 63
E5 = 64
F5 = 65
FS5 = 66
G5 = 67
GS5 = 68
A5 = 69
AS5 = 70
B5 = 71
C6 = 72
CS6 = 73
D6 = 74
DS6 = 75
E6 = 76
F6 = 77
FS6 = 78
G6 = 79
GS6 = 80
A6 = 81
AS6 = 82
B6 = 83
C7 = 84
CS7 = 85
D7 = 86
DS7 = 87
E7 = 88
F7 = 89
FS7 = 90
G7 = 91
GS7 = 92
A7 = 93
AS7 = 94
B7 = 95
C8 = 96
CS8 = 97
D8 = 98
DS8 = 99
E8 = 100
F8 = 101
FS8 = 102
G8 = 103
GS8 = 104
A8 = 105
AS8 = 106
B8 = 107
C9 = 108
CS9 = 109
D9 = 110
DS9 = 111
E9 = 112
F9 = 113
FS9 = 114
G9 = 115
GS9 = 116
A9 = 117
AS9 = 118
B9 = 119
C10 = 120
CS10 = 121
D10 = 122
DS10 = 123
E10 = 124
F10 = 125
FS10 = 126
G10 = 127
NO_SOUND = 128
[docs]@dataclass
class MidiNote:
"""
Midi note
"""
duration_ms: int
"""
| Duration of sounding note:
| Any fraction less than 10ms will be truncated.
| 0 - 9: zero
| 10 - 2550: duration [ms]
"""
note: Note
"""
Midi note
"""
volume: int
"""
| Volume:
| 0: off
| 1 - 255: max volume
"""
[docs] def flatten(self):
volume = clip(self.volume, 0, 255)
duration = clip(int(self.duration_ms / 10), 0, 255)
return duration, self.note, volume
[docs]class PlaySoundEffect(CubeCommand):
"""
Play sound effect command
References:
https://toio.github.io/toio-spec/en/docs/ble_sound#playing-sound-effects
"""
_payload_id = 0x02
_converter = struct.Struct("<BBB")
[docs] def __init__(self, sound_id: int, volume: int):
self.sound_id = sound_id
self.volume = max(min(volume, 255), 0)
[docs] def __bytes__(self) -> bytes:
return self._converter.pack(self._payload_id, self.sound_id, self.volume)
[docs]class PlayMidi(CubeCommand):
"""
Play midi notes command
References:
https://toio.github.io/toio-spec/en/docs/ble_sound#playing-the-midi-note-numbers
"""
_payload_id = 0x03
_converter = struct.Struct("<BBB")
[docs] def __init__(self, repeat: int, notes: Union[list[MidiNote], tuple[MidiNote, ...]]):
self.repeat = repeat
self.notes = notes
[docs] def __bytes__(self) -> bytes:
byte_data = self._converter.pack(self._payload_id, self.repeat, len(self.notes))
for note in self.notes:
byte_data = byte_data + struct.pack("<BBB", *note.flatten())
return byte_data
[docs]class Stop(CubeCommand):
"""
Stop sound command
"""
_payload_id = 0x01
[docs] def __init__(self):
pass
[docs] def __bytes__(self) -> bytes:
return bytes(self._payload_id)
[docs]class Sound(CubeCharacteristic):
"""
Sound characteristic
References:
https://toio.github.io/toio-spec/en/docs/ble_sound
"""
[docs] @staticmethod
def is_my_data(_payload: GattReadData) -> None:
return None
[docs] def __init__(self, interface: CubeInterface):
self.interface = interface
super().__init__(interface, TOIO_UUID_SOUND_CTRL)
[docs] async def play_sound_effect(self, sound_id: SoundId, volume: int):
"""
Send play sound effect command
Args:
sound_id (SoundId): Sound ID
volume (int): Volume
"""
sound_effect = PlaySoundEffect(sound_id, volume)
await self._write(bytes(sound_effect))
[docs] async def play_midi(
self, repeat: int, midi_notes: Union[list[MidiNote], tuple[MidiNote, ...]]
):
"""
Send play midi note command
Args:
repeat (int): Number of repetitions (0: Infinite)
midi_notes (Union[list[MidiNote], tuple[MidiNote, ...]]): List of midi notes
"""
midi = PlayMidi(repeat, midi_notes)
await self._write(bytes(midi))
[docs] async def stop(self):
"""
Send sound stop command
"""
stop = Stop()
await self._write(bytes(stop))