Skip to content

Thursday, 19 September 2024

Implementing an Audio Mixer, Part 2

Recap

In Part 1, we covered PCM audio and superimposing waveforms, and developed an algorithm to combine an arbitrary number of audio streams into one.

Now we need to use these ideas to finish a full implementation using Qt Multimedia.

Using Qt Multimedia for Audio Device Access

So what do we need? Well, we want to use a single QAudioOutput, which we pass an audio device and a supported audio format.

We can get those like this:

const QAudioDeviceInfo &device = QAudioDeviceInfo::defaultOutputDevice();
const QAudioFormat &format = device.preferredFormat();

Let’s construct our QAudioOutput object using the device and format:

static QAudioOutput audioOutput(device, format);

Now, to use it to write data, we have to call start on the audio output, passing in a QIODevice *.

Normally we would use the QIODevice subclass QBuffer for a single audio buffer. But in this case, we want our own subclass of QIODevice, so we can combine multiple buffers into one IO device.

We’ll call our subclass MixerStream. This is where we will do our bufferCombine, and keep our member list of streams mStreams.

We will also need another stream type for mStreams. For now let’s just call it DecoderStream, forward declare it, and worry about its implementation later.

One thing that’s good to know at this point is DecoderStream objects will get the data buffers we need by decoding audio data from a file. Because of this, we’ll need to keep our audio format from above to as a data member mFormat. Then we can pass it to decoders when they need it.

Implementing MixerStream

Since we are subclassing QIODevice, we need to provide reimplementations for these two protected virtual functions:

virtual qint64 QIODevice::readData(char *data, qint64 maxSize);
virtual qint64 QIODevice::writeData(const char *data, qint64 maxSize);

We also want to provide a way to open new streams that we’ll add to mStreams, given a filename. We’ll call this function openStream. We can also allow looping a stream multiple times, so let’s add a parameter for that and give it a default value of 1.

Additionally, we’ll need a user-defined destructor to delete any pointers in the list that might remain if the MixerStream is abruptly destructed.

// mixerstream.h

#pragma once

#include <QAudioFormat>
#include <QAudioOutput>
#include <QIODevice>

class DecodeStream;

class MixerStream : public QIODevice
{
    Q_OBJECT

public:
    explicit MixerStream(const QAudioFormat &format);
    ~MixerStream();

    void openStream(const QString &fileName, int loops = 1);

protected:
    qint64 readData(char *data, qint64 maxSize) override;
    qint64 writeData(const char *data, qint64 maxSize) override;

private:
    QAudioFormat mFormat;
    QList<DecodeStream *> mStreams;
};

Notice that combineSamples isn’t in the header. It’s a pretty basic function that doesn’t require any members, so we can just implement it as a free function.

Let’s put it in a header mixer.h and wrap it in a namespace:

// mixer.h

#pragma once

#include <QtGlobal>

#include <limits>

namespace Mixer
{
inline qint16 combineSamples(qint32 samp1, qint32 samp2)
{
    const auto sum = samp1 + samp2;
    if (std::numeric_limits<qint16>::max() < sum)
        return std::numeric_limits<qint16>::max();

    if (std::numeric_limits<qint16>::min() > sum)
        return std::numeric_limits<qint16>::min();

    return sum;
}
} // namespace Mixer

There are some very basic things we can get out of the way quickly in the MixerStream cpp file. Recall that we must implement these member functions:

explicit MixerStream(const QAudioFormat &format);
~MixerStream();

void openStream(const QString &fileName, int loops = 1);

qint64 readData(char *data, qint64 maxSize) override;
qint64 writeData(const char *data, qint64 maxSize) override;

The constructor is very simple:

MixerStream::MixerStream(const QAudioFormat &format)
    : mFormat(format)
{
    setOpenMode(QIODevice::ReadOnly);
}

Here we use setOpenMode to automatically open our device in read-only mode, so we don’t have to call open() directly from outside the class.

Also, since it’s going to be read-only, our reimplementation of QIODevice::writeData will do nothing:

qint64 MixerStream::writeData([[maybe_unused]] const char *data,
                              [[maybe_unused]] qint64 maxSize)
{
    Q_ASSERT_X(false, "writeData", "not implemented");
    return 0;
}

The custom destructor we need is also quite simple:

MixerStream::~MixerStream()
{
    while (!mStreams.empty())
        delete mStreams.takeLast();
}

readData will be almost exactly the same as the implementation we did earlier, but returning qint64. The return value is meant to be the amount of data written, which in our case is just the maxSize argument given to it, as we write fixed-size buffers.

Additionally, we should call qAsConst (or std::as_const) on mStreams in the range-for to avoid detaching the Qt container. For more on qAsConst and range-based for loops, see Jesper Pederson’s blog post on the topic.

qint64 MixerStream::readData(char *data, qint64 maxSize)
{
    memset(data, 0, maxSize);

    constexpr qint16 bitDepth = sizeof(qint16);
    const qint16 numSamples = maxSize / bitDepth;

    for (auto *stream : qAsConst(mStreams))
    {
        auto *cursor = reinterpret_cast<qint16 *>(data);
        qint16 sample;

        for (int i = 0; i < numSamples; ++i, ++cursor)
            if (stream->read(reinterpret_cast<char *>(&sample), bitDepth))
                *cursor = Mixer::combineSamples(sample, *cursor);
    }

    return maxSize;
}

That only leaves us with openStream. This one will require us to discuss DecodeStream and its interface.

The function should construct a new DecodeStream on the heap, which will need a file name and format. DecodeStream, as implied by its name, needs to decode audio files to PCM data. We’ll use a QAudioDecoder within DecodeStream to accomplish this, and for that, we need to pass mFormat to the constructor. We also need to pass loops to the constructor, as each stream can have a different number of loops.

Now our constructor call will look like this:

DecodeStream(fileName, mFormat, loops);

We can then use operator<< to add it to mStreams.

Finally, we need to remove it from the list when it’s done. We’ll give it a Qt signal, finished, and connect it to a lambda expression that will remove the stream from the list and delete the pointer.

Our completed openStream function now looks like this:

void MixerStream::openStream(const QString &fileName, int loops)
{
    auto *decodeStream = new DecodeStream(fileName, mFormat, loops);
    mStreams << decodeStream;

    connect(decodeStream, &DecodeStream::finished, this, [this, decodeStream]() {
        mStreams.removeAll(decodeStream);
        decodeStream->deleteLater();
    });
}

Recall from earlier that we call read on a stream, which takes a char * to which the read data will be copied and a qint64 representing the size of the data.

This is a QIODevice function, which will internally call readData. Thus, DecoderStream also needs to be a QIODevice.

Getting PCM Data for DecodeStream

In DecodeStream, we need readData to spit out PCM data, so we need to decode our audio file to get its contents in PCM format. In Qt Multimedia, we use a QAudioDecoder for this. We pass it an audio format to decode to, and a source device, in this case a QFile file handle for our audio file.

