Home Page
Archive > Posts > 2024 > All
Search:

RME Babyface Pro in Linux
An exercise in patience

After years of saying I’d do it, I'm finally moving my ecosystem from Windows to Linux. After some experimenting and soul searching, I've decided to go with Linux Mint, as it's not exactly Ubuntu with their atrociously horrible decisions, but it provides stability, ease of setup, and a similar enough interface to Windows so as to not lower my productivity.

Getting all of my legacy hardware working, including my 6-monitor setup, was mostly painless, but my beloved Babyface Pro (external professional audio mixer hardware) has been absolute hell to get working. It only natively supports Windows, OSX, and iOS for the “PC” (USB audio passthrough) mode, and the CC mode (class compliant) does not offer my custom waveform transforms. So, my only real option was to use the other analog input interfaces on the Babyface (XLR, SPDIF, or quarter inch audio).


The first hurdle was the power. Normally the device is powered through USB, however if I was going to be using the other audio inputs, I didn't want to leave the USB plugged in all the time, and the Babyface doesn't come with a power adapter. Fortunately, I had a 12V 1A+ power adapter in my big box of random power adapters. The second hurdle was when I discovered that the Babyface does not store the mixer settings when it's powered off. So, every time it gets powered on, it needs to be hooked to another (Windows) machine that can push the mixer settings to it. This also isn't too big a deal as I keep it on a UPS so it generally won't lose power, and if it does, I can use a VM to push the settings.

The next problem was deciding what interface to go through. I was really hoping to use SPDIF/optical since it is a digital signal that does not suffer from interference and degradation, but all the SPDIF interfaces I tried (4 in total) all sounded like garbage. I guess the SPDIF interface on the Babyface is a piece of Junk, which was very disheartening.


My only remaining option was using the analog inputs. I decided to use a mini (3.5mm; 1/8") stereo to quarter inch (6.35mm) mono splitter cord to run into “IN 3/4” and this worked perfectly. However, if the USB interface is plugged in at the same time then this actually creates very audible line noise on the analog inputs within the Babyface itself! This is a horrible design flaw of the device that I was shocked to run into. Fortunately, as mentioned in step 1, I already planned on having the USB cord unplugged, so not a deal breaker.

I first tried the headphone and line out jacks on my motherboard, but the audio quality was only at about 90%. I next tried the line out on my Creative Sound Blaster Audigy from 2014 and the audio was at about 95% quality. It also felt like a cardinal sin to plug in a PCIE 1.0 1x device (0.250 GB/s) into a PCIE 5.0 16x slot (63 GB/s) - lol. So, I bought a Sound Blaster Play 3! USB to mini audio adapter and the audio was perfect! I finally had my setup figured out.


As a fun note, I went to an audiologist a few days ago to have my hearing tested, and the waveform I had devised (through brute force testing) that I had been using through the Babyface for the last 7 years was the exact inverse of the results on the hearing loss frequency chart.

6 annoyances in GoLang
I still love it though

Go has been around for quite a while now and has had a lot of time to grow and mature, and it is really quite a lovely language. Watching it develop since its inception has been a delight. Its native concurrency is best in the industry, it is designed for things to be super-clean and standardized, and it compiles really fast. The support for it in IntelliJ/GoLand is top-notch and makes it really a blast to work in.


There are of course a lot of design decisions in the language that could be debated, like how setting a variable has to be a top level statement that cannot be embedded in other statements, how there is no “real” class based system, and how their specific name casing conventions are actually built into the language and enforced. However, I understand the reason for all of these decisions and feel that the designers of the language were justified in the decisions they made for the reasons they give.


There are at least 6 problems I’ve run into a lot recently though that I feel could be better. I would love to jump into the source of the language and see about making these fixes myself, but the GoLang team has a history of completely ignoring outsiders, and it would be highly unlikely that they would accept anything I submitted, especially since a number of these things would require very long and complicated proposals to even start looking at. Go may be an open source language, but it is not open development.


These first 3 would be additions to the language that would not break anything, which is in line with their promise of never breaking anything.

1) Interface with constraints elements cannot be used as variable types. Example:
type intish interface{ int64 | uint64 }
func adder[T intish](val T) T { return val + 1 } //This is allowed
var val intish //Error: cannot use type intish outside a type constraint: interface contains type constraints

IntelliJ inspection Error: Interface includes constraint elements '...', can only be used in type parameters

It wouldn’t be a big change for the compiler and runtime to be able to enforce these types of constraints when typecasting and I really wish the language had this. This is the type of thing TypeScript does beautifully since it is actually part of its native design and functioning.


2) Member functions (methods with receivers) cannot have generics
type foobar int
func (*foo) bar[T any](val T) {} //Method cannot have type parameters

3) []generic cannot be typecast to []any

There is no real reason that an array containing interfaces shouldn’t be convertable via typecast to another array of interface types as long as they are compatible.

type testAny any
startVar := make([]testAny, 3)
endVar := []any(startVar) //cannot convert startVar (variable of type []testAny) to type []any


This next one is an unfortunate consequence of the language design and would not be easily fixable.


4) Import cycle not allowed

This means there cannot be circular dependencies. If package A includes package B, then package B cannot include package A. This often wasn’t a problem with languages like C since the headers were independent of the implementations, but because packages in Go do not have headers, and their package types cannot be used as method receivers in other packages, this one is just not possible. And it can be quite limiting.



This next one is a bug in either the language or the documentation, and I consider it to be a security problem, but the go team does not seem to care about it. See https://github.com/golang/go/issues/65201


[Edit on 2024-04-10] Oh wow! they fixed it!


5) sql.RawBytes modifies the current buffers instead of setting new buffers like the documentation says.

All the documentation says about RawBytes is “RawBytes is a byte slice that holds a reference to memory owned by the database itself. After a Rows.Scan into a RawBytes, the slice is only valid until the next call to Rows.Next, Rows.Scan, or Rows.Close.”


If the value you are reading is a []byte/string then reading into a RawBytes works as expected. However, if it is anything else, like an int, it reads into the buffer that RawBytes already holds. This can lead to buffer injections and other really nasty bugs. For example:

db.Exec(`CREATE TEMPORARY TABLE goTest (i int NOT NULL, str varchar(10)) ENGINE=MEMORY`)
db.Exec(`INSERT INTO goTest VALUES (?, ?)`, 6, "foobar")
var scanIn sql.RawBytes
rows, _ := db.Query(`SELECT str FROM goTest WHERE i=?`, 6)
rows.Next()
rows.Scan(&scanIn)
//Do something with scanIn
rows.Close()
rows, _ = db.Query(`SELECT i FROM goTest WHERE i=?`, 6)
rows.Next()
rows.Scan(&scanIn) //This corrupts the internal sql driver buffer since it reads an int into the pointer we received earlier


This final one has been an annoyance of mine since day 1 of working with Go, and it still annoys the hell out of me. There could be ways to fix it without breaking things, but I cannot think of any truly elegant solutions.


6) Short variable declaration can’t handle setting both new and already existing variables
This is the most common pattern you’ll see in Go by far:
var data string
if _data, err := someFunc(); err != nil {
    fmt.Println(err)
} else {
    data = _data
}

You can either do it this way, or also declare err in the outer scope, thereby polluting the scope with unneeded variables. There is no way to set both the temporary error variable and also set the outer data variable in 1 line.


One non-breaking hack would be to add a symbol before variables you wanted to set in the outer scope. Example:

var data string
if +data, err := someFunc(); err != nil {
    fmt.Println(err)
}