Ryse: Son of Rome Review

Germany-based company Crytek is best known for its Crysis and early Far Cry games and the CryEngine technology. Ryse: Son of Rome is probably not as well-known, and it certainly is not very popular. The game’s Metacritic score is 61, and the user score is 6.8. The biggest complaint I was able to ascertain was a lack of freedom and extreme repetitiveness. While I cannot argue with that, I also see it as a strength instead of a weakness. If you want to know why I encourage you to continue reading.

Read More »

Sign and Verify JWT With Hashicorp Vault REST API

Cryptography is complicated in more than just one way. Therefore, it is commonly recommended not to roll your own, but instead, employ tried and tested methods. Unless you are an experienced cryptographer, it is likely to overlook crucial things, for example, when to authenticate an encrypted message – before decrypting or after? This blog post is about JSON Web Tokens that are digitally signed with an RSA key. Instead of implementing the signing and verification code yourself, you should be using a dedicated server component to do the complex crypto for you, like Hashicorp Vault.

Read More »

Write ID3 mp3 Tags, Cover Art with Python and eyed3

In March 2019, I wrote about using Python with the "pytaglib" library to read and modify audio files’ metadata. This solution worked nicely for what I needed at the time, but as I rewrote my WAV-to-MP3 conversion in Python, I found that it lacked support for adding cover art to the files. After a bit of research, I found eyed3. It also comes with a nice side-effect: it does not require installation of the Taglib C++ library.

Installation is simple.

pip install eyeD3

Additionally, on Windows, you need this, too:

pip install python-magic-bin

For more details, visit the installation guide. Like my old pytaglib post, I will keep this one short and only show code samples for the most relevant tasks.

Before you do anything, import the eyed3 library, of course.

import eyed3

Load a file.

song = eyed3.load(file)

If the file does not yet have any tags, you must create them before setting any metadata.

if not song.tag:
    song.initTag()

Note that this erases existing tags, so only call that if you start from scratch. Now you are ready to set the individual tags or read them if you need them.

song.tag.artist = "Behemoth"
song.tag.album = "The Satanist"
song.tag.genre = "Black Metal"
song.tag.recording_date = 2014
song.tag.track_num = 4
song.tag.title = "Ora pro nobis Lucifer"

The important piece for me, write cover art.

with open(cover_art_filename, "rb") as cover_art:
    song.tag.images.set(3, cover_art.read(), "image/jpeg")

The value 3 indicates that the front cover shall be set. See the documentation for other values. If you are an iTunes user, then select 0 for "Other". iTunes does not seem to like "Front Cover" 🙄.

You may run into a warning message like the following if you use genre names not defined in the ID3 specification.

eyed3.id3:WARNING: Non standard genre name

You do not need to worry about that. If it annoys you, add the following line to your code. eyed3 still writes the tag without any issue.

eyed3.log.setLevel("ERROR")

Convert Java POJO With Protobuf field to JSON Using Jackson

In this blog post, I will explain how to convert a regular Java class that contains a Protobuf message field to JSON using the Jackson library, for example, in a Spring Boot application as a return value of an @Controller method.

You might wonder, how this is such a big deal? After all, you can create complex POJO hierarchies, and Jackson will pick them up just fine. Well, maybe this error message will convince you.

o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [
    Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: 
    Type definition error: [simple type, class com.google.protobuf.UnknownFieldSet]; 
    nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Direct self-reference leading to cycle 
    (through reference chain: com.thecodeslinger.ppjs.web.dto.AwesomeDto["awesomePowerUp"]
        ->com.thecodeslinger.ppjs.proto.AwesomePowerUpOuterClass$AwesomePowerUp["unknownFields"]
        ->com.google.protobuf.UnknownFieldSet["defaultInstanceForType"])] with root cause

Java classes that you create with the Protobuf compiler require their JSON converter JsonFormat.Printer. So, how can we get Jackson and JsonFormat.Printer love each other and have a wedding together?

Simple: we create a custom JsonSerializer.

public class ProtobufSerializer extends JsonSerializer<Message> {

    private final JsonFormat.Printer protobufJsonPrinter = JsonFormat.printer();

    @Override
    public void serialize(Message anyProtobufMessage, JsonGenerator jsonGenerator, SerializerProvider serializerProvider)
            throws IOException {
        // The magic sauce: use the Protobuf JSON converter to write a raw JSON
        // string of the Protobuf message instance.
        jsonGenerator.writeRawValue(protobufJsonPrinter.print(anyProtobufMessage));
    }
}