When a QAudioDecoder's start method is called, it will begin decoding the source file in a non-blocking manner, emitting a signal bufferReady when a full buffer of decoded PCM data is available.

On that signal, we can call the decoder’s read method, which gives us a QAudioBuffer. To store in a data member in DecodeStream, we use a QByteArray, which we can interact with using QBuffers to get a QIODevice interface for reading and writing. This is the ideal way to work with buffers of bytes to read or write in Qt.

We’ll make two QBuffers: one for writing data to the QByteArray (we’ll call it mInputBuffer), and one for reading from the QByteArray (we’ll call it mOutputBuffer). The reason for using two buffers rather than one read/write buffer is so the read and write positions can be independent. Otherwise, we will encounter more stuttering.

So when we get the bufferReady signal, we’ll want to do something like this:

const QAudioBuffer buffer = mDecoder.read();
mInputBuf.write(buffer.data<char>(), buffer.byteCount());

We’ll also need to have some sort of state enum. The reason for this is that when we are finished with the stream and emit finished(), we remove and delete the stream from a connected lambda expression, but read might still be called before that has completed. Thus, we want to only read from the buffer when the state is Playing.

Let’s update mixer.h to put the enum in namespace Mixer:

#pragma once

#include <QtGlobal>

#include <limits>

namespace Mixer
{
enum State
{
    Playing,
    Stopped
};

inline qint16 combineSamples(qint32 samp1, qint32 samp2)
{
    const auto sum = samp1 + samp2;

    if (std::numeric_limits<qint16>::max() < sum)
        return std::numeric_limits<qint16>::max();

    if (std::numeric_limits<qint16>::min() > sum)
        return std::numeric_limits<qint16>::min();

    return sum;
}
} // namespace Mixer

Implementing DecodeStream

Now that we understand all the data members we need to use, let’s see what our header for DecodeStream looks like:

// decodestream.h

#pragma once

#include "mixer.h"

#include <QAudioDecoder>
#include <QBuffer>
#include <QFile>

class DecodeStream : public QIODevice
{
    Q_OBJECT

public:
    explicit DecodeStream(const QString &fileName, const QAudioFormat &format, int loops);

protected:
    qint64 readData(char *data, qint64 maxSize) override;
    qint64 writeData(const char *data, qint64 maxSize) override;

signals:
    void finished();

private:
    QFile mSourceFile;
    QByteArray mData;
    QBuffer mInputBuf;
    QBuffer mOutputBuf;
    QAudioDecoder mDecoder;
    QAudioFormat mFormat;
    Mixer::State mState;
    int mLoopsLeft;
};

In the constructor, we’ll initialize our private members, open the DecodeStream in read-only (like we did earlier), make sure we open the QFile and QBuffers successfully, and finally set up our QAudioDecoder.

DecodeStream::DecodeStream(const QString &fileName, const QAudioFormat &format, int loops)
    : mSourceFile(fileName)
    , mInputBuf(&mData)
    , mOutputBuf(&mData)
    , mFormat(format)
    , mState(Mixer::Playing)
    , mLoopsLeft(loops)
{
    setOpenMode(QIODevice::ReadOnly);

    const bool valid = mSourceFile.open(QIODevice::ReadOnly) &&
                       mOutputBuf.open(QIODevice::ReadOnly) &&
                       mInputBuf.open(QIODevice::WriteOnly);

    Q_ASSERT(valid);

    mDecoder.setAudioFormat(mFormat);
    mDecoder.setSourceDevice(&mSourceFile);
    mDecoder.start();

    connect(&mDecoder, &QAudioDecoder::bufferReady, this, [this]() {
        const QAudioBuffer buffer = mDecoder.read();
        mInputBuf.write(buffer.data<char>(), buffer.byteCount());
    });
}

Once again, our QIODevice subclass is read-only, so our writeData method looks like this:

qint64 DecodeStream::writeData([[maybe_unused]] const char *data,
                               [[maybe_unused]] qint64 maxSize)
{
    Q_ASSERT_X(false, "writeData", "not implemented");
    return 0;
}

Which leaves us with the last part of the implementation, DecodeStream's readData function.

We zero out the char * with memset to avoid any noise if there are areas that are not overwritten. Then we simply read from the QByteArray into the char * if mState is Mixer::Playing.

We check to see if we finished reading the file with QBuffer::atEnd(), and if we are, we decrement the loops remaining. If it’s zero now, that was the last (or only) loop, so we set mState to stopped, and emit finished(). Either way we seek back to position 0. Now if there are loops left, it starts reading from the beginning again.

qint64 DecodeStream::readData(char *data, qint64 maxSize)
{
    memset(data, 0, maxSize);

    if (mState == Mixer::Playing)
    {
        mOutputBuf.read(data, maxSize);
        if (mOutputBuf.size() && mOutputBuf.atEnd())
        {
            if (--mLoopsLeft == 0)
            {
                mState = Mixer::Stopped;
                emit finished();
            }

            mOutputBuf.seek(0);
        }
    }

    return maxSize;
}

Now that we’ve implemented DecodeStream, we can actually use MixerStream to play two audio files at the same time!

Using MixerStream

Here’s an example snippet that shows how MixerStream can be used to route two simultaneous audio streams into one system mixer channel:

const auto &device = QAudioDeviceInfo::defaultOutputDevice();
const auto &format = device.preferredFormat();

auto mixerStream = std::make_unique<MixerStream>(format);

auto *audioOutput = new QAudioOutput(device, format);
audioOutput->setVolume(0.5);
audioOutput->start(mixerStream.get());

mixerStream->openStream(QStringLiteral("/path/to/some/sound.wav"));
mixerStream->openStream(QStringLiteral("/path/to/something/else.mp3"), 3);

Final Remarks

The code in this series of posts is largely a reimplementation of Lova Widmark’s project QtMixer. Huge thanks to her for a great and lightweight implementation. Check the project out if you want to use something like this for a GPL-compliant project (and don’t mind that it uses qmake).

The post Implementing an Audio Mixer, Part 2 appeared first on KDAB.

Ruqola 2.3.0 is a feature and bugfix release of the Rocket.chat app.

