RE-MOCT v1.6.2

v1.6.2 released. The status line now reports when your output device is resampling the audio — see Output rate & bit-perfect. The rip screen renders the cover art before it writes anything and lets you choose a different one. A radio stream that cannot recover now stops and says why instead of buffering forever. Non-ASCII text now renders as written rather than as ?. A disc that matches several releases, or a set where two discs share a track count, is chosen by you rather than guessed, and the rip screen shows its cover art before writing. See Text and alphabets and Release & disc selection. This release adds hidden track support: a disc with audio in the pregap ahead of track 1 is detected, shown, played and ripped as its own file. This release adds the [Library] section, collection-wide search with |, and partial rips with u. It also fixes a two-second offset present in every rip RE-MOCT has produced before now - see CD Ripping. RE-MOCT runs on Windows and Linux. Grab a build from the GitHub Releases page, or build from source - see Build. Its streaming source ships as a loadable plugin, so an internet-radio fix can arrive as a rebuilt plugin with no host rebuild.
C++20
PDCursesMod / ncursesw
miniaudio
TagLib
Windows + Linux
Plugin ABI
CD Track 02 [F:] [REPEAT ALL] [SHUFFLE] [CD] [New Miserable Experience (1992)] [EQ] RE-MOCT v1.6.2
Playlist [12]  46m 8s
Gin Blossoms – Hey Jealousy 3:56
Gin Blossoms – Mrs. Rita 4:25
Gin Blossoms – Until I Fall Away 3:52
[#############################---------]  0:28 / 3:56  1411 kbps  44.1 kHz  stereo  CD  vol:15
SPC:pause  n/p:next/prev  [/]:seek  -/+:vol  Tab:focus  Enter:play  ?:help  ^Q:quit

Introduction

RE-MOCT (Music On Console Terminal) is a keyboard-driven terminal music player, CD ripper, internet-radio and podcast client for Windows and Linux, inspired by MOC. It runs in a standard console window with a two-pane layout - file browser on the left, playlist on the right - and is designed for continuous, uninterrupted playback with minimal resource overhead.

It has two faces. Classic mode is a faithful, minimal MOC homage. Press Ctrl+T for Awesome mode - the remix, with a comet progress bar, a sub-cell block visualizer, breathing animations, and 18 named truecolor palettes cycled with F7 / F8 (the 256-color visuals shine on a Linux terminal).

Key design goals: zero UI stutter (all heavy I/O off the main thread), no silent data races (every shared state is explicitly protected), and no volatile CD or hardware paths leaking into persistent config.

Supported Formats

.mp3 .flac .ogg .opus .wav .aiff .aif .m4a .wma .mp4

Playlist files: .m3u .m3u8 .pls .xspf

Interface Layout

┌─ RE-MOCT ─────────────────────────────────────────────────────────────────┐
│  ► Artist - Title        [REPEAT ALL] [SHUFFLE] [CD] Album (Year)  v1.1│
├─ File Browser ───────────────────┬─ Playlist ─────────────────────────────┤
│  [Drives]                        │  Playlist [14]  39m 40s                │
│  [Recent]                        │  ► Green Day - Burnout           2:08  │
│  [FAVs]                          │    Green Day - Having a Blast    2:44  │
│  [Bookmarks]                     │    Green Day - Chump             3:59  │
│  C:\                             │    ...                                 │
│  D:\                             │                                        │
│  ▶ G:\  (CD)                     │  Right pane cycles between:            │
│    CD Track 01                   │  Playlist · EQ · Visualizer            │
│    CD Track 02                   │  Track Info · Lyrics · Queue           │
│    CD Track 03                   │  Help · About · Bookmarks              │
├─ Progress ───────────────────────┴────────────────────────────────────────┤
│  ──────────────────────────────────────────  0:00 / 0:00  1411 kbps  CD  │
├─ Commands ────────────────────────────────────────────────────────────────┤
│  SPC:pause  n/p:next/prev  [/]:seek  -/+:vol  Tab:focus  Enter:play      │
└───────────────────────────────────────────────────────────────────────────┘

Tab switches keyboard focus between the file browser and playlist. The active pane is highlighted. The right pane content changes based on context - press e for EQ, v for visualizer, i for track info, L for lyrics, Q for queue, ? for help, A for about.

Playback

Gapless Playback

Next track is pre-decoded into a second buffer while the current track is still playing. Transition is seamless with no gap between tracks.

Crossfade

Configurable crossfade duration. End of current track fades out while next track fades in simultaneously. Queue overrides crossfade correctly.

ReplayGain

Reads ReplayGain tags from files and applies volume normalization. Toggle with f. Not available during CD playback.

Speed Control

Playback speed adjustable from 50% to 200% in 2% steps via { / }. Varispeed - speed and pitch move together, tape-style, the way a turntable does. This is deliberate, not a limitation.

Balance

Left/right stereo balance via < / >. Reset to centre with `.

BPM Detection

Automatic BPM analysis runs in a background thread on track load. Displayed in track info pane once complete.

Sleep Timer

Cycle through 15 / 30 / 45 / 60 / 90 minute auto-stop timers with Z. Timer shown in title bar.

Repeat & Shuffle

Repeat cycles: off → one → all via r. Shuffle randomises playback order via z. Both persist across sessions.

Playlist

The playlist is the right-hand pane in focus. Tracks are deduped on add. The playlist is restored on next launch (CD tracks are excluded - they are volatile).

a
Add highlighted file or directory (recursive) to playlist
d
Delete selected track from playlist
K / J
Move selected track up / down (d still deletes)
c
Clear entire playlist
S
Save the pane by extension (M3U / M3U8 / PLS / XSPF); on a focused playlist file, reformat it to another container
l
Load / append playlist file - autoplays if stopped
o
Cycle sort: none / title / artist / duration / filename
F3
Follow-playing: cursor tracks the playing track (on by default)
Loading a playlist via l or the goto bar appends to any existing tracks and jumps the cursor to the first newly added entry. If nothing is playing, it autoplays immediately.

File Browser

The left pane. Navigate with arrow keys or j/k. The browser shows a set of virtual entries at the top, above the current directory:

[Drives]
Lists all mounted drives including optical drives
[Recent]
Up to 50 recently played tracks, most recent first
[FAVs]
Curated favourites list - see Favourites section
[Library]
Every artist in your music folders, regardless of folder layout - see Library section
The left arrow does the same thing as the [Back] row in every section: it steps back one level where a section has levels, leaves the section where it does not, and always returns you to the folder you were actually browsing. Every section header says how to leave it.

Navigation

g
Goto bar - type a path with Tab autocomplete. Accepts directories and playlist files.
Tab
Autocomplete in goto bar - cycles through matches
H
Toggle hidden files
O
Cycle browser sort: name / date modified / size
b
Bookmark current directory
B
Show bookmarks popup (Enter to jump, x to delete)
The goto bar accepts drive roots (G:\), directories, and playlist files (.m3u, .pls, etc.). Pressing Enter on a playlist file in the goto bar loads and autoplays it.

Library

[Library] lists every artist in your music folders regardless of how those folders are arranged. RE-MOCT stays a folder player - the directory browser is unchanged and still where everything starts - and this sits beside it for the times you know the artist and not the folder.

Opening it the first time reads the tags of everything under your music folder, which takes a little while and shows its progress as it goes; after that it opens instantly, and it only re-reads files that have actually changed. Choosing an artist opens their albums, choosing an album opens its tracks, numbered and with their lengths. Tracks are ordered by disc and track number, falling back to the filename for rips that were never tagged. Albums that share a name between two artists stay separate. Coming back up a level puts the cursor back on the row you came through rather than at the top of the list.

A library track behaves exactly like the same file in the folder browser, so everything you already know works on it. a on an album row takes the whole album in one press, in the order the album is listed, and says how many tracks it added - tracks already in the playlist are not added twice, so the count is what actually went in. A track that has been deleted since the last scan is reported as missing rather than passed over in silence.

|
Search your whole collection, narrowing as you type - title, artist, album, album artist, genre and filename. Works from inside [Library] and from the folder browser.
\
Search the list in front of you - unchanged. Same key as | , shifted.
g
Inside [Library]: open the genre list, and press again to close it
%
Inside [Library]: most played with counts, then never played, then back
@
On a folder in the browser: add it to the library, or remove it if already added
F12
Rescan the library - it never rescans behind your back, so this is how you tell it to look again
Esc
Cancel a running scan - the index you had before is left exactly as it was
← / [Back]
Step back one level: tracks to albums, albums to artists, artists out of the section
Collection search: a query with several words matches when every word is found somewhere. Accents are folded, so bjork finds BJÖRK, motley crue finds Mötley Crüe, and dvorak finds Dvořák. Enter on a result plays it, and a, q, *, u, x and ; all work on a result too.
Compilations group under Various Artists as one row, with each track showing who performed it. An album is treated as a compilation when it has no album-artist tag, or one saying something like Various or Soundtrack, and at least three different track artists - so an album with guest features stays where it belongs. Genres written two ways are folded into one, listed under whichever spelling more of your files use; nothing is split to achieve it, so Hip-Hop stays one genre and a track with no genre tag is not filed under an invented heading.
Multiple folders: music on a second drive or outside your music folder was invisible to the library. @ adds a folder and is remembered between runs; removing one takes its tracks out straight away and never touches the files. Adding a folder already covered by one you added is refused with a message. A library folder that is not there when you rescan - an unplugged drive, a renamed folder - is skipped and reported, and its tracks are left in the library rather than treated as deleted.

Play Queue

The queue overrides normal playlist order. Queue entries play before the next playlist track, in FIFO order. When the queue empties, playback returns to the playlist from where it left off.

q
Add highlighted track (from playlist or browser) to play queue
Q
Show / hide queue pane
Adding a track to the queue immediately clears any preloaded next-track decoder so the crossfade correctly targets the queue entry rather than the next playlist track. CD tracks can be queued. On disc eject, any queued CD tracks for that drive are automatically purged.

Favourites (FAVs)

A curated list of up to 50 starred audio files. Stored in config as fav= entries. FIFO overflow: adding a 51st entry drops the oldest. CD tracks and directories are never stored.

*
Toggle favourite on highlighted track (playlist or browser). Toast confirms.
Enter
Inside [FAVs]: add file to playlist, play it, navigate browser to its directory
Del
Inside [FAVs]: remove entry from favourites list
Dead path filtering: when displaying the FAVs list, RE-MOCT checks fs::exists() for each entry. Files on unplugged USB drives or deleted paths are shown in a dim/greyed colour and silently skipped on Enter - no crash, no error dialog.

CD Audio (CDDA)

RE-MOCT supports Red Book CD audio via Win32 IOCTL on Windows. Inserting a CD and selecting its drive from [Drives] loads the track list directly into the playlist.

Hardware reading

Raw sector reads via IOCTL_CDROM_READ_RAW. Audio decoded in a dedicated reader thread into a lock-free ring buffer. Audio callback consumes from ring - no mutex in realtime path.

Eject detection

Stopping a CD stops playback but keeps the disc loaded - the track list, titles, and MusicBrainz metadata all survive, and pressing Enter, Ctrl+R, or Ctrl+Y reopens the drive on demand. Stopping does not spin the drive: the handle is closed and reopened lazily. A real eject (during playback, or on the next action against an empty tray) removes all CD entries from playlist and queue, clears MusicBrainz state, and stops cleanly.

To eject from the keyboard, highlight a CD drive in [Drives] (a ⏏ Shift+E hint appears on drives with a disc) and press Shift+E. If that disc is playing, playback stops first. Drives that support software eject open the tray immediately; drives that do not report so honestly and you can use the physical button. The drive list can be refreshed for hot-plugged drives with F12.

Some optical drives take a moment to spin down after stop before their physical eject button responds - that is the drive settling, not RE-MOCT. Give it a second and the button works.
MusicBrainz lookup

Ctrl+R fetches metadata via DiscID (SHA-1 + custom Base64 from TOC). Ctrl+F opens a manual search modal - enter artist and album, get a ranked results list from MusicBrainz, with Discogs as a fallback when MusicBrainz has no match (Discogs results are marked [D]). Both sources load full track titles. Rate-limited to 1 req/sec.

Volatile isolation

CD paths (G:CD Track 01) never enter recent tracks, play stats, config playlists, or bookmarks. All state is purged on eject or application exit.

Note: CD tracks cannot be tag-edited (the e tag editor silently ignores them). The info pane omits "Times Played" for CD tracks. Crossfade and EQ both work in CD mode.
Enhanced CD / CD Extra: discs that carry a data session after the audio tracks are detected automatically. The data session is never listed as a bogus audio track, and when ripping, the final audio track's true end is found so the data portion is not read into the output.

MusicBrainz (Ctrl+R)

When a disc is loaded, Ctrl+R computes the MusicBrainz DiscID from the TOC and queries the database. On success, track titles and artist update in the playlist immediately and the title bar shows album name and year. If the disc is not found, an error message appears in the command bar for ~5 seconds.

Manual Search (Ctrl+F)

Ctrl+F opens a search modal at any time - no disc required. Type an artist and album name, press Enter or Tab to search. RE-MOCT queries MusicBrainz first; if no results are found it falls back to Discogs automatically. Navigate results with Up/Down, press Enter to load metadata. Press Escape to close without changes.

Discogs results are marked [D] in the list. Selecting one fetches that release's full detail from Discogs, so track titles load directly - the same as a MusicBrainz pick. Non-standard track positions (e.g. A1, 1-2) are renumbered to line up with the disc, and section/heading rows that carry no audio are skipped.

Hidden Tracks (HTOA)

Some CDs place audio in the pregap - the span before track 1 - where the drive will read it but no track listing mentions it. RE-MOCT detects it, shows it, plays it, and rips it as its own file.

Detection is from the disc’s table of contents alone, so it is instant on insert and costs no read. A disc whose pregap is longer than five seconds gets a hidden-track row; an ordinary short pregap does not, and such discs behave exactly as they always have.

The row
Sits first, above track 1, numbered 00. The playlist header gains +1 HTOA beside the track count, which stays what the disc says - a 13-track disc still reads 13, because the hidden track is not one of them. With the cursor on the row the position field reads [-/13].
Enter
Plays it, like any CD track. It scrobbles, counts as a play, and appears in [Recently Played].
u / U
Mark it / clear marks. A whole-disc rip never includes it - ripping it is always a deliberate act.
Ctrl+R
Reads its title from MusicBrainz, which carries hidden tracks as a distinct field. Ctrl+F reads it from Discogs.
Ripping it. Marked alone, only the hidden track is ripped. Marked alongside numbered tracks, both happen - the hidden track becomes its own file and the numbered tracks rip as they would have. The file is named 00 - Title, sorts first, and lands in the same album folder; where no source names the track it is <Album> HTOA rather than a placeholder. Per-track volume level is written; album volume level excludes it, since it is not part of the release. The cue sheet carries it as an extra FILE with INDEX 00, emitted only when the hidden track was ripped alongside the whole disc.
What the log tells you. Extraction records how many sectors were read, retried, split, or substituted. If any sector could not be read, the count is written to the log, to disc.json, and shown on the status line - a partial extraction is never handed over looking complete.

Text and alphabets

RE-MOCT displays what your tags actually say. Japanese, Chinese, Korean, Greek, Cyrillic and every other script render as written; accented Latin keeps its accents, so a file tagged Müller shows Müller rather than Muller.

Characters that occupy two terminal columns — most CJK — are measured as two, so rows stay aligned no matter what alphabet a folder is in. Typographic punctuation is still normalised: curly quotes become straight ones, en and em dashes become hyphens, an ellipsis becomes three dots. That is deliberate, and it is the only substitution that remains.

The same text is what leaves the program. Scrobbles to Last.fm and ListenBrainz carry the real title and artist, the OS media card and Discord show them, and the tag editor writes back what you typed rather than a flattened version of it. Before 1.6.1 all four received text with the non-Latin characters replaced.

Release & disc selection

A CD's table of contents identifies the disc, but not which release it belongs to. The same physical disc is often published several ways — on its own, and again inside a box set — and MusicBrainz lists each of them. Separately, a multi-disc release can contain two discs with the same number of tracks, in which case the track count cannot say which one is in the drive.

RE-MOCT used to resolve both of these silently. It now asks.

Ctrl+R
Look the disc up. When it matches more than one release, a list appears showing artist, title, year, country and - in the right-hand column - the disc number that release would give you: 19t for a single-disc release, 3/7 for disc 3 of a 7-disc set, ?/2 when that release cannot say which disc you have.
Ctrl+F
Search by artist and album name instead, when the disc ID matches nothing you want. Available on Linux as well as Windows.
F5
Reopen the list to change your mind. Reopens from what was already fetched - it does not query again.
Esc
Cancel. Nothing is applied and the previous metadata is left as it was.
When two discs share a track count, a second list appears asking which one you have, showing each candidate’s first track - which is usually enough to recognise the disc in your hand even when the counts are identical.
When RE-MOCT has to guess, it says so. A disc whose medium could not be determined is marked in red on screen, recorded in the rip log with the reason, and written to disc.json as disc_source: "ambiguous_fallback" rather than merely as a number. Such a disc is not scrobbled at all until you choose: a guess can be shown on screen, where you can see it and correct it, but it should not become a permanent entry in your listening history. Choosing a disc records disc_source: "user" and scrobbling resumes.
A release you chose by hand is trusted. If you pick a release with Ctrl+F, its tracklist is not re-checked against the disc - that is what makes searching by name a usable repair when the disc ID matches badly. It is also the one remaining way a confidently wrong tag can be written, so choose deliberately.
Disc numbers are written to your files. DISCNUMBER and TOTALDISCS for FLAC and Opus, TPOS for MP3, DISC for WavPack, the disk atom for M4A. Before 1.6.1 the disc number existed only as a folder name and was lost the moment a file left that folder.

CD Ripping

Press Ctrl+Y in CD mode to open the rip dialog. Toggle any mix of output formats - FLAC, MP3, WAV, Opus, WavPack, and M4A - with the number keys before picking a rip mode; all selected formats are written from a single verified disc read, with lossless formats marked as the verifiable master. Output goes to %USERPROFILE%\Music\re-moct\Artist - Album (Year)\. Multi-disc releases nest each disc in its own Disc N subfolder under the album folder; single-disc albums write straight into the album folder.

Ripping only some tracks. With a CD open, mark tracks in the playlist with u and RE-MOCT rips just those; U clears the marks again. Marking nothing rips the whole disc exactly as it always has, so if you never press u nothing about ripping has changed. Unmarking the last track goes back to ripping everything rather than to ripping nothing, and the rip window shows how many of the disc’s tracks it is about to take, appearing only when that is fewer than all of them. Marks survive opening and closing the rip window, and are cleared when you change discs.

What a partial rip deliberately does not write. Some of what a rip writes describes the disc as a whole rather than the tracks you took. There is no cue sheet, because a cue sheet over part of a disc describes a disc that does not exist and cannot be burned back. There is no album volume level either, because it cannot honestly be measured from part of an album - rip half now and half later and you would have two different album levels for one album. Per-track volume levels are still written as usual, and the disc’s table of contents is still saved, since it stays true and records what you did not take as much as what you did. CUETools verification checks the whole disc’s audio as a single number and has no partial form, so it is shown as unavailable while only some tracks are marked rather than quietly ripping the whole disc behind your back. Each omission is named, with its reason, in the rip log.
AccurateRip mode

Verifies each track against the AccurateRip database using CRCv2. Applies drive read offset correction and auto-detects disc pressing offset via frame-450 CRC scan. On mismatch, a second pass is triggered after a cache-flush seek.

CUETools mode

Computes a disc-wide CRC32 (CTDB) that is offset-immune - no drive offset table required - and checks it against the CUETools database over an online lookup. The verdict and the disc ID it was checked under are written into every track's tags, so a rip can be re-checked later without reading the disc again. One verdict covers the whole disc, unlike AccurateRip's per-track result.

Local mode

Best-effort offline rip with no network verification. Useful for obscure pressings not in any database. ReplayGain and cover art still applied.

Output & tagging

FLAC and MP3 files are tagged with artist, album, title, year, track number, ReplayGain (track + album), AccurateRip result, and embedded cover art. Art comes from the Cover Art Archive first; if that has nothing, RE-MOCT falls back to iTunes, then Deezer, matched by artist and album. No API keys are required.

AccurateRip technical detail: CRCs are computed disc-absolutely - mul_by=1 starts at the 150-sector lead-in before each track's nominal LBA. The 150-sector preamble is physically read from disc because it contains real data (not silence) that affects the checksum. Drive offset is applied as a combined sample shift split into whole-sector LBA advance plus sub-sector skip.
Fixed in 1.5.0: every ripped track started two seconds late. RE-MOCT read each track from a position 150 sectors - exactly 2.00 seconds - past where the track actually begins, so each track is missing its first two seconds, carries the first two seconds of the next track on the end instead, and the final track of a disc ends in two seconds of digital silence. CD playback started two seconds into each track for the same reason. The cause was a disc address used in the wrong units: a drive reports where a track starts as a running time from the beginning of the disc, and a read command wants that same position counted without the standard two-second lead-in. A rip is now byte-for-byte identical to the same disc ripped by dBpoweramp, all twelve tracks, with AccurateRip confirming every one at confidence 200. Disc identification is untouched - the AccurateRip, CUETools, CDDB and MusicBrainz disc IDs are computed exactly as before and are unchanged. If you have ripped CDs with RE-MOCT and want them right, they need re-ripping; the audio that was never read cannot be recovered from the files, so there is no tool to correct an existing rip in place and none is planned.
A rip that never asked AccurateRip now says so. AccurateRip is only queried in [A] mode. The other three never ask it, and used to record the answer as though a lookup had been attempted and failed with a network error - a CUETools rip wrote that into the tags of every file it produced. Now the log reads not queried, the summary counts those tracks separately instead of adding them to the not-found total, and no AccurateRip tag is written at all. [A] rips are unaffected and the CUETools verdict itself is unchanged.
The rip screen shows the cover art it is about to use. It is rendered on the confirmation screen, beside the format and mode list, so you see it before anything is written. P opens a list of every image the Cover Art Archive holds for that release - often dozens on a box set, including each disc’s own artwork, booklet pages and tracklist - with a preview beside the list. Choosing one replaces both folder.jpg and the art embedded in every tagged file, so the two cannot disagree. F5 reopens the release list without leaving the screen, for when the cover tells you the release itself is wrong. On a narrow window the preview is omitted and both keys still work.
The cover art, before anything is written. The rip confirmation screen renders the cover it is about to use, beside the format and mode list. P opens a list of every image the Cover Art Archive holds for that release - often dozens on a box set, including each disc’s own artwork, booklet pages and tracklist - with a preview beside the list. On a disc the archive does not cover, the list falls back to iTunes and Deezer results rather than refusing to open. Choosing one replaces both folder.jpg and the art embedded in every tagged file, so the two cannot disagree. F5 reopens the release list without leaving the screen, for when the cover tells you the release itself is wrong. On a narrow window the preview is omitted and both keys still work.
Rip mode selection (after Ctrl+Y)
A
AccurateRip - network CRC verify + drive offset correction
C
CUETools - disc-wide CRC32, offset-immune, online database lookup
Y
Local - best-effort rip, no verification
B
Local 2-pass - no network verification
N / Esc
Cancel - return to CD mode without ripping

Output rate & bit-perfect

Your sound output has a sample rate of its own, and it is not necessarily the rate of the file you are playing. When they differ, something converts the audio on the way out - the operating system, the driver, or the device itself. Until 1.6.2 nothing in RE-MOCT said so, because the rate on the status line has always been the rate of the file.

It now reports both. When the output device runs at a different rate from the track, the status line shows it:

44.1 kHz
The file is 44.1 kHz and the output device is too. Nothing is being resampled.
44.1 kHz -> 48
The file is 44.1 kHz and the device is running at 48. Something converted it on the way out.
44.1 kHz =
Bit-perfect mode confirmed the device is running at the file’s exact format. Nothing was altered at all.
44.1 kHz -> dsp
The equalizer, ReplayGain or balance is active, so samples are being changed regardless of rate.
The first two appear whether or not bit-perfect is switched on, because they are true either way. A blank means the rates match, not that nothing was touched - format and channel count can still be converted underneath without the rate changing. Only = claims nothing was altered at all.
It is worth checking on your own machine. On the Windows machine RE-MOCT is developed on, all eleven outputs run at 48 kHz and none accepts 44.1 - so every CD rip had been quietly converted for years, while a 48 kHz Opus file reached the hardware untouched. On the Linux machine the same library gives the opposite answer: the rips pass through and the Opus is converted. Same files, same code. It is a fact about the output device, not the collection.

Bit-perfect playback

Set bit_perfect=1 in remoct.conf. It is off by default and has no key and no menu, because it is something you decide once for a machine rather than per track.

When on, RE-MOCT asks for exclusive control of the output device at the file’s own format and hands it the samples untouched. This applies to lossless files only - FLAC, WavPack and WAV. Lossy files, radio and CD audio are excluded deliberately: an MP3 or an audiobook has already discarded what there would be to preserve.

It checks rather than assumes. An audio system will happily grant exclusive access and then convert the audio anyway, reporting success the whole time. RE-MOCT compares what the device is actually running - rate, format and channel count - against what it asked for, and if they differ it hands the device back and plays normally. On hardware that cannot take the track’s rate that is every time, and the status line says so rather than claiming something untrue.
Bit-perfect and the equalizer are mutually exclusive, as are bit-perfect and ReplayGain or a non-centre balance. Each of those changes every sample by definition, so the claim cannot hold no matter what the device is doing. The status line shows -> dsp in that case rather than =.

Equalizer

10-band parametric EQ using peaking biquad filters. Works for both file playback and CD audio. Press e to open the EQ pane.

← / →
Select band
↑ / ↓
Adjust gain (±1 dB)
1 – 5
Load preset: Flat / Bass Boost / Vocal / Treble / V-Shape
0
Reset all bands to flat
e
Close EQ pane

Bands: 60 Hz · 170 Hz · 310 Hz · 600 Hz · 1 kHz · 3 kHz · 6 kHz · 12 kHz · 14 kHz · 16 kHz

Visualizer

Press v to open the visualizer in the right pane. It renders a frequency spectrum using block characters, computed with a real FFT so the display is accurate across the full range and reflects each track's actual mastering. Updates in real time from the audio callback output.

Cursor position in the playlist shows in the header as [current/total], so you always know where you are in a long list.

Lyrics

Press L to toggle the lyrics pane. RE-MOCT looks for a .lrc file with the same base name as the playing track (e.g. track.mp3track.lrc). LRC files with timestamps scroll automatically as the track plays.

Tag Editor

Press i to open the track info pane, then e to enter edit mode. Five fields are editable: Title, Artist, Album, Genre, and Year. Works for MP3, FLAC, and all TagLib-supported formats.

↑ / ↓ or j / k
Move between fields
Type
Append characters to field
Backspace
Delete last character
Enter
Save tags and exit edit mode
Esc
Cancel - discard all changes
Tag editing is blocked while a track is playing - stop playback first with s (the warning stays on screen so you can read it). CD tracks and radio streams cannot be edited. Files with no existing tags show empty editable fields. If a save fails - for example a read-only or locked file - RE-MOCT reports it and leaves the on-disk tags and the displayed title unchanged, rather than silently pretending it worked.

Internet Radio & Streaming

RE-MOCT plays internet radio alongside local files. Press Ctrl+U to add and play a stream by URL, or open the [Radio] view to search the radio-browser.info directory (press / to search, d to remove a saved station).

ICY / SHOUTcast
MP3/AAC streams with live StreamTitle now-playing metadata
iHeartRadio
HLS playback with now-playing reconciliation and a digital (web-player) rendition path
Ctrl+A
Toggle the opt-in iHeart deep-analysis log (NDJSON)
Ctrl+P
Experimental iHeart minted-profileId A/B probe (off by default; evaluated only in the deep log)

Streaming is decoded and buffered off the UI thread through a lock-free ring, so radio playback never stutters the interface.

When a stream drops. RE-MOCT retries a lost connection with a widening delay, and while it does the audio already buffered plays out - so a brief outage is often inaudible rather than a gap. When the connection returns, playback resumes at the live edge without replaying the seconds you already heard. If it cannot recover, the stream stops and says why rather than showing [BUFFERING] indefinitely; a stream that has died and one that is about to recover used to look identical. A recording running when a stream dies is finalized properly rather than left open.

Podcasts

The [Podcasts] section sits in the browser sidebar alongside [Radio] and [Books], and follows the same grammar: a list of feeds, each opening to its episodes. Subscriptions are saved and persist across restarts.

Subscribing

Two ways to add a show. Press / inside [Podcasts] to search the Podcast Index directory by name - matches appear in the same list as your feeds, and Enter subscribes. Or paste a feed URL directly. Search uses your own free Podcast Index API key: the first time you press /, RE-MOCT offers to open the signup page or let you enter a key and secret (shown on screen, so a machine that cannot open a browser is not a dead end). The secret is protected at rest the same way the Last.fm one is. Every failure - no key, a wrong key or a drifted system clock, a network drop, zero results - degrades to a status-line message and never blocks pasting a URL or freezes the interface. Already-subscribed shows are named, not duplicated.

Episodes

Enter
On a feed: open its episodes (newest first). On an episode: download and play - full seeking, real duration, embedded chapters
Shift+D
Download an episode for offline listening without playing (queue holds up to 5, one at a time)
y
Mark an episode played or unplayed by hand (status only - resume position and the file are untouched)
d / Del
On a feed: unsubscribe. On a downloaded episode: delete the file (resume position and played state kept)
;
Show the episode's chapters - embedded, or published by the feed (fetched on demand, cached offline once downloaded)

Each episode is marked new, in progress (with the resume time), downloaded, or played. Resume works per episode, so two shows in progress keep their own positions; an episode that reaches the end is marked played and stops rather than auto-advancing. A playing episode shows its own cover (or the show poster) in the art pane and on the OS media card. The download queue retries a failed pull up to three times, then skips it so one bad episode never stalls the rest.

By design, RE-MOCT is a podcast client, not a platform: no refresh daemon, no gpodder.net sync, no OPML import, no auto-download of new episodes, and no auto-advance. Podcasts are never scrobbled.

Audiobooks

The [Books] section lists .m4b audiobooks (and other chaptered MP4-family files). Playback remembers where you left off in each book, and chapters are first-class.

;
Toggle the chapter list - works on a highlighted book in the browser or playlist, not only the playing one
, / .
Jump to the previous / next chapter of the playing book
Enter
On a chapter: start playback at that chapter (an explicit chapter choice wins over a stored resume position)

Chapters are read from the file's own metadata. The MP4 chapter parser handles near-degenerate tables in a single pass, so a book with thousands of tiny chapters opens instantly rather than hanging.

Scrobbling & Presence

RE-MOCT reports what you play to the services you connect:

Ctrl+G
Last.fm login - scrobbles + now-playing
Ctrl+B
ListenBrainz login - scrobbles + now-playing
Ctrl+D
Discord Rich Presence - current track + album art
Sensitive credentials in remoct.conf - the Last.fm shared secret, session key, and in-flight auth token, and the ListenBrainz token - are protected at rest: encrypted with DPAPI on Windows (tied to your user account), and obfuscated with a machine-derived key on Linux (obfuscation, not encryption). The API key and usernames stay plaintext, so the file is still readable and diffable. Because the protection is bound to machine and user, these fields do not carry over if you copy the config elsewhere - you re-authenticate there. This guards against casual disclosure (synced folders, backups, pasted logs), not against software running as your own user; the file is machine-local and gitignored - keep it private.

Key Bindings

The complete list, mirroring the in-app help screen (press ? at any time). Shift-combinations are written as the capital letter.

Playback
Enter
Play selected / enter directory
Space
Pause / Resume
s
Stop
n / p
Next / Previous track
[ / ]
Seek −5 / +5 seconds
- / +
Volume down / up (5%)
m
Mute / unmute
< / >
Balance left / right (` = centre)
{ / }
Speed down / up (2% steps, 50–200%)
f
Toggle ReplayGain normalization
*
Star / favourite highlighted track
e
10-band EQ (1–5: presets, 0: flat)
t
Toggle elapsed / remaining time
w
Toggle clock display in title bar
!
Toggle desktop toast notifications
Modes
r
Cycle repeat: off / one / all
z
Toggle shuffle
Z
Sleep timer: cycle 15 / 30 / 45 / 60 / 90 min / off
Appearance
Ctrl+T
Toggle Classic / Awesome theme
F7 / F8
Awesome theme: previous / next (18 named palettes)
F2
Spectrum style: classic bars / 80s LED
v
Toggle visualizer
Shift+F
Toggle file-type column (FLAC / MP3 / ...) in the playlist
Ctrl+N
Toggle Nerd Font icons (needs a Nerd Font)
~
Reload theme.conf colours (live)
Alt+Enter
Toggle fullscreen (borderless)
Navigation
Tab
Switch focus: browser / playlist
j / k or ↑↓
Navigate down / up
PgDn / PgUp
Navigate one page down / up
Home / End
Jump to first / last row
Go to parent directory
g
Goto directory (Tab = complete)
F3
Follow the playing track (cursor tracks the song)
Playlist
a
Add selection to playlist (recursive)
d
Delete selected track
K / J
Move track up / down
c
Clear entire playlist
Shift+S
Save the pane to M3U / M3U8 / PLS / XSPF (by extension); on a focused playlist file, reformat it to another container
l
Load / append a playlist file (M3U / M3U8 / PLS / XSPF)
\
Search playlist (jump to a track)
q
Add track to play queue
Shift+Q
Show / hide queue pane
Convert
x
Convert file / folder / marked set / playlist to another format
u
Mark / unmark file (for convert)
Shift+U
Clear all marks
↑↓ L/R M
Rip / rec / convert modal: pick per-format quality
Browser & Views
o
Cycle playlist sort: none / title / artist / duration / file
Shift+O
Cycle browser sort: name / modified / size
Shift+H
Toggle hidden files in browser
b
Bookmark current directory
B
Show bookmarks (Enter: jump, x: delete)
i
Track info popup
Shift+L
Toggle lyrics (needs a .lrc file)
Shift+A
About RE-MOCT
Shift+X
Output device picker
CD Audio
Ctrl+Y
Rip CD (A = AccurateRip, C = CUETools, Y = Local, B = Local 2-pass)
Ctrl+R
Fetch CD metadata from MusicBrainz
Shift+E
Eject highlighted CD drive (in [Drives])
F12
Refresh the [Drives] list (pick up hot-plugged drives)
Radio & Streaming
Ctrl+U
Play an internet-radio stream URL
Ctrl+K
iHeart stream mode: Web Player (digital) / Raw broadcast
F6
iHeart re-pin mode: off / ad-escape / hybrid / timed / live-edge (ad and drift handling)
Ctrl+E
Record the playing stream ([Rec] panel)
Ctrl+A
Toggle deep-analysis iHeart log (diagnostic)
Podcasts & Books
/
In [Podcasts]: search the Podcast Index to find and subscribe (paste a feed URL too)
Enter
Open a feed's episodes; on an episode, download and play
Shift+D
Download an episode for offline listening without playing
y
Mark an episode played / unplayed (status only)
;
Show chapters (episode or audiobook; embedded or feed-published)
, / .
Previous / next chapter of the playing book or episode
Scrobbling & Presence
Ctrl+G
Last.fm login (scrobbling)
Ctrl+B
ListenBrainz login (paste user token)
Ctrl+D
Toggle Discord Rich Presence
Utility
Ctrl+F
MusicBrainz / Discogs manual metadata search
Ctrl+O
Normalize folder (batch ReplayGain scan)
Global
?
Toggle this help (j/k, PgUp/PgDn, Home/End scroll)
Ctrl+L
Force redraw / fix layout after resize
Ctrl+Q
Quit RE-MOCT