This class is very simple and can work with any Protobuf Message class instance. This way, it is universal and only needs to be written once. The main ingredient is the jsonGenerator.writeRawValue method that takes the input without modification. Since we already ensure a proper JSON format using Protobuf’s converter, this is no problem in this case. Otherwise, be careful with this method.

The last step is to annotate the Message field in the POJO, so Jackson knows what to do.

@JsonSerialize(using = ProtobufSerializer.class)
private final AwesomePowerUpOuterClass.AwesomePowerUp awesomePowerUp;

You can find a complete working example that uses Spring Boot and a REST endpoint on my Github account.

Install Minikube in VirtualBox on Remote Machine for Kubectl

At work, we are using Kubernetes as a way to run our application services. To test and debug deployments before they go into code review and to the development environment, a local Kubernetes is beneficial. That is where Minikube comes into play. Unfortunately for me, our application services require more resources than my work laptop can provide, especially RAM. Either I close all applications and run Minikube, or I have a helpful browser and IDE window open 😉.

Since I need the local K8s cluster from time to time, I wondered if I could run it on my personal computer and access it from my laptop. This way, I can dedicate at least six physical cores and 24 GB of RAM to the VM (even more, but that was a nice number and more than enough).

Read More »

OpenRGB – An RGB Software I Want to Use (It Runs on Linux!)

If you are in the market for anything gaming PC or gaming laptop related, chances are, you have come across the industry-wide trend of RGB illuminated hardware and peripherals. Everything is RGB, from the graphics card to the RAM, to your headset (because you can see the lights when you wear it 🙄), and many, many more. I am not against RGB lighting per se, but if you follow the industry as a PC hardware enthusiast, it is evident that in some aspects, this has gone too far.

Quick side note: after a rant about RGB software, I will show examples of using OpenRGB on Windows and Linux. If you are interested in only that, skip the rant and scroll to the bottom.

Read More »

Apple, Stop Parenting Me – Rant About iOS 14 Auto-Volume Reduction

Apple is a company that tends to believe it knows best what its customers want. Sometimes a company – not specific to Apple – does actually know better than the customer. Apple has been very active in the past years to push customer health and provide hardware, the Apple Watch, and software, the Health app, to facilitate this push in the form of products they can sell. I do not own an Apple Watch, but I genuinely view it as a good thing.

Now, with iOS 14, Apple has gone a bit too far with regards to health monitoring. It now enforces rules I, the customer and user of a device, cannot override. I am talking about the automatic volume reduction when iOS thinks I have been listening to loud audio for too long.

This is not okay!
This is not a situation where a company knows better.

It is actively limiting its product’s usefulness to me, the customer who paid a lot of money for it. I understand the motivation, but I cannot condone the action taken. Apple cannot even know why I turn up the volume to levels it deems inappropriate for a more extended period.

Here are a few examples, some of which already happened to me.

  1. Bluetooth-pairing the phone with my car’s audio system.

    I usually crank the phone’s volume to max to roughly match the other audio sources, like music on a USB stick (yes, I am a cave-man that has music on a stick).

  2. Listening to podcasts while going for a walk or run next to a busy road.

    Imagine my surprise when the voices speaking to me seemed to have disappeared because iOS lowered the volume to a point where the audio was drowned by traffic noise. I thought my phone had died – which has happened often enough thanks to an iOS bug that incorrectly reported battery percentage and dropped from 30% to turning off within 15-20 minutes.

  3. Listening with studio headphones that have a high input resistance (in ohm).

    I recently bought a new pair of headphones, and the quickest way to compare them with my old ones was to plug them into my phone. 80 Ω is not a lot, but enough to have to crank up the volume a bit higher to get a decent fun level. In the end, it is still much quieter compared to my PC soundcard that supports up to 600 Ω headphones.

No. 1 has not yet happened, but I assume it might once the world is rid of the COVID-19 pandemic, and I can/must travel to work a couple of times per month. On longer car rides, I usually listen to podcasts, and as mentioned, I turn up the volume on my phone in those cases. The other two issues have already managed to annoy me, and No. 3 prompted me to write this little rant – although that is the least likely of the three examples to occur regularly. Most of the time, it will be No. 2 when I am out walking or going for a run. The traffic noise is much worse than people talking to me. I am not even listening to music, which is also worse than people talking to me. I prefer Apple to turn down the car noise on the roads instead of my headphones. Until they can do that, stop messing with my volume, please.

(Is this a ploy to get me to buy horribly expensive AirPods Pro with
noise cancellation?)