New features:

  • Implement Rocket.Chat Marketplace.
  • Allow to clean room history.
  • Allow to check new version.
  • Implement moderation (administrator mode).
  • Add welcome page.
  • Implement pending users info (administrator mode).
  • Use cmark-rc (https://github.com/dfaure/cmark-rc) for markdown support.
  • Delete oldest files from some cache directories (file-upload and media) so it doesn't grow forever.

Fixed bugs:

  • Clean market application model after 30 minutes (reduce memory footprint).
  • Fix show discussion name in completion.
  • Fix duplicated messages in search message dialog.
  • Add delegate in search rooms in team dialog.

URL: https://download.kde.org/stable/ruqola/
Source: ruqola-2.3.0.tar.xz
SHA256: 051186793b7edc4fb2151c80ceab3bcfd65acb27d38305568fda54553660fdd0
Signed by: E0A3EB202F8E57528E13E72FD7574483BB57B18D Jonathan Riddell jr@jriddell.org
https://jriddell.org/jriddell.pgp

Wednesday, 18 September 2024

This year was my first Akademy, and I was thrilled to be able to attend in person. Even better, some veterans said it was the best Akademy so far. It was great to see some people I had met in April and to meet new people. I arrived on Friday, 6th Sept and left the following Friday. I very much enjoyed staying in the lovely town of Würzburg and doing a day tour of Rothenberg. Now that I've caught up on sleep (the jet lag, it is real), it's time to write about it.

As described in the Akademy 2024 report, the focus this year was resetting priorities, refocusing goals and reorganizing projects. Since I had recently become a more active contributor to KDE, I was keen to learn about the direction things will take over the next year. It was also exciting to see the new design direction in Union and Plasma Next!

A Personal Note

Speaking of goals, a personal one I've striven toward in my career is to contribute to something that improves the world, even if indirectly. It's not something I've always been able to do. It feels good to be able to work with a project that is open source, and is working to make the computing world more sustainable.

I'd also like to recognize all the wonderful, welcoming folks that make Akademy such a great conference. I've been to a few other tech conferences and events, with varying atmospheres and attitudes. I can say that people at Akademy are so helpful, and so nice - it made being among a lot of new faces in a foreign country a truly great experience.

The Conference

The keynote had powerful information about the impacts of improper tech disposal and what we can do to improve the situation. This highlighted the importance of the KDE Eco project, which aims to help to reduce e-waste and make our projects more sustainable. Their new Opt Green initiative is going to take concrete steps toward this.

Some of the talks I attended:

  • KDE Goals - one talk about the last 2 years of goals and another revealing the new goals.
  • "Adapt or Die" - how containerized packaging affects KDE projects.
  • Union and styling in KDE's future.
  • Banana OS KDE Linux - why it's being developed and some technical planning.
  • Getting Them Early: Teaching Pupils About The Environmental Benefits Of FOSS This strategy has been powerful for other projects (like Microsoft Windows, Google Chromebooks, Java), so I'm glad to see it for KDE.
    • Why and how to use KDE frameworks in non-KDE apps
    • KDE Apps Initiative
    • Cutting Gordian's "End-User Focus" vs. "Privacy" Knot - collecting useful user data while respecting privacy and security.
    • Plasma Next - Visual Design Evolution for Plasma
    • The Road to KDE Neon Core

BoF Sessions

Some of the BoF sessions I attended:

  • Decentralizing KUserFeedback
  • Streamlined Application Development Experience
  • Organizing the Plasma team, Plasma goals
  • Plasma "Next" initiative
  • Union: The future of styling in KDE
  • KWallet modern replacement
  • Video tutorial for BoF best practice (Kieryn roped me into this one)
  • Security Team BoF Notes

Thanks to everyone who made this year's Akademy such a wonderful experience. If anyone out there is thinking of attending next year, and can make it, I really recommend it. I'm hoping to be at Akademy 2025!

Contrary to popular belief, Akademy 2024 was not my first Akademy. KDE seems to keep tagged faces from Akademy group photos around, so I stumbled over some vintage pictures of me in 2006 (Glasgow) and 2007 (Dublin). At the time, I was an utter greenhorn with big dreams, vague plans, and a fair bit of social anxiety.

Dublin is where Aaron Seigo taught me how to eat with chopsticks. I continue to draw from that knowledge to this day.

And then I disappeared for 15 years, but now it's time for a new shot. This time around, I'm a little less green (rather starting to grey a little) and had a surprising amount of stuff to discuss with various KDE collaborators. Boy, is there no end of interesting people and discussion topics to be had at Akademy.

"Oh, you're the PowerDevil guy"

You're not wrong, I've been contributing to that for the past year. As such, one highlight for me was to meet KDE's hardware integration contractor Natalie Clarius in person and sync up on all things power-related.

Akademy's no-photo badge makes its wearer disappear from selfies. AI magic, perhaps?

Natalie presented a short talk and hosted a BoF session ("Birds of a Feather", a.k.a. workshop) about power management topics. We had a good crowd of developers in attendance, clearing up the direction of several outstanding items.

Power management in Plasma desktops is in decent shape overall. One of the bigger remaining topics is (re)storing battery charge limits across reboots, for laptops whose firmware doesn't remember those settings. There is a way forward that involves making use of the cross-desktop UPower service and its new charge limit extensions. This will give us the restoring feature for free, but we have to add some extra functionality to make sure that charge threshold customization remains possible for Plasma users after switching over.

We also looked at ways to put systems back to sleep that weren't supposed to wake up yet. Unintended wake-ups can happen e.g. when the laptop in your backpack catches a key press from the screen when it's squeezed against the keyboard. Or when one of those (conceptually neat) "Modern Standby" implementations on recent laptops are buggy. This will need a little more investigation, but we've got some ideas.

I talked to Bhushan Shah about power saving optimizations in Plasma Mobile. He is investigating a Linux kernel feature designed for mobile devices that saves power more aggressively, but needs support from KDE's power management infrastructure to make sure the phone will indeed wake up when it's meant to. If this can be integrated with KDE's power management service, we could improve battery runtimes for mobile devices and perhaps even for some laptops.

The friendly people from Slimbook dropped by to show off their range of Linux laptops, and unveiled their new Slimbook VI with KDE neon right there at the conference. Compared to some of their older laptops, build quality is improved leaps and bounds. Natalie and I grilled their BIOS engineer on topics such as power profiles, power consumption, and how to get each of their function keys show the corresponding OSD popup.

KDE Slimbook VI shortly after the big reveal

"I'm excited that your input goal was chosen"

Every two years, the KDE community picks three "Goals" to rally around until the next vote happens. This time, contributors were asked to form teams of "goal champions" so that the work of educating and rallying people does not fall on the shoulders of a single poor soul per goal.

So now we have eight poor souls who pledge to advance a total of three focus areas over the next two years. Supported by KDE e.V.'s new Goals Coordinator, Farid. There's a common thread around attracting developers, with Nicolas Fella and Nate Graham pushing for a "Streamlined Application Development Experience" and the KDE Promo team with a systematic recruitment initiative titled "KDE Needs You". And then there's this other thing, with a strict end user focus, briefly introduced on stage by guess who?

Yup. Hi! I'm the input guy now.

Turns out a lot of people in KDE are passionate about support for input devices, virtual keyboards and input methods. Gernot Schiller (a.k.a. duha) realized this and assembled a team consisting of himself, Joshua Goins (a.k.a. redstrate) as well as Yours Truly to apply as champions. The goal proposed that "We care about your Input" and the community's response is Yes, Yes We Do.

As soon as the new goals were announced, Akademy 2024 turned into an Input Goal Akademy for me. In addition to presenting the new goal on stage briefly, we also gathered in a BoF session to discuss the current state, future plans and enthusiastic volunteering assignments related to all things input. I also sat down with a number of input experts to learn more about everything. There is still much more I need to learn.

It's a sprawling topic with numerous tasks that we want to get done, ranging from multi-month projects to fixing lots of low-hanging fruit. This calls for a series of dedicated blog posts, so I'll go into more detail later.

Join us at #kde-input:kde.org on Matrix or watch this space (and Planet KDE in general) for further posts on what's going on with input handling in KDE.

Look at the brightness side

KWin hacker extraordinaire Xaver Hugl (a.k.a. zamundaaa) demoed some of his color magic on a standard SDR laptop display. Future KWin can play bright HDR videos in front of regular SDR desktop content. Accurate color transformations for both parts without special HDR hardware, that's pretty impressive. I thought that HDR needs dedicated hardware support, turns out I'm wrong, although better contrast and more brightness can still improve the experience.

I also got to talk to Xaver about touchpad gestures, learned about stalled attempts to support DDC/CI in the Linux kernel directly, and pestered him for a review to improve Plasma's D-Bus API for the new per-monitor brightness features. Also the XDC conference in Montreal, is happening in less than a month, featuring more of Xaver as well as loads of low-level graphics topics. Perhaps even input-related stuff. It's only a train ride from Toronto, maybe I'll drop by. Maybe not. Here's a medieval German town selfie.

Towering over the rooftops of Rothenburg ob der Tauber with Xaver, Jonathan Riddell, and two suspect KWin developers in the back

Thanks to the entire KWin gang for letting me crash their late-night hacking session and only throwing the last of us out at 2 AM after my D-Bus change got merged. Just in time for the Plasma 6.2 beta. I was dead tired on Thursday, totally worth it though.

Atomic KDE for users & developers

Plasma undoubtedly has some challenges ahead in order to bring all of its power and flexibility to an image-based, atomic OS with sandboxed apps (i.e. Flatpak/Snap). David Edmundson's talk emphasized that traditional plugins are not compatible with this new reality. We'll need to look into other ways of extending apps.

David Edmundson wildly speculating about the future

The good news is that lots of work is indeed happening to prepare KDE for this future. Baloo making use of thumbnailer binaries in addition to thumbnailer plugins. KRunner allowing D-Bus plugins in addition to shared libraries. Arjen Hiemstra's work-in-progress Union style being customizable through configuration rather than code. Heck, we even learned about a project called KDE Neon Core trying to make a Snap out of each and every piece of KDE software.

Going forward, it seems that there will be a more distinct line between Plasma as a desktop platform and KDE apps, communicating with each other through standardized extension points.

All of this infrastructure will come in handy if Harald Sitter's experimental atomic OS, KDE Linux (working title), is to become a success. Personally, I've long been hoping for a KDE-based system that I can recommend to my less technical family members. KDE Linux could eventually be that. Yes, Fedora Kinoite is also great.

KDE Linux: Useful to users, hardware partners, and... developers?

What took me by surprise about Harald's presentation was that it could be great even as a development platform for contributing to the Plasma desktop itself.

As a desktop developer, I simply can't run my Plasma development build in a container. Many functions interact with actual hardware so it needs to run right on the metal. On my current Arch system, I use a secondary user account with Plasma installed into that user's home directory. That way the system packages aren't getting modified - one does not want to mess with system packages.

But KDE Linux images contain the same system-wide build that I would make for myself. I can build an exact replacement with standard KDE tooling, perhaps a slight bit newer, and temporarily use it as system-wide replacement using systemd-sysext. I can revert whenever. KDE Linux includes all the development header files too, making it possible to build and replace just a single system component without building all the rest of KDE.

Different editions make it suitable for users anywhere between tested/stable (for family members) and bleeding edge (for myself as Plasma developer). Heck, perhaps we'll even be able to switch back and forth between different editions with little effort.

Needless to say, I'm really excited about the potential of KDE Linux. Even without considering how much work it can save for distro maintainers that won't have to combine outdated Ubuntu LTS packages with the latest KDE desktop.

Conclusion

There's so much else I've barely even touched on, like NLnet funding opportunities, quizzing Neal Gompa about KDE for Enterprise, Rust and Python binding efforts, Nicolas Fella being literally everywhere, Qt Contributor Summit, finding myself in a hostel room together with fellow KDE devs Carl & Kåre. But this blog post is already long enough. Read some of the other KDE blogs for more Akademy reports.

German bus stops have the nicest sunsets. Also rainbows!

Getting home took all day and jet lag isn't fun, but I've reasonably recovered to give another shot at bringing KDE software to the masses. You can too! Get involved, donate to KDE, or simply enjoy the ride and discuss this post on KDE Discuss.

Or don't. It's all good :)

