<feed xmlns='http://www.w3.org/2005/Atom'>
<title>soundbox-go, branch main</title>
<subtitle>Go library to interface with soundbox devices</subtitle>
<id>https://cgit.xengineering.eu/soundbox-go/atom</id>
<link rel='self' href='https://cgit.xengineering.eu/soundbox-go/atom'/>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/'/>
<updated>2025-01-31T12:43:43Z</updated>
<entry>
<title>Update CHANGELOG.md for version 0.2.2</title>
<updated>2025-01-31T12:43:43Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2025-01-31T12:43:43Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=437c7ca98741bb178cedb9e940c6bcc14521d457'/>
<id>urn:sha1:437c7ca98741bb178cedb9e940c6bcc14521d457</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Fix 100 % CPU usage</title>
<updated>2025-01-12T08:25:47Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2025-01-12T08:25:47Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=788b502b1ec4382558e9996aee5b6cb7a87f76e3'/>
<id>urn:sha1:788b502b1ec4382558e9996aee5b6cb7a87f76e3</id>
<content type='text'>
The `default` statement in the PipeWire reading caused 100 % CPU usage.
Introducing a one millisecond delay there fixes the issue.
</content>
</entry>
<entry>
<title>Update CHANGELOG.md for version 0.2.1</title>
<updated>2024-12-15T11:48:33Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-15T11:48:33Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=bde7e0d767e5e0b1072d05eff58bef16f5858e28'/>
<id>urn:sha1:bde7e0d767e5e0b1072d05eff58bef16f5858e28</id>
<content type='text'>
</content>
</entry>
<entry>
<title>pipewire: Assert OS is GNU / Linux</title>
<updated>2024-12-15T11:36:59Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-15T11:25:08Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=196ed013acad91cf89b7a1e086dbbe0d158166d3'/>
<id>urn:sha1:196ed013acad91cf89b7a1e086dbbe0d158166d3</id>
<content type='text'>
PipeWire is a Linux-specific sound system. This commit adds a proper
error if the operating is not matching.
</content>
</entry>
<entry>
<title>pipewire: Make sure only one instance is running</title>
<updated>2024-12-15T11:36:59Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-15T11:21:24Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=de2694f182790dd311265e378498d586f9b3ae66'/>
<id>urn:sha1:de2694f182790dd311265e378498d586f9b3ae66</id>
<content type='text'>
The init / deinit of the PipeWire C library is not well handled for
multiple instances. Furthermore the PipeWire audio streaming relies on a
channel which is a global variable.

Both issues have to be fixed before multiple PipeWire capture instances
are allowed.
</content>
</entry>
<entry>
<title>pipewire: Fix silence dropping</title>
<updated>2024-12-15T11:36:59Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-15T11:01:53Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=2184e703ba3c3fd79e792a78a70e76daa2aa1062'/>
<id>urn:sha1:2184e703ba3c3fd79e792a78a70e76daa2aa1062</id>
<content type='text'>
It used to drop silence per channel, not silence per sample. In the
unlikely case of perfect silence on one channel and sound on the other
this would switch effectively the channels.
</content>
</entry>
<entry>
<title>pipewire: Implement capture tear-down</title>
<updated>2024-12-15T11:36:51Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-11T19:03:29Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=ca5ebc8a795114a72f4410f05b2270f24ead1d60'/>
<id>urn:sha1:ca5ebc8a795114a72f4410f05b2270f24ead1d60</id>
<content type='text'>
This closes the PipeWire process properly to not leak memory and remove
the PipeWire capture node of soundbox as soon as the context passed to
StreamPipewireContext() is closed.
</content>
</entry>
<entry>
<title>pipewire: Disable auto-connect</title>
<updated>2024-12-14T15:52:55Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-14T15:52:55Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=62d3045df3283872628b0b8b8a8caeef1c226dfa'/>
<id>urn:sha1:62d3045df3283872628b0b8b8a8caeef1c226dfa</id>
<content type='text'>
This used to connect the soundbox capture device directly to the default
input (usually system microphone). This is not wanted since the main
purpose of soundbox is not streaming the microphone but output of media
players.

