This removes downscaling (halving-add) when multiple voices are
being mixed. To avoid clipping, and get similar behavior to before,
set the "level" of each voice to (1/voice_count).
Slow paths that were applicable to only M0 chips were removed.
As a side effect, the internal volume representation is now 0 ..
0x8000 (inclusive), which additionally makes a level of exactly 0.5
representable.
Testing performed, on PyGamer: For all 4 data cases, for stereo and
mono, for 1 and 2 voices, play pure sign waves represented as
RawSamples and view the result on a scope and through headphones.
Also, scope the amount of time spent in background tasks.
Code size: growth of +272 bytes
Performance (time in background task when mixing 2 stereo 16-bit voices):
76us per down from 135us (once per ~2.9ms long term average)
(Decrease from 4.7% to 2.4% of all CPU time)
After adding the ability to change files in an existing MP3File object,
it became apparent that at the beginning of a track some part of an
existing buffer was playing first.
I noticed that in get_buffer, the just-populated buffer wasn't being
returned, but the other one was. But still after fixing this, I heard
wrong audio at the beginning of a track, so I took the heavy duty approach
and zeroed the buffers out. That means there's a remaining bug to chase,
which is merely hidden by the memset()s.
This enables jeplayer to allocate just one MP3File at startup, rather
than have to make repeated large allocations while the application is
running.
The buffers have to be allocated their theoretical maximum, but that
doesn't matter much as all the real-life MP3 files I checked needed
that much allocation anyway.
Apparently sometimes, a proper "frame info" block is not found after
a "sync word". Keep looking for one as needed, instead of giving up
after one try.
This was one reason that the "bartlebeats" mp3s would not play.
In conversion I missed these arguments were being passed, but noticed
it when an implausible value for audio_channel was sent to mp3's
reset_buffer method.
It's not clear whether there was any negative impact to this, but it
should be fixed!
There were several problems with the way this worked -- the read_count
approach was too complicated and I made a mistake "simplifying" it from
WaveFile. And when the right channel was returned, it was off by 1 byte,
making it into static.
Instead, directly track which is the "other" channel that has data
available, and by using the right data type make the "+ channel"
arithmetic give the right result.
This requires a double cast (int16_t*)(void*) due to an alignment warning;
the alignment is now ensured manually, but the compiler doesn't make the
necessary inference that the low address bit must be clear.
When a playing mp3 is deinitted, it's possible to reach get_buffer,
but all the internal pointers are NULL. This would lead to a hard fault.
Avoid it by returning GET_BUFFER_ERROR instead.
We weren't correctly collecting the start and stop sequences. As
a result, the GC would free the space and allocate other info
there.
Thanks to JacobT on Discord for the bug report!
This code is shared by most parts, except where not all the #ifdefs
inside the tick function were present in all ports. This mostly would
have broken gamepad tick support on non-samd ports.
The "ms32" and "ms64" variants of the tick functions are introduced
because there is no 64-bit atomic read. Disabling interrupts avoids
a low probability bug where milliseconds could be off by ~49.5 days
once every ~49.5 days (2^32 ms).
Avoiding disabling interrupts when only the low 32 bits are needed is a minor
optimization.
Testing performed: on metro m4 express, USB still works and
time.monotonic_ns() still counts up
This PR refines the _bleio API. It was originally motivated by
the addition of a new CircuitPython service that enables reading
and modifying files on the device. Moving the BLE lifecycle outside
of the VM motivated a number of changes to remove heap allocations
in some APIs.
It also motivated unifying connection initiation to the Adapter class
rather than the Central and Peripheral classes which have been removed.
Adapter now handles the GAP portion of BLE including advertising, which
has moved but is largely unchanged, and scanning, which has been enhanced
to return an iterator of filtered results.
Once a connection is created (either by us (aka Central) or a remote
device (aka Peripheral)) it is represented by a new Connection class.
This class knows the current connection state and can discover and
instantiate remote Services along with their Characteristics and
Descriptors.
Relates to #586
While finding sources of clicks and buzzes in nrf i2sout, I identified
this site as one which could be long running. Reproducer code was to
play a 22.05kHz sample and repeatedly print `os.listdir('')`
These arguments are constrained to be compile-time constants, a fact
that gcc complains about under "-Og" optimization, but not in normal
builds. Declare them as enumerated types
Also, move the rendering setup code to shared-module from
shared-bindings.
In CP 5.0, displayio_display_core_set_region_to_update now starts
its own transaction, so it has to be moved outside of the transaction
started by the render call.
Otherwise, examples like the one attached to the related issue fail
because tud_hid_ready never returns true.
Testing performed: Adapted the example to nrf particle xenon (it was
handy), removed dependency on IR, verified that the problem occurred
before this change, and that it was fixed after this change.
Closes: #2048
It lets us re-use the same buffer for playing multiple files.
This also allows us to control the size of the buffer. Half of the
buffer will be used for the fist, and half for the second internal
buffer.
When nrf pwm audio is introduced, it will be called `audiopwmio`. To
enable code sharing with the existing (dac-based) `audioio`, factor
the sample and mixer types to `audiocore`.
INCOMPATIBLE CHANGE: Now, `Mixer`, `RawSample` and `WaveFile` must
be imported from `audiocore`, not `audioio`.
This also improves Palette so it stores the original RGB888 colors.
Lastly, it adds I2CDisplay as a display bus to talk over I2C. Particularly
useful for the SSD1306.
Fixes#1828. Fixes#1956
This fixes the bug that bitmap changes do not cause screen updates
and optimizes the refresh when the bitmap is simply shown on the
screen. If the bitmap is used in tiles, then changing it will
cause all TileGrids using it to do a full refresh.
Fixes#1981
Different operations to the display tree have different costs. Be
aware of these costs when optimizing your code.
* Changing tiles indices in a TileGrid will update an area
covering them all.
* Changing a palette will refresh every object that references it.
* Moving a TileGrid will update both where it was and where it moved to.
* Adding something to a Group will refresh each individual area it
covers.
* Removing things from a Group will refresh one area that covers all
previous locations. (Not separate areas like add.)
* Setting a new top level Group will refresh the entire display.
Only TileGrid moves are optimized for overlap. All other overlaps
cause sending of duplicate pixels.
This also adds flip_x, flip_y and transpose_xy to TileGrid. They
change the direction of the pixels but not the location.
Fixes#1169. Fixes#1705. Fixes#1923.
This changes the displayio pixel computation from per-pixel to
per-area. This is precursor work to updating portions of the screen
(#1169). It should provide mild speedups because bounds checks are
done once per area rather than once per pixel. Filling by area also
allows TileGrid to maintain a row-associative fill pattern even when
the display's refresh is orthogonal to it.
If one of the default pins was already in use it would crash.
The internal API has been refined to allow us to get the value
without causing an init of the singleton.
Fixes#1753
The previous code assumed HID report ids were consecutive. This is
not true in the CircuitPython descriptor where report ids are fixed
for each report type.
Fixes#1617