Tuesday, 17 September 2024

Introduction

License information in source code is best stored in each file of the source code as a comment, if at all possible. That way the license metadata travels with the file even if it was copied from its original package/repository into a different one.

Client-side JavaScript, CSS and similar languages that make up a large chunk of the web are often concatenated, minified and even uglified in an attempt to make the website faster to load. In this process, most often, the comments get culled the first to reduce the number of characters that serve no function to the program code itself.

Problem

The problem therefore is that typically when JavaScript, CSS (or similar client-side1 code) is being built, it tends to lose not just comments that describe the code’s functionality, but also comments that carry licensing and copyright information. And since licenses (FOSS or not) typically require the text of the license and copyright notices2 to be kept with the code, such removal can be problematic.

Proposal

The goal is to preserve copyright and licensing information of web front-end code even after minification in such a way that it makes FOSS license compliance3 of web applications easier.

In addition, my proposal is intended to keep things:

  • as simple as possible;
  • as short as possible;
  • not introduce any new specifications, but rely on well-established standards; and
  • not require any additional tooling, but rely on what is already in common use.

Essentially, my suggestion is literally as simple as wrapping every .js, .css and similar (e.g. .ts, .scss, …) file with SPDX snippets tags, following the REUSE specification as follows:

At the very beginning of the file introduce an “important” comment block that starts the (SPDX) snippet and includes all the REUSE/SPDX tags that apply to this file, e.g.:

/*!
 * SPDX-SnippetBegin
 * SPDX-CopyrightText: © 2024 Hacke R. McRandom <hacker@mcrandom.example>
 * SPDX-LicenseIdentifier: MIT
 */

And at the very end of the file introduce another “important” comment block that simply closes the (SPDX) snippet:

/*! SPDX-SnippetEnd */

… and that is basically it!

How any why this works (in theory)

This results in e.g. a .js file that would look something like this:

/*!
 * SPDX-SnippetBegin
 * SPDX-CopyrightText: © 2024 Hacke R. McRandom <hacker@mcrandom.example>
 * SPDX-LicenseIdentifier: MIT OR Unlicense
 */

import half_of_npm

code_goes_here();

/*! SPDX-SnippetEnd */

and a .css file as follows:

/*!
 * SPDX-SnippetBegin
 * SPDX-CopyrightText: © 2020 Talha Mansoor <talha131@gmail.com>
 * SPDX-LicenseIdentifier: MIT
 */

}
pre {
  overflow: auto;
  white-space: pre;
  word-break: normal;
  word-wrap: normal;
  color: #ebdbb2; /* This is needed due to bug in Pygments. It does not wraps some part of the code of some lagauges, like reST. This is for fallback. */
}

/*! SPDX-SnippetEnd */

All JavaScript, CSS, TypeScript, Sass, etc. files would look like that.

Then on npm run build (or whatever build system you use) the minifier keeps those tags where they are, because ! is a common trigger to keep that comment when minifying.

So if all the files are tagged as such4, the minified barf5 you get, should include all the SPDX tags in order and in the right place, so you see which license/copyright starts and ends to apply in the gibberish.

And if it pulls stuff that does not use REUSE (snippets) yet, you will still be able to tell it apart6, since it will be the barf that’s between SPDX-SnippetEnd of the previous and SPDX-SnippetBegin of the next properly marked barf.

Is this really enough?

OK, so now we know the start and end of a source code file that ended up in the minified barf. But are the SPDX-SnippetCopyrightText and SPDX-License-Identifier enough?

I think so, yes.

If I chose to express my copyright notice using an SPDX tag – especially if I followed the format that pretty much all copyright laws prescribe – that should be no problem7.

The bigger question is whether communicating the license solely through the SPDX IDs8 is enough, since you would technically not be including the whole license text(s). Honestly, I think it is fine.9 At this stage SPDX is not just long-established in the industry and community, but is also a formal international standard (ISO/IEC 5692:2021). Practically from its beginning – and probably the most known part of the spec – unique names/IDs of licenses and the equivalent canonical texts of those license have been part of SPDX. Which means that if I see SPDX-License-Identifier: MIT I know it specifically means the text that is on https://spdx.org/licenses/MIT.html. Ergo, as long as you are using licenses from the SPDX License List in these tags, all the relevant info is present.

How to keep the tags – Overview of popular minifiers

As mentioned, most minifiers tend to remove comments by default to conserve space. But there is a way to retain license comments (or other important comments). And this method existed for over a decade now!

I have done some research into how different minifiers deal with this. Admittedly, mostly by reading through their documentation. Due to my lack of skills, I did not manage to test out all of them in practice.

But at least theoretically the vast majority of the minifiers that I was told are common (plus a few more I found) seem to support at least one way of keeping important – or even explicitly copyright/license-relevant – comments.

minifierJSDoc-style / @licenseYUI-style / /*!
Google Closure Compiler✔️✔️ 11
Esbuild✔️✔️
JShrink✔️✔️
SWC10✔️✔️
Terser✔️✔️
UglifyJS✔️✔️
YUI Compressor✔️
Bun❌ (🛠️?)

From what I can tell it is only Bun that does not support any way to (selectively) preserve comments. There is a ticket open to implement (at least) the ! method though.

While not themselves minifiers, module bundlers do call and handle minfiers. Here are notes about the ones that I learnt are the most popular ones:

From this overview it seems like using the /*! comment method is our best option – it is short, the most widely supported and not loaded with meaning.

More details on both styles below.

Using @license / JSDoc-style

JSDoc is a markup language used to annotate JavaScript source code files to add in-code documentation, which is then used to generate documentation.

Looking at the JSDoc specification, the following keywords seem relevant:

So from JSDoc it seems the best choice would be @license. To quote from the spec itself:

The @license tag identifies the software license that applies to any portion of your code.

You can use any text to identify the license you are using. If your code uses a standard open-source license, consider using the appropriate identifier from the Software Package Data Exchange (SPDX) License List.

Some JavaScript processing tools, such as Google's Closure Compiler, will automatically preserve any JSDoc comment that includes a @license tag. If you are using one of these tools, you may wish to add a standalone JSDoc comment that includes the @license tag, along with the entire text of the license, so that the license text will be included in generated JavaScript files.

Using /*! / YUI-style

This other style seems to originate from Yahoo! UI Compressor 2.4.8 (also from 2013), to quote its README:

C-style comments starting with /*! are preserved. This is useful with comments containing copyright/license information. As of 2.4.8, the '!' is no longer dropped by YUICompressor. For example:

/*!
* TERMS OF USE - EASING EQUATIONS
* Open source under the BSD License.
* Copyright 2001 Robert Penner All rights reserved.
*/

remains in the output, untouched by YUICompressor.

Many other projects adopted this, some extended it by using also the single-line //!, but others have not.

Also note that YUI itself does not use the double-asterisk /** tag (if it did, it should be /**!), whereas that is typically the starting tag in JSDoc (and JavaDoc) of a document-relevant comment block.

So from the YUI-style tags, it seem using (multi-line) C-style comments that start with /*! is the most consistently used.

And as YUI-style seems to be the most commonly implemented way to tag and preserve (licensing-relevant) comments in JS, it would seem prudent to adopt it for our purposes – to preserve REUSE-standardised tags to mark license and copyright information in files and snippets.

A few PoC I tried

So far the theory …

But when it comes to testing it in practice, it gets both a bit messy and I very quickly reach the limits of my JS skills.

I have tried a few PoC, and ran into mixed, yet promising, results so far.

The most issues, I assume, are fixable by simply changing the settings of the build tools accordingly.

It is entirely possible that a lot of the issues are PEBKAC as well.

Svelte + Rollup + Terser

The simplest PoC I tried is a Svelte app that uses Rollup as a build tool and Terser as the minifier – kindly offered by Oliver “oliwerix” Wagner as a guinea pig. I left the settings as they are, and the results are mostly fine.

First we pull the 29c9881 commit and build it with npm install; npm run build.

In public/build/ we have three files: bundle.css, bundle.js, bundle.js.map.

The bundle.css file does not have any SPDX-* tags, and I suspect this is because it consist solely of 3rd party components, which do not use these tags yet. The public/global.css is still referred to separately in public/index.html and retains the SPDX-* tags in its non-minified form. So that is fine, but would need further testing to check the minified CSS.

The bundle.js file contains the minified JS and the SPDX-* tags remain there, but with one SPDX-SnippetEnd being misplaced.

If we compare e.g. rg SPDX- src/*.js12:

src/store.js
2: * SPDX-SnippetBegin
3: * SPDX-SnippetCopyrightText: 1984 Winston Smith <win@smith.example>
4: * SPDX-License-Identifier: Unlicense
18:/*! SPDX-SnippetEnd */

src/main.js
2: * SPDX-SnippetBegin
3: * SPDX-SnippetCopyrightText: © 2021 Test Dummy <dummy@test.example>
4: * SPDX-License-Identifier: BSD-2-Clause
17:/*! SPDX-SnippetEnd */

… and rg SPDX- public/build/bundle.js