Furthermore depending on the sound setup it could lead to a feedback
loop with a very loud noise.
</content>
</entry>
<entry>
<title>pipewire: Avoid unnamed struct member init</title>
<updated>2024-12-10T20:28:00Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-10T20:28:00Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=5da49db791b6d70ca5ac4154dc49e55d6219ba33'/>
<id>urn:sha1:5da49db791b6d70ca5ac4154dc49e55d6219ba33</id>
<content type='text'>
This makes the code less readable.
</content>
</entry>
<entry>
<title>Update CHANGELOG.md for release v0.2.0</title>
<updated>2024-12-08T17:37:57Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-08T17:37:57Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=957d98544686d87d9df522b35981d1055b83c0b9'/>
<id>urn:sha1:957d98544686d87d9df522b35981d1055b83c0b9</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Apply go fmt</title>
<updated>2024-12-08T17:36:10Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-08T17:36:10Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=477fb59944222966b8a9fc1d92893a336e827285'/>
<id>urn:sha1:477fb59944222966b8a9fc1d92893a336e827285</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Add examples to overview on generated API doc</title>
<updated>2024-12-08T17:34:11Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-08T17:31:56Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=f30e3668a6a65a8e5e43b3cc0b2030788b43857d'/>
<id>urn:sha1:f30e3668a6a65a8e5e43b3cc0b2030788b43857d</id>
<content type='text'>
This makes it easier for readers to find them.
</content>
</entry>
<entry>
<title>Add generate-compile-commands.sh</title>
<updated>2024-12-08T17:22:14Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-29T15:21:46Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=b76ac2daffb98fe7aea14385638150381cfb031b'/>
<id>urn:sha1:b76ac2daffb98fe7aea14385638150381cfb031b</id>
<content type='text'>
This documents the `bear` call required to produce a proper
`compile_commands.json` here.

The JSON file is not committed since it contains absolute paths. It is
used for language servers like `clangd`.
</content>
</entry>
<entry>
<title>pipewire: Fix huge latency by dropping silence</title>
<updated>2024-12-08T17:22:14Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-12-08T16:36:28Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=49f47e76a85b3477835a0adec05649507d94b2e8'/>
<id>urn:sha1:49f47e76a85b3477835a0adec05649507d94b2e8</id>
<content type='text'>
The current architecture uses the following processing:

- capture raw audio from PipeWire as unsigned 16 bit integers
- convert with a `ffmpeg` process to OGG / FLAC
- stream the `ffmpeg` output to multiple soundboxes via TCP

Only the first part is different for URL sources. Since using PipeWire
significant latency (up to 15 seconds) were measured.

It turned out that this happens exactly when zero bytes (silence) are
fed into the `ffmpeg` process. This commit avoids this by dropping those
empty samples.

It has to be made sure that only samples are dropped where both channels
are zero. Otherwise audible noise is the result.
</content>
</entry>
<entry>
<title>pipewire: Add experimental PipeWire support</title>
<updated>2024-12-08T17:22:10Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-17T11:51:15Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=06c4f8b0120f5598f9d179b8c0fea33df35659a8'/>
<id>urn:sha1:06c4f8b0120f5598f9d179b8c0fea33df35659a8</id>
<content type='text'>
This implements a PipeWire capture device which can be used as an input
source instead of the already available URL input.

Known issues with the current PipeWire support are:

- user has to connect the monitor of the default audio sink to the
  capture device manually
- correct shutdown has to be tested
- multiple instances do not work
- medium code quality requires refactoring

Since this is nevertheless usable and possible unknown bugs should be
figured out in practise soon this implementation is already added.
Bugfixes and refactoring might follow.
</content>
</entry>
<entry>
<title>Update CHANGELOG.md for version 0.1.5</title>
<updated>2024-11-29T09:21:31Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-29T09:21:31Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=4513eb614707d129824e60270bd45cdee9f04d06'/>
<id>urn:sha1:4513eb614707d129824e60270bd45cdee9f04d06</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Refactor streamContext()</title>
<updated>2024-11-29T09:16:57Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-29T09:16:57Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=f5f0d12b697e71e67a403791c1b93ffdf61e272a'/>
<id>urn:sha1:f5f0d12b697e71e67a403791c1b93ffdf61e272a</id>
<content type='text'>
This avoids an unnecessary additional for loop.
</content>
</entry>
<entry>
<title>Handle internal error correctly</title>
<updated>2024-11-29T09:06:01Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-29T09:06:01Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=97af30e8fee2274a437a13d8c2c19e4bfe29ddee'/>
<id>urn:sha1:97af30e8fee2274a437a13d8c2c19e4bfe29ddee</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Fix variable buffer size</title>
<updated>2024-11-29T08:16:57Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-29T08:16:57Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=49dc5ae3c12f2c696302171b54b130d94dea05e0'/>
<id>urn:sha1:49dc5ae3c12f2c696302171b54b130d94dea05e0</id>
<content type='text'>
The switch to `io.Copy()` to pump the data to the soundbox devices
removed the control over the buffer size of this copy action.

While for generic copy actions it is even an advantage when a big buffer
is used this is a problem for the soundbox use case. A big buffer used
during copy means that the first soundbox device gets audio data
significantly earlier than the later ones since `io.MultiWriter()` works
sequentially.

Thus this commit switches to `io.CopyBuffer()` where a buffer has to
provided. For that purpose the same buffer size is used as before the
refactoring to use `io.Copy()`.
</content>
</entry>
<entry>
<title>Replace custom code by io package functions</title>
<updated>2024-11-28T21:44:28Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-28T21:44:28Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=99031596efbcf004188217d2a9e53ac5ce33b511'/>
<id>urn:sha1:99031596efbcf004188217d2a9e53ac5ce33b511</id>
<content type='text'>
This makes use of two functions from this package:

- io.Copy()
- io.MultiWriter()

