diff --git a/docs/badge/programming.rst b/docs/badge/programming.rst index 57b99928f7588070727ed9146f494fdf6c1f5297..adccc6366f160244ca78ea70116ca8afcb341ef1 100644 --- a/docs/badge/programming.rst +++ b/docs/badge/programming.rst @@ -735,9 +735,8 @@ Currently the simulator supports the display, LEDs, the buttons, accelerometer (in 2D) and some static input values from the gyroscope, temperature sensor and pressure sensor. -It does **not** support any audio API, and in fact currently doesn't even stub -out the relevant API methods, so it will crash when attempting to run any Music -app. It also does not support positional captouch APIs. +It does **not** support most of the audio APIs. It also does not support +positional captouch APIs. To set the simulator up, clone the repository and prepare a Python virtual environment with the required packages: diff --git a/sim/fakes/bl00mbox.py b/sim/fakes/bl00mbox.py index a7f292e72284f2240f79138afb07738bd7b8765b..92bedec458c12b623f115564b0f7240b7fbdd35e 100644 --- a/sim/fakes/bl00mbox.py +++ b/sim/fakes/bl00mbox.py @@ -1,41 +1,110 @@ -class tinysynth: - def __init__(self, a): - pass +import pygame +from _sim import path_replace - def decay_ms(self, a): - pass - def decay(self, a): +class _mock(list): + def __init__(self, *args, **kwargs): pass - def waveform(self, a): - pass + def __getattr__(self, attr): + if attr in ["tone", "value"]: + return 0 + if attr in ["trigger_state"]: + return lambda *args: 0 + return _mock() - def tone(self, note): - pass + def __call__(self, *args, **kwargs): + return _mock() - def start(self): - pass - def sustain(self, a): +class Channel: + def __init__(self, id): pass - def attack_ms(self, a): - pass + def new(self, a, *args, **kwargs): + return a(self, *args, **kwargs) - def release_ms(self, a): + def clear(self): pass - def volume(self, a): - pass + mixer = None + channel_num = 0 + volume = 8000 - def stop(self): - pass +class _patches(_mock): + class sampler(_mock): + class Signals(_mock): + class Trigger(_mock): + def __init__(self, sampler): + self._sampler = sampler -class Channel: - def __init__(self, id): - pass + def start(self): + self._sampler._sound.set_volume( + self._sampler._channel.volume / 32767 + ) + self._sampler._sound.play() - def clear(self): - pass + def __init__(self, sampler): + self._sampler = sampler + self._trigger = patches.sampler.Signals.Trigger(sampler) + + @property + def trigger(self): + return self._trigger + + @trigger.setter + def trigger(self, val): + pass + + def _convert_filename(self, filename): + if filename.startswith("/flash/") or filename.startswith("/sd/"): + return filename + elif filename.startswith("/"): + return "/flash/" + filename + else: + return "/flash/sys/samples/" + filename + + def __init__(self, channel, path): + if type(path) == int: + self._signals = _mock() + return + self._sound = pygame.mixer.Sound(path_replace(self._convert_filename(path))) + self._signals = patches.sampler.Signals(self) + self._channel = channel + + @property + def signals(self): + return self._signals + + +class _helpers(_mock): + def sct_to_note_name(self, sct): + sct = sct - 18367 + 100 + octave = ((sct + 9 * 200) // 2400) + 4 + tones = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"] + tone = tones[(sct // 200) % 12] + return tone + str(octave) + + def note_name_to_sct(self, name): + tones = ["A", "Bb", "B", "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab"] + semitones = tones.index(name[0]) + if semitones > 2: + semitones -= 12 + if name[1] == "b": + octave = int(name[2:]) + semitones -= 1 + elif name[1] == "#": + octave = int(name[2:]) + semitones += 1 + else: + octave = int(name[1:]) + return 18367 + (octave - 4) * 2400 + (200 * semitones) + + def sct_to_freq(sct): + return 440 * 2 ** ((sct - 18367) / 2400) + + +plugins = _mock() +patches = _patches() +helpers = _helpers()