3:     * SPDX-SnippetBegin
4:     * SPDX-SnippetCopyrightText: 1984 Winston Smith <win@smith.example>
5:     * SPDX-License-Identifier: Unlicense
8:/*! SPDX-SnippetEnd */
10:/*! SPDX-SnippetEnd */
13:     * SPDX-SnippetBegin
14:     * SPDX-SnippetCopyrightText: © 2021 Test Dummy <dummy@test.example>
15:     * SPDX-License-Identifier: BSD-2-Clause

… it is clear that something is amiss. A snippet cannot end before it begins.

But when checking the public/build/bundle.js.map SourceMap, we again see the SPDX-* tags in order just fine.

I would really like to know what went wrong here.

React Scripts (+ WebPack + Terser)

Before that I tried to set up a “simple” React Scripts app with the help of my work colleague, Carlos “roclas” Hernandez.

Here, again, I am getting mixed results out of the box.

First we pull the af54954 commit and build it with npm install; npm run build.

On the CSS side, we see that there are two files in build/static/css/, namely: main.05c219f8.css and main.05c219f8.css.map.

Both the main.05c219f8.css and its SourceMap retain the SPDX-* tags where we want them, so that is great!

On the JS side it gets more complicated though. In build/static/js/ we have several files now, and if we pair them up:

  • 453.2a77899f.chunk.js and 453.2a77899f.chunk.js.map
  • main.608edf8e.js, main.608edf8e.js.LICENSE.txt and main.608edf8e.js.map

The 453.2a77899f.chunk.js* files contain no SPDX-* tags. Honestly, I do not know where they came from, but assume it is again 3rd party components, which do not use these tags yet. So we can ignore them.

But it is the main.608edf8e.js* files that we are interested in.

Unfortunately, it is here that it gets a bit annoying.

It seems React Scripts is quite opinionated and hard-codes its preferences when it comes to minification etc. So even though it is easy to set-up WebPack and Terser to preserve (important) comments in the code itself, React forces it otherwise.

What this results in then is the following:

  • main.608edf8e.js is cleaned of all comments – no SPDX-* tags here;
  • but now main.608edf8e.js.LICENSE.txt has all the SPDX-* tags as well other important comments (e.g. @license blocks from 3rd party components);
  • and as for the main.608edf8e.js.map SourceMap, it includes SPDX-* tags, as expected.

The really annoying bit is that it seems like main.608edf8e.js.LICENSE.txt is not mapped, it is just a dump of all the license-related comments. So that does not help us here.

There is a workaround by injecting code and settings using Rewire, but so far I have not managed to set it up correctly. I am sure it is possible, but I gave up after it took way too much of my time already.

Some early thoughts on *.js.map and *.js.LICENSE.txt

If the license and copyright info is missing from the minified source code, but it is there in the *.js.map SourceMap (spec), I think that is better than nothing, but I am leaning towards it not being enough for the goal we are trying to reach here.

Similarly, when the minifier simply shoves all the license comments into a separate *.js.LICENSE.txt file, removing them from the *.js file and without any way to map the license and copyright comments back to the source code, I do not see how this is much more useful than the *.js.map itself.

So far, it seems to me like this is a problem caused by some frameworks (e.g. React Scripts) hard-coding their preferences when it comes to minification, without an easy way to override it.

But if there was a *.js.LICENSE.txt (or equivalent) that was mapped13 via SourceMaps, so one could figure out which license comment matches which source code block in the minified code, I would be inclined to take that as potentially good enough.

Future ideas

Once the base issue of preserving SPDX tags in minified (web front-end) code in proper places is solved, we can expand it to make it even more useful.

Here is a short list of ideas that popped up already. I am keeping them hidden by default, to not detract too much from the base problem.

Nothing stops us from adding more relevant information in these tags – in fact, as long as it is an SPDX tag, that would be in line with both the SPDX standard and the REUSE spec. A prefect candidate to include would be something to designate the origin or provenance of the package the file came from – e.g. using PackageURL.

To make this even more useful in practice, it is entirely imaginable that build tools could help generate or populate these tags and therefore inject information themselves, some early ideas:

  • for (external) packages that do not use REUSE/SPDX Snippet Tags, the build tool could be ordered to generate them from REUSE/SPDX File Tags;
  • same, but to pull the information from a different source (e.g. LICENSE file) – that might be a bit dubious when it comes to exactness though;
  • the above-mentioned PackageURL (or other origin identifier) could be added by the build tool.

All of the above future ideas are super early ideas and some could well be too error-prone to be useful, but should be kept in mind and discussed after the base issue is solved.

Open questions & Call for help and testers

As stated, I am not very savvy when it comes to writing web front-ends, so at this point this project would really benefit from people with experience in building web front-ends taking a look.

If anyone knows how to get React and any other similarly opinionated frameworks to not create the *.js.LICENSE.txt file, but keep the important comments in code, that would be really useful.

If you are a legal or license compliance expert, while I am quite confident in the logic behind this, feedback is very welcome and do try to poke holes in my logic.

If you are a technical person (esp. front-end developers), please, try applying this to code in different build environments and let me know what works and what breaks. We need more and better PoCs.

If you have proposed fixes, even better!

Comments, ideas, suggestions, … welcome.

Ultimately, my goal is to come up with a solution that works and requires (ideally) no changes in tooling and specifications.

If that means abandoning this proposal and finding a better one, so be it. But it has to start somewhere that is half-way doable.

Credits

While I did spend quite a bit of time on this, this would not exist without prior work and a lot of help from others.

First of all, the basic idea of treating each file as a snippet that just happens to currently span a whole file, was originally proposed to me by José Maria “chema” Balsas Falaguera in 2020 and we worked together on an early PoC on how to apply REUSE to a large-ish JS + CSS code-base … before (REUSE Snippets and later) SPDX Snippets came to be.

In fact, it was this Chema’s idea that sparked my quest to first bring snippet support to REUSE, and later to SPDX specifications.

At REUSE I would specifically like to thank Carmen Bianca Bakker, Max Mehl, and Nico Rikken for not refusing this idea upfront and also for being great intellectual sparring partners on this adventure.

And at SPDX it was Alexios “zvr” Zavras who saw the potential and helped me draft SPDX tags for snippets to the point where it got accepted.

I would also like to thank Henrik “hesa” Sandklef, Maximilian Huber and Philippe Ombredanne for their feedback and some proposals on how to expand this further later on.

Of course, none of this would be possible without everyone behind SPDX, REUSE, YUI, and JSDoc.

I am sure I forgot to mention a few other people, and if that was you, I humbly apologise and ask you to let me know, so I can correct this issue.