Configuration

Config is saved automatically to %APPDATA%\RE-MOCT\remoct.conf on Windows and ~/.config/RE-MOCT/remoct.conf on Linux. It is a plain text key=value file, auto-generated - the block below is a trimmed slice of a real one.

volume=0.25
crossfade=2
repeat_mode=1
shuffle=0
awesome_mode=1
awesome_theme=0
eq_enabled=0
nerd_icons=1
follow_playing=1
rip_formats=flac,mp3
flac_level=5
mp3=V2
opus_bitrate=128000
aac_vbr=1
aac_vbr_level=4
fav=C:\Music\No Doubt
bookmark=C:\Music\Lossless

volume is a 0-1 float. repeat_mode is 0/1/2 (off / one / all). crossfade is in seconds (0 disables). The equalizer writes ten separate eq_0eq_9 band keys when enabled, not a single list. rip_formats is a comma-separated set, with per-format quality in its own keys (flac_level, mp3, opus_bitrate, the aac_* ladder).

Notable entries

track=
Playlist entry (CD tracks never saved)
recent=
Recent track path (max 50, CD paths excluded)
fav=
Favourite track path (max 50, FIFO, CD paths excluded)
stat=
Play count and last-played timestamp per file
bookmark=
Bookmarked directory paths (no CD drive roots)
library=
0 removes the [Library] section entirely - absent, not a row that refuses to open. Defaults to on; until you open it, it costs nothing.
bit_perfect=
1 enables bit-perfect playback for lossless files - see Output rate & bit-perfect. Off by default; there is no key or menu for it.
library_root=
Points the library at a folder other than your system music folder. Change it between runs and it rescans once, and says so.