`io.Copy()` is used to move the data from whatever reader is provided.
`io.Multiwriter()` solves the issue that we need to stream to multiple
network connections at the same time (one for each soundbox).
</content>
</entry>
<entry>
<title>Handle read error after processing buffer</title>
<updated>2024-11-27T20:55:40Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-27T20:55:40Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=bb84332ef7ca114f0c608bbea7f11451f010a831'/>
<id>urn:sha1:bb84332ef7ca114f0c608bbea7f11451f010a831</id>
<content type='text'>
This is recommended by the Go standard library. One reason is that a
Reader might deliver the last couple of bytes together with the EOF
error. This is only handled correctly if the returned bytes are
processed first and the error is handled later.

[1]: https://pkg.go.dev/io#Reader
</content>
</entry>
<entry>
<title>Introduce io.Reader-based streamContext()</title>
<updated>2024-11-27T20:38:16Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-27T20:29:39Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=6b7da29eea100b92659a3f1f5df6958ebad544c8'/>
<id>urn:sha1:6b7da29eea100b92659a3f1f5df6958ebad544c8</id>
<content type='text'>
This prepares the switch to adding more sources than web URLs.
Everything providing an io.Reader can then simply use this internal
function in the background to avoid code duplication.
</content>
</entry>
<entry>
<title>Fix streaming only via first interface candidate</title>
<updated>2024-11-17T11:48:00Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-17T11:45:35Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=d4cf5ed8e38d1e5d0b8110e8959e002c216f073f'/>
<id>urn:sha1:d4cf5ed8e38d1e5d0b8110e8959e002c216f073f</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Update CHANGELOG.md for version 0.1.4</title>
<updated>2024-11-10T15:40:19Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-10T15:40:19Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=a95013aa0813d1644aa15780f360fa4dad2223a8'/>
<id>urn:sha1:a95013aa0813d1644aa15780f360fa4dad2223a8</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Add CHANGELOG.md</title>
<updated>2024-11-10T15:36:26Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-10T15:36:26Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=6f51073ecb44f5476e37e3d5ab8d87e917116bae'/>
<id>urn:sha1:6f51073ecb44f5476e37e3d5ab8d87e917116bae</id>
<content type='text'>
This keeps track of the changes to this repository.
</content>
</entry>
<entry>
<title>Avoid unnecessary variable for args</title>
<updated>2024-11-10T14:54:59Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-10T14:54:59Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=93d3ce8ca7d335fb15da64981321f82891c1493c'/>
<id>urn:sha1:93d3ce8ca7d335fb15da64981321f82891c1493c</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Use Go code for output network stream</title>
<updated>2024-11-10T14:53:44Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-08T19:56:56Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=36a2fda37e837bc39c88a2fa38678b0f3bded041'/>
<id>urn:sha1:36a2fda37e837bc39c88a2fa38678b0f3bded041</id>
<content type='text'>
Calling the external program `ffmpeg` should be avoided completely in
the future to make soundbox-go a pure Go code base. `ffmpeg` provides
the following functionality to soundbox-go:

- web radio input stream transport
- re-encoding of the audio stream
- output stream transport to soundbox devices

The last part should be replaced with this commit as a first step.
</content>
</entry>
<entry>
<title>Rename cmd to args</title>
<updated>2024-11-08T19:35:24Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-11-08T19:35:24Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=e30a1b0768d35ec70e5d2c00e6ae87c0f267b881'/>
<id>urn:sha1:e30a1b0768d35ec70e5d2c00e6ae87c0f267b881</id>
<content type='text'>
This variable only contains the arguments for the called program. Thus
it should be named like this.
</content>
</entry>
<entry>
<title>Fix outdated README.md</title>
<updated>2024-10-31T21:37:26Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-10-31T21:37:26Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=702c39c9bb3a6441de366aa30842960741017615'/>
<id>urn:sha1:702c39c9bb3a6441de366aa30842960741017615</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Change module name and restructure content</title>
<updated>2024-10-31T21:10:36Z</updated>
<author>
<name>xegineering</name>
<email>me@xegineering.eu</email>
</author>
<published>2024-10-31T21:10:36Z</published>
<link rel='alternate' type='text/html' href='https://cgit.xengineering.eu/soundbox-go/commit/?id=79eeb90079e417f0a9d040c1de8f3278c628810f'/>
<id>urn:sha1:79eeb90079e417f0a9d040c1de8f3278c628810f</id>
<content type='text'>
The repository names for soundbox are named as below:

- app: soundbox-app
- Go library module: soundbox-go
- Device: soundbox

The Go module names were:

- app: xengineering.eu/soundbox/app
- Go library module: xengineering.eu/soundbox

This does not make clear which module is related to which repository
since the names are different. Thus it should be changed to:

- app: xengineering.eu/soundbox-app
- Go library module: xengineering.eu/soundbox-go

The import statement for the library is then:

    import "xengineering.eu/soundbox-go/soundbox"

This is a bit longer but it keeps the property that the library is
referenced inside the code by the simple name `soundbox`.
</content>
</entry>
</feed>
