Check out my progress on my Trello board here - https://trello.com/b/o7vbR74U/maira-refactored-20
I need help with translating the app into multiple languages - https://herboldracing.com/translations
For any questions or suggestions, please reach out to me on my Discord server - https://discord.gg/Y7JN3BAz72
Feel free to contribute to the project by forking the repo and submitting pull requests. Your contributions are greatly appreciated!
This document captures the architectural description from the initial ChatGPT analysis of the MAIRA Refactored codebase.
At a very high level, the solution looks like this:
-
App(App.xaml.cs)- WPF
Applicationsubclass. - Acts as a global singleton and owns almost all “services” / components.
- Exposes them via
App.Instance.
- WPF
-
MainWindowand other WPF windowsMainWindowis the primary shell.- Additional windows: Help, Error, Grip-O-Meter, STT, update dialogs, etc.
Examples (not exhaustive):
Simulator(iRacing telemetry, IRSDKSharper)DirectInputRacingWheelPedalsLFEVirtualJoystickAudioManagerSoundsTelemetry(memory-mapped IPC)CloudServiceTradingPaintsRecordingManagerMultimediaTimerStreamDeckAdminBoxxSpeechToTextWindSteeringEffectsHidHotPlugMonitorLoggerTopLevelWindowDebugSettingsFile
DataContext(global singleton root)Settings(view-model-ish plus persistence wiring)ContextContextSettingsContextSwitches
-
Windows/
- WPF windows such as
MainWindow,HelpWindow,ErrorWindow,GripOMeterWindow,SpeechToTextWindow, etc.
- WPF windows such as
-
Pages/ (WPF
UserControlpages hosted inMainWindow)AppSettingsPagePedalsPageRacingWheelPageSteeringEffectsPageSpeechToTextPageSimulatorPageGraphPageSoundsPageAdminBoxxPageDebugPageDonatePageHelpPage- …and similar.
-
Controls/ (reusable visual components)
MairaButtonMairaMappableButtonMairaComboBoxMairaKnobMairaSwitchMairaButtonMappingMairaAppMenuPopup
-
Viewers/
TelemetryDataViewerHeaderDataViewerSessionInfoViewer
These are custom drawing controls that render telemetry/session/header data.
-
Classes/ — helpers such as:
GraphBaseGraphButtonMappingsRecordingRecordingDataHelpServiceSerializerMathZMiscTradingPaintsXMLUsbSerialPortHelperLogitechGSDK- etc.
-
Translate/ — localization (ResX resources).
-
Help/ & Website/ — static documentation.
-
PInvoke/ — low-level Win32 bindings.
-
Arduino/, AdminBoxx/, InnoSetup/ — external tooling / supporting assets.
Mental model:
Appis the global service locator.Componentsare the engine room.DataContextis the global view-model & settings.Windows/Pages/Controlsform the WPF UI layer on top.
The project uses composition heavily with fairly shallow inheritance. The main hierarchies:
App : Application
Windows (all inherit from Window):
MainWindowErrorWindowGripOMeterWindowHelpWindowNewVersionAvailableWindowRunInstallerWindowSpeechToTextWindowUpdateButtonMappingsWindowUpdateContextSwitchesWindow- (and a few similar dialog-style windows)
Pages (all inherit from UserControl):
AdminBoxxPageAppSettingsPageGraphPagePedalsPageRacingWheelPageSimulatorPageSoundsPageSpeechToTextPageSteeringEffectsPageDebugPageDonatePageHelpPage- etc.
Core custom controls:
MairaButton : UserControlMairaMappableButton : MairaButtonMairaComboBox : UserControlMairaKnob : UserControlMairaSwitch : UserControlMairaButtonMapping : UserControlMairaAppMenuPopup : UserControl
TelemetryDataViewer : ControlHeaderDataViewer : ControlSessionInfoViewer : Control
These are low-level drawing controls:
- Override
OnRender. - Maintain their own visual state.
- Render textual and graphical representations of telemetry / session data.
DataContext : INotifyPropertyChanged- Singleton, exposed as
DataContext.Instance.
- Singleton, exposed as
Settings : INotifyPropertyChangedContextSwitches : INotifyPropertyChangedContext : IComparable<Context?>ContextSettings— options regarding how contexts behave.
Most service classes do not use deep inheritance; they are plain classes. Notable base types:
RecordingManager : IDisposableLogger(plain class; no interface)Graph : GraphBaseGraphBase- Core bitmap/drawing-buffer logic.
Telemetry- Holds
DataBufferStructwith[StructLayout(LayoutKind.Sequential, Pack = 4)]for MMF.
- Holds
HelpIconVisibilityConverter : IMultiValueConverter- A variety of utility helper classes.
Summary of inheritance topics:
- WPF base types (
Window,UserControl,Control) dominate. - Domain-specific bases:
GraphBase,MairaButton. - Data classes implement
INotifyPropertyChanged. - Most functionality is assembled by composition plus global singletons.
This section describes “who talks to whom” at the component level.
App constructs and owns almost all components:
App
├─ Logger
├─ TopLevelWindow
├─ CloudService
├─ SettingsFile
├─ Graph (GraphBase)
├─ Pedals
├─ AdminBoxx
├─ Debug
├─ MainWindow
├─ RacingWheel
├─ ChatQueue
├─ AudioManager
├─ Sounds
├─ DirectInput
├─ StreamDeck
├─ LFE
├─ MultimediaTimer
├─ Simulator
├─ RecordingManager
├─ SteeringEffects
├─ VirtualJoystick
├─ GripOMeterWindow
├─ Telemetry
├─ SpeechToTextWindow
├─ SpeechToText
├─ HidHotPlugMonitor
├─ Wind
└─ TradingPaints
Other classes get at these via App.Instance!.
DataContext.Instanceis created in its constructor and stored statically.DataContextowns:Localization(viaComponents.Localization)._settings : Settings.
Settings responsibilities:
- Raise
PropertyChangedfor WPF bindings. - Coordinate with
App.Instance.SettingsFilefor persistence:- Mark
SettingsFile.QueueForSerialization = trueon changes.
- Mark
- Sometimes trigger UI or other side effects.
ContextSwitches responsibilities:
- Reference
DataContext.Instance. - Toggle
SettingsFile.QueueForSerializationwhen switches change.
Usage graph:
Pages / Controls
└─ bind to → DataContext.Instance
├─ Settings
└─ ContextSwitches, Context, ContextSettings
Settings
└─ serializes via → SettingsFile (component)
SettingsFile
└─ owned by → App
Simulator
- Uses
IRSDKSharper.IRacingSdk. - Produces live properties: lap data, tire data, RPM, flags, etc.
- Updates other components or is polled by them.
- Example:
RacingWheeluses RPM, speed, slip, surface data.
- Example:
DirectInput
- Uses
SharpDX.DirectInput. - Manages physical joystick/FFB devices.
- Sends FFB data using DI constants (e.g.,
DI_FFNOMINALMAX).
RacingWheel
- Uses telemetry from
Simulator. - Implements FFB algorithms:
Native60HzNative360HzDetailBooster- and others.
- Outputs FFB via
DirectInput. - Contributes to shared telemetry (
Telemetry).
Other hardware-facing components:
PedalsLFEVirtualJoystickStreamDeckWindAdminBoxx
Each:
- Wraps a hardware device or subsystem.
- Interacts with
SimulatorandSettings.
Telemetry Component
- Uses
MemoryMappedFileand a fixedDataBufferStructlayout. - Collects values from components like
RacingWheel,Pedals, etc. - Exposes them to external consumers (e.g., overlay tools).
Windows / MainWindow
- Host pages in a frame or via content controls.
- Often set
DataContexttoDataContext.Instance. MainWindowexposes static references to certain pages:- e.g.
MainWindow._graphPage.
- e.g.
- Components like
Graphcall into those references:Graph.Initialize(MainWindow._graphPage.Image);.
Pages
-
In XAML, typically:
<UserControl.DataContext> <Binding Source="{x:Static datacontext:DataContext.Instance}" /> </UserControl.DataContext>
-
Bind to
Settingsand otherDataContextproperties. -
Code-behind handles user interactions and calls:
App.Instance.RacingWheelApp.Instance.Sounds- etc.
Custom Controls
MairaButton:- Visual button with a
Clickevent and bitmap-based styling.
- Visual button with a
MairaMappableButton:- Extends
MairaButtonwith button-mapping behavior (usingButtonMappingshelper).
- Extends
MairaComboBox,MairaKnob,MairaSwitch:- Visual representations bound to
Settings. - Update settings on user interaction.
- Visual representations bound to
- Many of these controls:
- Directly use
App.InstanceandDataContext.Instance.
- Directly use
Viewers
TelemetryDataViewer,HeaderDataViewer,SessionInfoViewer:- Custom drawing.
- Read IRSDK / telemetry values.
- Render textual and graphic info.
This area describes data & control flow in runtime.
-
Startup
Appis created.- It constructs almost all components.
DataContextis constructed, setsDataContext.Instance = this.DataContextcreates:Localization.Settings.
Settings:- Loads defaults.
- Loads persisted values via
SettingsFile.
-
UI Binding
- Each page/window sets
DataContexttoDataContext.Instance(XAML or code). - Controls bind to properties on
Settings(e.g.,RacingWheelEnableForceFeedback, pedal calibration settings, etc.).
- Each page/window sets
-
User Changes a Setting
- WPF binding pushes changes from UI into
Settingsproperties. Settings.OnPropertyChanged:- Optionally logs changes via
Logger. - Sets
SettingsFile.QueueForSerialization = true. - Raises
PropertyChangedto refresh UI.
- Optionally logs changes via
- WPF binding pushes changes from UI into
-
Persistence
SettingsFile(component) handles actual serialization:- Likely using
XmlSerializer+Serializerhelper. - Writes to disk so settings survive restarts.
- Likely using
MVVM characterization:
- Model:
Settings,Context,ContextSettings. - ViewModel: Mainly
Settings+ globalDataContext(single shared VM). - View: Pages, Windows, Controls.
The pattern is MVVM-flavored but:
- Uses a global singleton viewmodel.
- Uses a service-locator (
App.Instance) pattern. - Relies heavily on code-behind event handlers instead of
ICommand.
-
Worker Thread & Simulator
Appowns a worker thread (_workerThread) and anAutoResetEvent.- This drives periodic work for telemetry updates.
Simulator:- Connects to iRacing via
IRSDKSharper.IRacingSdk. - On each tick, pulls telemetry and updates its properties.
- Connects to iRacing via
-
Hardware Components Consume Telemetry
RacingWheel:- Reads data from
Simulator(e.g., speed, surface, slip). - Computes FFB torque using the selected algorithm.
- Reads data from
Pedals:- Reads telemetry (brake pressure, ABS, etc.).
- Applies calibration from
Settings.
LFE,Wind,AdminBoxx,VirtualJoystick:- Consume relevant telemetry and configuration.
-
Hardware Output
RacingWheel+DirectInput:- Collaborate to send FFB packets over USB.
VirtualJoystick:- Exposes values as vJoy axes/buttons.
AudioManager/Sounds:- Play audio cues based on telemetry events.
Telemetry:- Packs selected values into
DataBufferStruct. - Writes to memory-mapped file for other processes.
- Packs selected values into
-
Visual Feedback
Graph(inheritsGraphBase):- Uses
MainWindow._graphPage.Imageas its drawing surface. - Renders multichannel graphs (torque, forces, etc.).
- Uses
TelemetryDataViewer,SessionInfoViewer,HeaderDataViewer:- Render textual / numeric metadata from IRSDK / telemetry.
End-to-end mental pipeline:
iRacing (IRSDK)
↓
Simulator
↓
Components (RacingWheel, Pedals, LFE, Wind, etc.)
↓
Hardware & I/O (DirectInput, Audio, VirtualJoystick, AdminBoxx)
↓ ↓
Telemetry MMF UI Viewers & Graphs