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))