hook out → wow, this took longer than expected … so many rabbit holes!


  1. Also called browser-side code. 

  2. In droit d’auteur / civil law jurisdictions, the removal of a copyright statement could also qualify as a violation of civil law or even a criminal offence … when the copyright holder is an author (physical person), and not a legal entity

  3. Both from the point-of-view of a license compliance expert and their tooling; and from (at least) the spirit of FOSS licenses. 

  4. Barring any magical weirdness that happens during the minifying/uglifying that might mess things up in things I did not predict. Here I need front-end experts’ help and more testing. 

  5. If unreadable binary is a blob, “barf” sounded like an appropriate equivalent for nigh-unreadable minified code. 

  6. Here is where the added functionality of treating every file as a (very-long) snippet pays off. If we did not mark the start and end of each file, we would not be able to tell that in the minified code. In that case, a consequence would be that we would falsely assume a piece of minified code would fall under a certain license, whereas it would simply be part of a file that was unmarked (e.g. pulled in from a 3rd party component). 

  7. Probably Apache-2.0’s requirement to include the NOTICE file would not be satisfied, but that is not a new issue. And there are three ways to make Apache-2.0 happy, so it is solvable if you are diligent. 

  8. The value of an SPDX-License-Identifier tag is an SPDX License Expression, which can either be just a single license or a logical statement of several licenses. (Hat-tip to Peter Monks for pointing this out.) 

  9. I do accept that there is a tiny bit of handwaving involved here, but honestly, I think this should be good enough, given the limitations of minified code. If the consensus ends up that full license texts need to be shipped as well, this can be done via hyperlinks, and those can be generated from the SPDX IDs. Or if you really want to be pedantic about it, upload and hyperlink the (usually missing) license texts from each JS/CSS package itself. 

  10. SWC is primarily a compiler and module bundler, but I keep it here because it does its own minification instead of relying on an external tool. 

  11. It is not documented but Google Closure Compiler does indeed support ! and treats it explicitly as a license block (without any additional JSDoc parsing) since 2016

  12. I use RipGrep in this example, but normal grep should work too. 

  13. And I do not know of any that does that. 

From Fri, Sep 6th to Tue, Sep 10th I attended the 2024 edition of KDE Akademy in Würzburg, Germany. I booked a room in a hotel downtown the same place CoLa, a fellow KDE developer, stayed. Since parking is rather expensive in downtown areas, I left the car in front of the university building where the event was about to start on Saturday morning and took the bus into the city to the hotel. We all used the bus in coming days and one would always meet some KDE folks easy to spot wearing their lanyards.

On Friday night the KDE crowd gathered at a pub in the city and it was great to see old friends and also meet new people. At some point, I was talking to Carlos. It turned out that he already made some contributions to KMyMoney. The git log says it was in 2022. While more and more fellow KDE developers joined the place it became louder and louder and conversations were not easy anymore. Too bad that some of us got stranded at different places on their way out to Würzburg and did not make it until Saturday.

Conference

On Saturday, the conference program started with a keynote by Joanna Murzyn who took us on a journey from crucial mineral mining hubs to electronic waste dumpsters, uncovering the intricate connections between code, hardware, open source principles as well as social and environmental justice. We discovered how the KDE community’s work is shaping a more resilient, regenerative future, and explore ways to extend those principles to create positive impact beyond tech world.

On the first day, I took the opportunity to see the following talks

  • Current Developments in KDE Hardware Integration
  • KDE to Make Wines — Using KDE Software on Enterprise Desktops a Return on Experience
  • KWin Effects: The Next Generation
  • Adapt or Die: How new Linux packaging approaches affect wider KDE
  • An Operating System of Our Own
  • What’s a Maintainer anyway?

The last one for the day complemented the keynote in a nice way. In KDE newcomer Nicole Teale’s talk entitled “Getting Them Early: Teaching Pupils About The Environmental Benefits Of FOSS” she presented the work she is doing introducing KDE/FOSS to pupils, with a focus on its environmental benefits. She shared ideas on how to get schools involved in teaching pupils about reusing old hardware with FOSS. and presented some of the projects that have already been implemented in schools in Germany. This project is funded by the Umweltbundesamt (UBA) called “Sustainable Software For Sustainable Hardware”. The goal of this project is to reduce e-waste by promoting the adoption of KDE / Free & Open Source Software (FOSS) and raising awareness about the critical role software plays in the long-term, efficient use of hardware.

This becomes important in 2025 when Windows 10 runs out of support and Windows 11 requires new hardware, even though the existing one is still perfectly suited for the requirements of the majority of people. Linux and KDE to the rescue.

Saturday ended with Pizza and beer at the university as the booked beer garden canceled the reservation due to approaching thunderstorms.

On Sunday, I saw the following talks

  • Openwashing – How do we handle (and enforce?) OSS policies in products?
  • Opt In? Opt Out? Opt Green! KDE Eco’s New Sustainability Initiative
  • KDE’s CI and CD infrastructure
  • The Road to KDE Neon Core — Gosh! We’re surrounded by Snaps everywhere!

and of course the KDE Akademy award ceremony. In between those talks I had a chance to meet Julius Künzel and take a look at the problems we have in the KMyMoney project with the MacOS CD builds. He spotted a few things but I did not have the time to take care of them yet.

As a tradition, on Sunday is also the gathering to take the group picture. Here’s this years edition:

CC-BY-SA 4.0 by Andy Betts

Birds of a feather sessions

On Monday and Tuesday I went to various BoF’s and took the opportunity to join the git/Gitlab presentation by Natalie Clarius. I learned a few subtleties of Gitlab that I didn’t know before, so it was worth it. In the meantime I talked with a lot of people and did a small bit of hacking (one bug fixed). The BoFs I joined:

Good-bye Akademy 2024 / Thank you volunteers

Tuesday afternoon was the time to wave good-bye to the fellow KDE people and drive back home which I reached without delay (no traffic on the road) after an hour and a half. Hopefully, I will be able to join next time. Next stop will be the auditing of KDE accounting coming up in Berlin in a few weeks.

A big thank you goes out to the numerous volunteers who made this event happen. The team around seaLne just did a marvelous job.

Monday, 16 September 2024

Back from the fun of Akademy in Würzburg we can now get to the important task of testing Plasma 6.2 beta. It’s now in KDE neon testing edition which we build from the Git branches which will be used to make the 6.2 final release in 2.5 weeks time. Grab it now or if you don’t have a machine to install it on you can try the Docker images using the simple command `neondocker -p -e testing`.

Sunday, 15 September 2024

Kdenlive 24.08.1 is out and we urge all to upgrade. This version fixes recent playback and render regressions while fixing a wide range of bugs.

Saturday, 14 September 2024

This year’s Akademy was a special one for me in many ways.

First of all, instead of me travelling to Akademy it took place in my hometown of Würzburg, Germany. While I did have a hand in organizing it, most of the credit for it goes to Tobias and David. I had a lot of fun introducing people to my area and the concept of drinking wine on a bridge.

Qt Contributor Summit

Right before Akademy there was the Qt Contributor Summit, also in Würzburg (what a coincidence!). It was great to meet old and new Qt faces and talk about topics that are relevant to KDE, like the upcoming migration of KDE API documentation to qdoc.

Akademy Talks