Plugin Architecture

Every platform-specific capability in RE-MOCT sits behind a seam with both a Windows and a Linux implementation - HTTP, IPC, desktop notifications, CD access, and plugin loading. The core is portable; the platform difference is resolved at build time by CMake, never by branching in the code.

The streaming source (the internet-radio engine: ICY, iHeart, HLS, and the now-playing metadata) is a real loadable plugin - remoct_stream.so on Linux, remoct_stream.dll on Windows - driven by the host through a frozen C ABI. Its HTTP is injected across a host-services table, so a streaming fix ships as a rebuilt plugin with no host rebuild. A deterministic test asserts the loaded plugin produces byte-identical audio to the compiled-in reference, so "swap the plugin" never means "hope it still matches."

Build

RE-MOCT builds on Windows (MSYS2 UCRT64) and Linux (Debian Trixie) with CMake + Ninja. The single-header dependencies (miniaudio, nlohmann/json) and the vendored MD5 are checked into the repo, so there is no separate download step - only the toolchain and libraries below. Full details are in BUILD.md.

Get the source

git clone https://github.com/RadMageIRL/re-moct
cd re-moct
On MSYS2, git is not part of the base install - pacman -S git first. It is the MSYS package, not a mingw-w64-ucrt-x86_64- one.

Windows (MSYS2 UCRT64)

