I've started the software redesign by going in the opposite direction from SquishBox: instead of adding things, I'm taking things away.

The first step has been to make FluidPatcher into a proper, self-contained synth application package. It works anywhere FluidSynth does, it doesn't know anything about SquishBox, and it can be installed and used independently as a Python package.

This separation is already helping me understand what belongs where.

Things are working smoothly enough now, and I have enough documentation and examples that I'm going to go ahead and publish **FluidPatcher 1.0.0 to PyPI**. I'm sure there will be bugs and future breaking changes, but at some point you have to call something a version 1.0 and let other people use it.

## Making the code match the ideas

A lot of the work getting here has involved encapsulating things in classes.

I'd used classes before when they seemed convenient, but this time I've really leaned into encapsulation and inheritance. The result isn't just code that looks more object-oriented; it has made the underlying concepts in FluidPatcher much clearer.

A good example is bank handling.

The bank files have gotten fairly sophisticated. I'm using some of PyYAML's more clever features, including implicit and path resolvers, to give them useful syntax and organization. But previously, all of that structure leaked out into the rest of the program. A function like `apply_patch()` had to know how the bank was structured and how to combine the different layers of data.

That meant code like this:

```python
for rule in fp.bank.get("rules", []) + fp.bank.get("patches", {}).get(patch, {}).get("rules", []):
    ...
```

That works, but it tells me far too much about how a bank happens to be represented internally.

Now the `Bank` class knows how to interpret itself, so the code that uses it can simply say:

```python
for rule in fp.bank[patch]["rules"]:
    ...
```

That's a small change, but it's the kind of change that makes the rest of the program easier to understand. The code says what it means instead of explaining how the data happens to be stored.

The same thing happened with the MIDI router. The custom router that adds FluidPatcher's rule handling on top of FluidSynth's MIDI router is now an instance of a `Router` class.

That gives me a much cleaner boundary around the router, and it also leaves the door open to other implementations. I've been trying to figure out whether occasional processor overload on the Raspberry Pi is caused by MIDI messages backing up in the Python router. If that's the problem, I could eventually replace it with a faster C implementation without having to redesign everything around it. As long as it behaves like a `Router`, FluidPatcher shouldn't particularly care what it's written in.

## Deciding what *not* to build

The more interesting architectural decisions, though, have been about things I decided **not** to add.

The router now handles every custom parameter in a rule, rather than just the first one. That sounds obvious in retrospect: rules don't inherently have a one-to-one relationship between MIDI messages and actions, so there isn't really a good reason to arbitrarily stop after one parameter.

I've also added some new kinds of transformations that are useful for real MIDI work, including logarithmic value tapers and splitting values into LSB/MSB pairs for controllers that use that format.

But I deliberately decided **not** to make rules able to modify other rules.

I'd been imagining a feature where, for example, a MIDI CC could enable or disable another routing rule, or change its MIDI channel. That would make it possible to build things like layers within a patch that could be switched on and off from a controller.

It's a useful idea. But implementing it would require a way to identify and tag rules, define how rules can interact with one another, and add a bunch of new syntax to the bank files.

At that point, the bank format starts turning from a declarative description of an instrument into something resembling a little programming language.

And I don't want that.

There's a useful line somewhere between **“describe what I want this MIDI setup to do”** and **“write a program in YAML.”** I think this feature would have pushed FluidPatcher across that line.

It's tempting to keep adding clever features because you can imagine a use for them. One of the benefits of separating FluidPatcher from SquishBox is that I can now ask a simpler question:

> **Is this actually a FluidPatcher problem?**

## FluidPatcher should do FluidPatcher things

That question came up again when I was thinking about MIDI routing.

I'd previously considered adding something like `python-rtmidi` so that FluidPatcher could route MIDI to external synthesizers and hardware devices as well as FluidSynth.

That made a lot of sense when FluidPatcher *was* the core of the SquishBox. If SquishBox was going to be a little synth workstation, then it seemed useful for its central software component to manage all of the MIDI connections.

But now that FluidPatcher is its own thing, I see that this is actually the wrong abstraction.

**FluidPatcher should do FluidPatcher things.**

It is a Python toolkit for controlling FluidSynth and doing interesting things with MIDI *inside that environment*. It doesn't need to become a general-purpose MIDI router just because a SquishBox might someday need one.

If I want a general MIDI routing application, I can write one.

If I want a little synth-host application that manages multiple synthesizers and routes MIDI between them, I can write that too.

And some of that functionality may eventually belong in the SquishBox framework itself, where global MIDI connection management is actually useful.

This is one of the things I like about the direction the project is taking. Separating the pieces doesn't mean there has to be less functionality. It means **the functionality can live in the place where it actually makes sense.**

## Documentation as part of the design

The other big part of this release has been documentation.

Documentation is one of those things that's easy to put off when you're writing software for yourself. But working on it has turned out to be surprisingly useful for the architecture itself.

Writing down how something is supposed to work forces me to decide what “supposed to work” actually means.

I've added example programs that demonstrate how FluidPatcher can be used as a library, including one that effectively replaces the old FluidPatcher GUI bank editor. I've also written tutorials with example bank files for each of the major concepts in the bank format.

That's been much more useful than simply documenting individual functions. The examples show what FluidPatcher is *for*, while the API documentation explains how to use it.

It also makes me rethink the video tutorials I've made in the past.

A lot of those videos are now obsolete because the software has changed so much. They may still be useful for historical context or for explaining some MIDI concepts, but they're not particularly good documentation anymore. Videos take a long time to make, and a relatively small code change can make one inaccurate.

Written tutorials have a nice advantage here: when something changes, I can update the example and move on.

## The thing I still haven't done

There's one conspicuous hole in all of this: **tests.**

I haven't written a proper Python test suite yet.

This is partly because tests are tedious, and partly because when you're working on something like this by yourself, there's always another interesting feature you'd rather be working on.

But I'm increasingly convinced that tests aren't just about catching bugs.

They're also a way of recording the behavior of the software.

I've read plenty of code and tutorials where the tests end up being some of the clearest documentation of what the author actually intended. A docstring can tell me what a function is *supposed* to do, but a test tells me what behavior I decided was important enough that I don't want it to change.

That's particularly valuable for FluidPatcher because some of its behavior is fairly subtle. Six months from now, I may not remember exactly why a particular routing rule behaves the way it does.

A test will.

So that's probably the next thing on the list.

For now, though, FluidPatcher has reached a point where I'm comfortable calling it 1.0. The code is cleaner, the boundaries are clearer, the documentation is substantially better, and—perhaps most importantly—I have a much better idea of what FluidPatcher **isn't**.

It's not SquishBox.

It's not a general-purpose MIDI router.

It's not a DAW.

It's not a programming language disguised as a YAML file.

It's a toolkit for building interesting MIDI-driven instruments and applications around FluidSynth.

And now that I know where its boundaries are, I can start building on the other side of them.