This Akademy I gave two talks: One long one where I looked back at the Qt6/KF6 transition, what went well, what didn’t, and looked towards the future of what’s next for our software platform. Then I also had a lightning talk where I talked about the role of maintainers in open-source projects, why KDE doesn’t have traditional maintainers, and why that’s a good thing.

Besides that there were also a lot of interesting talks from other people, too many to mention right now. Speaking as a member of the program committee we had some tough decisions to make about what to include.

Goals

During the conference we announced the new set of Goals that were recently elected. I’m excited that my own proposal “Streamlined Application Development Experience” got selected and I’m looking forward to working on it with you. Besides that I also want to see how I can help out with the other elected goals: “We care about your input” and “KDE needs you 🫵”.

Akademy Awards

Another way this Akademy was special for me is that I was awarded with an Akademy award for my work on KDE Frameworks and Plasma. It feels great to get recognition for all the work I’ve been doing for the last seven years.

A framed award titling: Non-Application Contribution Award September 2024 Nicolas Fella for their work on KDE Frameworks and Plasma

BOFs

During the week we had lots of smaller meetings and workshops (a.k.a BOFs, world’s most terrible acronym). I was leading two of them, one about my newly-elected goal where I was presenting my proposal in more detail, and one about the ongoing work of mine to migrate our API documentation to qdoc. Thanks to our sysadmin Ben we now have a website where the current (still very much WIP) state of the new API documentation page can be seen.

Other Things

What’s great about Akademy isn’t just talks and BOFs, it’s meeting people you only see online all year, talking to them in person, getting your code reviewed while staring on a screen together, chatting over random visions, complaining about things, laughing and enjoying things together, and wrapping up the day with a nice beer in your hand.

I’m already looking forward to next year’s Akademy, wherever that will be. Maybe it will be your place, organizing it is a lot less scary than you’d think ;).

Alright… this is published a bit later than usual due to travels and lack of energy. Anyway, let’s go for my web review for the week 2024-37.


Fediverse Discovery Providers

Tags: tech, fediverse, search

Nice to see such a project be funded. Let’s see how far this will go.

https://www.fediscovery.org/


2024: 0.5% of the Global Top 200 Websites Use Valid HTML

Tags: tech, html, quality

This is clearly not a great outcome. The browser monoculture probably doesn’t help.

https://meiert.com/en/blog/html-conformance-2024/


Family poisoned after using AI-generated mushroom identification book

Tags: tech, ai, machine-learning, gpt, law

This is bad. There was no way to know the book was AI generated and clearly it contained errors and lies.

https://www.reddit.com/r/LegalAdviceUK/comments/1etko9h/family_poisoned_after_using_aigenerated_mushroom/


Baiting the bot

Tags: tech, gpt, security

Looks like an interesting venue to attack systems which use LLMs.

https://conspirator0.substack.com/p/baiting-the-bot


Building a browser using Servo as a web engine!

Tags: tech, web, browser, servo

It’s good to see servo getting closer to being usable in a browser. Makes me dream of Falkon or Konqueror being resurrected with Servo as the engine.

https://servo.org/blog/2024/09/11/building-browser/


Windows NT vs. Unix: A design comparison

Tags: tech, windows, unix, design, system, architecture

Interesting exploration of the NT design compared to Unix. There was less legacy to carry around which explains some of the choices which could be made. In practice similarities abound.

https://blogsystem5.substack.com/p/windows-nt-vs-unix-design


The Insecurity of Debian

Tags: tech, debian, redhat, security

Interesting comparison of the difference in approaches between RedHat and Debian about default system hardening.

https://unix.foo/posts/insecurity-of-debian/


Linux’s Bedtime Routine

Tags: tech, linux, kernel, power

Ever wondered what happens when you suspend or hibernate on Linux? Here is a very deep exploration of the process from the kernel perspective.

https://tookmund.com/2024/09/hibernation-preparation


Operating system threads are always going to be (more) expensive

Tags: tech, multithreading, system, kernel

Good reminder of what OS threads entails and why they can’t be optimized much further. There’s so much you can do properly in userland.

https://utcc.utoronto.ca/~cks/space/blog/tech/OSThreadsAlwaysExpensive


QUIC is not Quick Enough over Fast Internet

Tags: tech, networking, performance, quic

Looks like there is still some work required on QUIC. There is a path forward though.

https://dl.acm.org/doi/10.11453589334.3645323


JSON diff and patch

Tags: tech, json, tools

Looks like a very nice tool to deal with JSON files.

https://github.com/josephburnett/jd


proctrace - a high level profiler for process lifecycle events · Tinkering

Tags: tech, linux, profiling, tools, processes

Looks like an interesting little profiling tool. The article explains quite well how it’s been done. Can be a nice blueprint to make other such tools.

https://tinkering.xyz/proctrace/


Docker images using uv’s python

Tags: tech, python, packaging

It feels more and more that uv might turn out to be a game changer for the Python ecosystem.

https://mkennedy.codes/posts/python-docker-images-using-uv-s-new-python-features/


uv under discussion on Mastodon

Tags: tech, python, foss, community, business

There is a sane conversation going on around uv in the Python community. Here is a good summary.

https://simonwillison.net/2024/Sep/8/uv-under-discussion-on-mastodon/


What’s new in C++26 (part 1)

Tags: tech, c++

Clearly nice examples of better quality of life adjustments coming with C++26.

https://mariusbancila.ro/blog/2024/09/06/whats-new-in-c26-part-1/


Replace strings by views when you can

Tags: tech, c++, performance, memory

Good reminder that packing your data is generally the right move when squeezing for performances.

https://lemire.me/blog/2024/09/09/replace-stdstring-by-stdstring_view-when-you-can/


Why I Prefer Exceptions to Error Values

Tags: tech, failure, exceptions

A couple of flaws in this article I think. For instance, the benchmark part looks fishy to me. Also it’s a bit opinionated and goes too far in advocating exceptions at the expense of error values. Still, I think it shows quite well that we can’t do without exceptions at all, even in the case of error values being available. In my opinion, we’re still learning how both can be cleverly used in code base.

https://cedardb.com/blog/exceptions_vs_errors/


Why some of us like “interdiff” code review systems (not GitHub) · GitHub

Tags: tech, version-control, git

A bit too much of a rant for my taste (even though I agree with the GitHub flaws). That said it illustrates nicely a use of git range-diff which is often overlooked.

https://gist.github.com/thoughtpolice/9c45287550a56b2047c6311fbadebed2


Scope Management 101 - by Kent Beck

Tags: tech, quality, agile, project-management, product-management

He is spot on again. The scope is what will allow to create flexibility in a fixed price project. This is what leads to the necessity to work incrementally.

https://tidyfirst.substack.com/p/scope-management-101


The Impossibility of Making an Elite Engineer

Tags: tech, engineering, career, learning

Interesting musing about what it takes for engineers to grow. Clearly there are a few paradoxes in there… that gives ideas to manage your career though.

https://tidyfirst.substack.com/p/the-impossibility-of-making-an-elite



Bye for now!