I can agree that a notification is a good start to educate users. But please do not take any automatic action. At least make it configurable. I am an adult, and I should be able to decide for myself. On top of that, there are legitimate use-cases where a higher "theoretical" volume is required.

CMake C++ Custom Library on Windows “Undefined Reference” – No Error on Linux

Here is the short version with a quick setup of the situation and the fix. After that, I’ll elaborate a bit.

TL;DR

Setup

I have a custom C++ library and a separate project for tests (all based on Qt 6). The test project requires the library for execution.

Here is a short excerpt of the CMake scripts, first the library, then the tests.

project(wt2-shared VERSION 2.0.0 DESCRIPTION "WorkTracker2 Shared 
Library")

# To export symbols.
add_compile_definitions(WT2_LIBRARY)

# Snip header + source definitions

add_library(${PROJECT_NAME} ${SOURCES} ${HEADERS})

target_include_directories(${PROJECT_NAME} PUBLIC include/)
target_link_libraries(wt2-shared Qt6::Core Qt6::Sql)
project(wt2-shared-test VERSION 2.0.0 DESCRIPTION "WorkTracker2 
Shared Library Tests")

# Snip header + source definitions

add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})

target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDES})
target_link_libraries(${PROJECT_NAME} Qt6::Core Qt6::Test wt2-shared)

Error

This error only occurred on Windows, and it does not matter which toolchain I used, be it MinGW or MSVC. The result was always the same.

The following shows the MinGW error.

[100%] Linking CXX executable wt2-shared-test.exe    
CMakeFiles\wt2-shared-test.dir/objects.a(testdatasource.cpp.obj):testdatasource.cpp:(.text+0x3e5): 
    undefined reference to `__imp__ZN4Data3Sql13SqlDataSourceC1E7QString'
CMakeFiles\wt2-shared-test.dir/objects.a(testdatasource.cpp.obj):testdatasource.cpp:(.text+0x401): 
    undefined reference to `__imp__ZN4Data3Sql13SqlDataSource4loadEv'
CMakeFiles\wt2-shared-test.dir/objects.a(testdatasource.cpp.obj):testdatasource.cpp:(.text+0x514): 
    undefined reference to `__imp__ZN4Data3Sql13SqlDataSourceC1E7QString'	
CMakeFiles\wt2-shared-test.dir/objects.a(testdatasource.cpp.obj):testdatasource.cpp:(.text+0x530): 
    undefined reference to `__imp__ZN4Data3Sql13SqlDataSource4loadEv'
collect2.exe: error: ld returned 1 exit status
mingw32-make[2]: *** 
[wt2-shared-test\CMakeFiles\wt2-shared-test.dir\build.make:142: 
wt2-shared-test/wt2-shared-test.exe] Error 1

Solution

The add_library definition in the CMakeLists.txt was incomplete.
To make it work, I added SHARED because I want a shared library.

add_library(${PROJECT_NAME} SHARED ${SOURCES} ${HEADERS})

Continue reading, though, to get the full picture. There is more to it than just making the library a shared one.

Read More »

CMake CLI Parameter “generator-name” Usage

This topic shouldn’t even require a blog post, but I find the CMake CLI usage rather odd when it comes to specifying a generator. Here’s a shortened "-h" output.

> cmake -h
Usage

cmake [options] <path-to-source>
cmake [options] <path-to-existing-build>
cmake [options] -S <path-to-source> -B <path-to-build>

Specify a source directory to (re-)generate a build system for it in the
current working directory.  Specify an existing build directory to
re-generate its build system.

Options
-S <path-to-source>          = Explicitly specify a source directory.
-B <path-to-build>           = Explicitly specify a build directory.
-C <initial-cache>           = Pre-load a script to populate the cache.
-D <var>[:<type>]=<value>    = Create or update a cmake cache entry.
-U <globbing_expr>           = Remove matching entries from CMake cache.
-G <generator-name>          = Specify a build system generator.
-T <toolset-name>            = Specify toolset name if supported by
                                generator.
-A <platform-name>           = Specify platform name if supported by
                                generator.

    ...snip...

Generators

The following generators are available on this platform (* marks default):
Visual Studio 16 2019        = Generates Visual Studio 2019 project files.
                                Use -A option to specify architecture.
Visual Studio 15 2017 [arch] = Generates Visual Studio 2017 project files.
                                Optional [arch] can be "Win64" or "ARM".

    ...snip...