Install MSYS2, open a UCRT64 shell, and install the toolchain and libraries:

pacman -S --needed \
  mingw-w64-ucrt-x86_64-gcc \
  mingw-w64-ucrt-x86_64-cmake \
  mingw-w64-ucrt-x86_64-ninja \
  mingw-w64-ucrt-x86_64-pkgconf \
  mingw-w64-ucrt-x86_64-ncurses \
  mingw-w64-ucrt-x86_64-taglib \
  mingw-w64-ucrt-x86_64-flac \
  mingw-w64-ucrt-x86_64-lame \
  mingw-w64-ucrt-x86_64-libebur128 \
  mingw-w64-ucrt-x86_64-fdk-aac \
  mingw-w64-ucrt-x86_64-opus \
  mingw-w64-ucrt-x86_64-opusfile \
  mingw-w64-ucrt-x86_64-libopusenc \
  mingw-w64-ucrt-x86_64-wavpack \
  mingw-w64-ucrt-x86_64-libvorbis
Run the binary from the UCRT64 shell, or keep C:\msys64\ucrt64\bin on PATH (or copy the runtime DLLs next to the exe) so its libraries are reachable.

Linux (Debian Trixie)

libfdk-aac-dev lives in the non-free component, so enable it first, then install the toolchain and libraries:

sudo sed -i 's/^Components: main.*/Components: main contrib non-free non-free-firmware/' \
  /etc/apt/sources.list.d/*.sources
sudo apt-get update

sudo apt-get install --no-install-recommends \
  build-essential g++ cmake ninja-build pkg-config git ca-certificates \
  libflac-dev libmp3lame-dev libebur128-dev libfdk-aac-dev \
  libopus-dev libopusfile-dev libopusenc-dev libwavpack-dev libvorbis-dev \
  libncurses-dev libtag-dev libcurl4-openssl-dev libnotify-dev

Linux (Arch / CachyOS)

There is no non-free split to enable - libfdk-aac is in extra, and Arch ships headers in the main package rather than separate -dev packages:

sudo pacman -Syu --needed \
  base-devel cmake ninja git ca-certificates \
  flac lame libebur128 libfdk-aac \
  opus opusfile libopusenc wavpack libvorbis \
  ncurses taglib curl libnotify
base-devel covers gcc, make, and pkgconf; cmake, ninja, and git are listed separately. Use -Syu rather than -S - partial upgrades are unsupported on Arch, so do not install against a stale package database. On CachyOS these may resolve to cachyos-extra builds instead of the stock Arch ones; same versions, same headers, nothing to configure.

Build and test

Once the libraries are installed, the build is identical on both platforms:

cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build --output-on-failure

Binary output: build/bin/remoct (or remoct.exe), with the streaming plugin beside it in build/bin/plugins/.

Installing (Linux)

install.sh in the repo root does the configure, build, and install in one step, and delegates the install itself to CMake's own rules rather than copying files by hand. That matters: the streaming plugin has to land in <bindir>/plugins/, because the host resolves it against the resolved path of the running executable. A hand-copy anywhere else produces a player that starts fine and then cannot open a single stream - so the layout is defined in exactly one place, CMakeLists.txt, and the script never second-guesses it. Verified on both Debian- and Arch-based systems.

./install.sh                            # build, then install to /usr/local
./install.sh --prefix "$HOME/.local"    # no sudo needed
./install.sh --uninstall                # remove what was installed

The full option set:

--prefix DIR    install prefix (default /usr/local, or $PREFIX)
--no-build      install an existing build, don't rebuild
--link          symlink to the build tree instead of copying (dev mode)
--with-tests    build the test tools too (default: player + plugin only)
--tests-only    build just the test tools, and do not install
--uninstall     remove installed files
-h, --help      this message

BUILD_DIR       build directory (default ./build)
PREFIX          install prefix (same as --prefix)
sudo is used only when the destination is genuinely not writable, and the check walks up to the nearest existing ancestor first - so --prefix "$HOME/.local" never creates root-owned directories in your home. --uninstall reads the install manifest CMake wrote, removing exactly what was installed rather than guessing at a layout that may have moved.
Dev mode. --link symlinks <prefix>/bin/remoct at the build tree instead of copying. No plugin symlink is needed: the host resolves plugins from the resolved executable path, so the linked command still finds the plugin next to the binary it points at. Rebuild and the installed command picks it up with no reinstall.

RE-MOCT v1.6.2  ·  C++20  ·  Built with PDCursesMod (Windows) / ncursesw (Linux), miniaudio, TagLib  ·  github.com/RadMageIRL/re-moct