Borland Makefiles            = Generates Borland makefiles.
* NMake Makefiles              = Generates NMake makefiles.
NMake Makefiles JOM          = Generates JOM makefiles.
MSYS Makefiles               = Generates MSYS makefiles.
MinGW Makefiles              = Generates a make file for use with
                                mingw32-make.
Unix Makefiles               = Generates standard UNIX makefiles.

    ...snip...
    
Kate - Ninja                 = Generates Kate project files.
Kate - Unix Makefiles        = Generates Kate project files.
Eclipse CDT4 - NMake Makefiles
                            = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - MinGW Makefiles
                            = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Ninja         = Generates Eclipse CDT 4.0 project files.
Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.

These generator options do not look like valid parameter values to the "-G" option. But they are. So, if you want to compile on Windows using MinGW, you have to use this.

> cmake -S ../src -B ./ -G "MinGW Makefiles"

Or, if you prefer Visual Studio project files:

> cmake -S ../ -B ./ -G "Visual Studio 15 2017 Win64"

This "syntax" looks weird, and it tripped me for about 10 minutes until I found a sample and understood how it works.

Road to the Perfect Mini ITX PC (Part 5): Other Small Form-Factor ITX Cases

Previously on “Road to the Perfect Mini ITX PC”:

  1. Fractal Design Core 500
  2. NZXT H200
  3. Fractal Design Meshify C
  4. Lian Li TU150

The first two cases I had bought were before I became aware of all the mass-market and niche options that existed at that time. Not only have I learned about the DAN Case, NCase M1, and Streacom DA2, but companies have released more cases during the past year. I’m talking about the NZXT H1, the Cooler Master NR200, or, recently, Phanteks’ second attempt at the Evolv Shift. Even Lian Li’s TU150 landed during that time, my current case. There are even so many more cases, like the Louqe Ghost S1, the FormD T1, Sliger SM560, and many more.

Read More »

Road to the Perfect Mini ITX PC (Part 4): Lian Li TU150

In a few aspects, the Lian Li TU150 is comparable to the NZXT H200. One: for an ITX enclosure, it is on the bigger side. And two: it has a similarly closed-off front. Other than that, they are pretty different, though. In some areas, that is a good thing, and it is a bad thing in others.

In the timeframe of just over a year, this is the fourth (!) computer case that I have tried. Usually, it is the CPU or GPU that gets replaced more often 😅 It is also my current case, which means I can provide good pictures to visualize my thought process better.

Read More »

Road to the Perfect Mini ITX PC (Part 3): Fractal Design Meshify C White

In the third part of my road to the perfect mini ITX computer case, things will get a bit weird. As you may have gathered from the title, I will not talk about a mini ITX enclosure in this blog post. Quite the opposite, in fact: the Fractal Design Meshify C is a full-sized mid-tower ATX case.

You may now wonder why I suddenly had a change of heart and ditched a big.SMALL™ case for a not-so-small big computer tower. Well, I was surrendering to big graphics cards. Or, put the other way around, I was annoyed that I had to search endlessly to find a fast and quiet, and affordable two-slot graphics card model, only to fail ultimately. But, let me not get ahead of myself and start from the beginning, the same way I did for the previous two blog posts.

Read More »

Road to the Perfect Mini ITX PC (Part 2): NZXT H200

The second of the bunch is one of the stylish cases from NZXT, the H200. While it is technically a mini ITX chassis, it is a large case for that market segment. Just like the Fractal Design Core 500, it is compatible with a wide range of hardware, making it the perfect enclosure for price-conscious buyers. On top of that, it also is beautiful.

Unfortunately, I do not have an image of a complete desk setup with this case. Here is one with a good look at the internal layout and installed hardware.

Read More »

Road to the Perfect Mini ITX PC (Part 1): Fractal Design Core 500

The computer that I bought roughly a year ago has seen quite a few revisions already. But I am not talking about the core hardware – although I switched the GPU at one point. I mean the case. I wanted to go with something small from the start, so the basis is a mini ITX mainboard. However, I have not been incredibly happy with any of the cases so far. In this first installment in a series of several blog posts, one for each computer case, I will share my experiences in building a small, attractive, and performant and yet price efficient computer. I will cover design, hardware compatibility, pricing, and availability. Unlike the YouTube tech creators, not everybody has a seemingly unlimited budget or receives hardware from the manufacturers for review or showcases. It may look easy in all those YouTube videos, but it might not be for everyone.

Although I am mainly talking about gaming hardware, the same thoughts also apply to compact office PCs or workstations. Depending on the use case, i.e., which PC component requires the most focus, one or the other might become less or more relevant. So, first off is the Fractal Design Core 500.

Read More »