Mcaster1DSPEncoder is an open-source, multi-format audio streaming DSP encoder suite — a maintained, modernized fork of the EdCast/Oddsock DSP encoder lineage. It ships as a plugin for Winamp, foobar2000, and RadioDJ, and as a self-contained encoder application targeting Shoutcast and Icecast servers.
All 4 build targets compiling and running under VS2022 v143 toolset.
Full AltaCast → Mcaster1DSPEncoder rename, ResizableLib UI, Segoe UI, native LAME.
MP3/LAME, Opus, HE-AAC/fdk-aac, OggVorbis, FLAC. PortAudio WASAPI capture. Volume slider. All codecs integrated and tested.
YAML-first init across all 4 targets. Per-encoder YAML files, legacy .cfg migration. config_yaml.cpp in all vcxproj files.
Podcast RSS feed gen, Podcast Settings tab, ICY 2.2 Extended tab (all 12 categories), header injection on Icecast2 connect, config save hardening.
Auto-discovery, shared YAML config, live health feedback, song history push API.
Castit VC6 → VS2022: libxml2 / libcurl / libmariadb via vcpkg. Broadcast scheduler modernization.
Listener metrics, tune-in/tune-out signals, platform engagement via ICY 2.1, Castit playlist optimization.
All issues blocking compilation under Visual Studio 2022 (v143 toolset, Windows 10 SDK) were resolved. All 4 build targets compile cleanly and run without crashes.
| Target | Output | Status |
|---|---|---|
| Core encoder library | libmcaster1dspencoder.lib | DONE |
| Winamp DSP plugin | dsp_mcaster1.dll | DONE |
| RadioDJ DSP plugin | dsp_mcaster1_radiodj.dll | DONE |
| foobar2000 component | foo_mcaster1.dll | DONE |
| Encoder application | Mcaster1DSPEncoder.exe | DONE |
_WIN32_WINNT=0x501 → 0x0601 to resolve _REASON_CONTEXT C2011 struct redefinition against Win10 SDKalloc_fixed::alloc::move_from() calling pfc::copy_array_t() on a type lacking has_owned_items — replaced with element-by-element copy loopFOOGUIDDECL (__declspec(selectany)) with empty define so class_guid symbols become regular externals visible to lib.exe, resolving mass LNK2001/MD (MultiThreadedDLL), eliminating STATUS_STACK_BUFFER_OVERRUN (0xC0000409) crash from mixed /MT+/MD buildGoal: Complete renaming of every AltaCast identity in the codebase. No new features. "Standalone" is dropped from all names — the application is self-contained by definition.
| Old Name | New Name | Notes |
|---|---|---|
altacastStandalone.exe | Mcaster1DSPEncoder.exe | Drop "Standalone" — implied |
altacastStandalone.vcxproj | Mcaster1DSPEncoder.vcxproj | |
libaltacast/ | libmcaster1dspencoder/ | Rename directory |
libaltacast.lib | libmcaster1dspencoder.lib | |
libaltacast.vcxproj | libmcaster1dspencoder.vcxproj | |
libaltacast.cpp / .h | libmcaster1dspencoder.cpp / .h | |
libaltacast_socket.* | libmcaster1dspencoder_socket.* | |
libaltacast_resample.* | libmcaster1dspencoder_resample.* | |
dsp_altacast.dll | dsp_mcaster1.dll | Winamp plugin |
altacast_winamp.vcxproj | mcaster1_winamp.vcxproj | |
dsp_radiodj.dll | dsp_mcaster1_radiodj.dll | RadioDJ plugin |
altacast_radiodj.vcxproj | mcaster1_radiodj.vcxproj | |
foo_altacast.dll | foo_mcaster1.dll | foobar2000 component |
altacast_foobar.vcxproj | mcaster1_foobar.vcxproj | |
altacast.h | mcaster1dspencoder.h | Shared plugin header |
altacast.rc | mcaster1dspencoder.rc | Shared resource file |
altacastStandalone.sln | Mcaster1DSPEncoder.sln | |
altacaststandalone_N.cfg | mcaster1dspencoder.cfg | Already done |
APP_NAME=altacast_* | APP_NAME=mcaster1_* | Preprocessor defines |
Complete inventory of all instances requiring change, organized by category.
| Current | New | File |
|---|---|---|
CaltacastStandaloneApp | CMcaster1DSPEncoderApp | altacastStandalone.h:22 |
dsp_altacast (class) | dsp_mcaster1 | altacast_foobar.cpp:153,253 |
initquit_altacast | initquit_mcaster1 | altacast_foobar.cpp:331 |
altacast_play_callback_ui | mcaster1_play_callback_ui | altacast_foobar.cpp:356 |
altacastGlobals (struct) | mcaster1Globals | libaltacast/libaltacast.h (all refs) |
| Current | New | File(s) |
|---|---|---|
altacast_init(altacastGlobals *g) | mcaster1_init(mcaster1Globals *g) | MainWindow.cpp:28,401,954,1005,1274,1360 |
altacast_init(altacastGlobals *g) | mcaster1_init(mcaster1Globals *g) | altacastStandalone.cpp:60 |
altacast_init(altacastGlobals *g) | mcaster1_init(mcaster1Globals *g) | altacast_radiodj.cpp:110 |
altacast_init(altacastGlobals *g) | mcaster1_init(mcaster1Globals *g) | altacast_foobar.cpp:68 |
altacast_init(altacastGlobals *g) | mcaster1_init(mcaster1Globals *g) | altacast_winamp.cpp:110 |
| Current | New | File |
|---|---|---|
#ifndef __DSP_ALTACAST_H | #ifndef __DSP_MCASTER1_H | libaltacast.h:1 |
#define __DSP_ALTACAST_H | #define __DSP_MCASTER1_H | libaltacast.h:2 |
FRONT_END_ALTACAST_PLUGIN 1 | FRONT_END_MCASTER1_PLUGIN 1 | libaltacast.h:110 |
AFX_ALTACASTSTANDALONE_H__* | AFX_MCASTER1DSPENCODER_H__* | altacastStandalone.h:4,5,50 |
#ifndef ALTACASTSTANDALONE | #ifndef MCASTER1DSPENCODER | EditMetadata.cpp:191 |
#ifdef ALTACASTSTANDALONE | #ifdef MCASTER1DSPENCODER | MainWindow.cpp:978,1061,1247 |
#ifndef ALTACASTSTANDALONE | #ifndef MCASTER1DSPENCODER | MainWindow.cpp:1482,1681 |
#ifdef altacastSTANDALONE | #ifdef MCASTER1DSPENCODER | libaltacast.cpp:3090 |
IDD_ALTACAST_CONFIG | IDD_MCASTER1_CONFIG | resource.h:7 |
IDD_ALTACAST 301 | IDD_MCASTER1 301 | resource.h:30 |
IDD = IDD_ALTACAST | IDD = IDD_MCASTER1 | MainWindow.h:56 |
ALTACAST_PLUGIN | MCASTER1_PLUGIN | altacast_foobar.vcxproj:76,124 |
ALTACAST_PLUGIN | MCASTER1_PLUGIN | altacast_radiodj.vcxproj:82,134 |
ALTACAST_WINAMP_EXPORTS | MCASTER1_WINAMP_EXPORTS | altacast_winamp.vcxproj:82,131 |
ALTACAST_RADIODJ_EXPORTS | MCASTER1_RADIODJ_EXPORTS | altacast_radiodj.vcxproj:82,134 |
ALTACASTSTANDALONE | MCASTER1DSPENCODER | altacastStandalone.vcxproj:79,91,127 |
APP_NAME=altacast_foobar | APP_NAME=mcaster1_foobar | altacast_foobar.vcxproj:76,124 |
APP_NAME=altacast_radiodj | APP_NAME=mcaster1_radiodj | altacast_radiodj.vcxproj:82,134 |
APP_NAME=altacast_winamp | APP_NAME=mcaster1_winamp | altacast_winamp.vcxproj:82,131 |
| Current String | New String | File |
|---|---|---|
"AltaCast" (dialog caption) | "Mcaster1 DSP Encoder" | altacast.rc:35 |
"AltaCast 1.1" (about text) | "Mcaster1 DSP Encoder" | altacast.rc:189 |
"AltaCast Standalone" (caption) | "Mcaster1 DSP Encoder" | altacast.rc:197 |
"altacast\n" (foobar info) | "mcaster1dspencoder\n" | altacast_foobar.cpp:36 |
"Written by admin@altacast.com\n" | "Maintained by davestj@gmail.com\n" | altacast_foobar.cpp:37 |
"dsp_altacastv2" (cfg key) | "dsp_mcaster1v2" | altacast_foobar.cpp:40 |
"altacast_foo" (log prefix) | "mcaster1_foo" | altacast_foobar.cpp:104 |
"altacast V3" (version str) | "mcaster1dspencoder V3" | altacast_foobar.cpp:282 |
"dsp_altacast" (log prefix) | "dsp_mcaster1" | altacast_radiodj.cpp:20 |
"altacaststandalone" (log prefix) | "mcaster1dspencoder" | altacastStandalone.cpp:19 |
LoadConfigs(currentDir, "altacaststandalone") | LoadConfigs(currentDir, "mcaster1dspencoder") | altacastStandalone.cpp:168 |
"dsp_altacast" (log prefix) | "dsp_mcaster1" | altacast_winamp.cpp:20 |
LoadConfigs(".", "altacaststandalone") | LoadConfigs(".", "mcaster1dspencoder") | Dummy.cpp:98 |
CInternetSession session("altacast") | CInternetSession session("mcaster1dspencoder") | MainWindow.cpp:193 |
"altacast" (tray notification) | "mcaster1dspencoder" | MainWindow.cpp:639 |
"altacast.chm" (help file) | "mcaster1dspencoder.chm" | MainWindow.cpp:1543 |
"altacast.log" | "mcaster1dspencoder.log" | libaltacast.cpp:127 |
"altacast" (defaultConfigName) | "mcaster1dspencoder" | libaltacast.cpp:600,670,694 |
"altacast_aacp.ini" | "mcaster1_aacp.ini" | libaltacast.cpp:2090 |
"ENCODEDBY","altacast" (FLAC tag) | "ENCODEDBY","mcaster1dspencoder" | libaltacast.cpp:2415 |
strcpy(g->gAppName, "altacast") | strcpy(g->gAppName, "mcaster1dspencoder") | libaltacast.cpp:2798,3253 |
"http://www.altacast.com" | "https://github.com/davestj/Mcaster1DSPEncoder" | libaltacast.cpp:2834 |
| Current | New | File |
|---|---|---|
IDD_ALTACAST | IDD_MCASTER1 | altacast.rc:32 |
IDD_ALTACAST_CONFIG | IDD_MCASTER1_CONFIG | resource.h:7 |
mainWindow->Create((UINT)IDD_ALTACAST,...) | mainWindow->Create((UINT)IDD_MCASTER1,...) | altacast_foobar.cpp:131 |
mainWindow->Create((UINT)IDD_ALTACAST,...) | mainWindow->Create((UINT)IDD_MCASTER1,...) | altacast_radiodj.cpp:246 |
mainWindow->Create((UINT)IDD_ALTACAST,...) | mainWindow->Create((UINT)IDD_MCASTER1,...) | altacast_winamp.cpp:246 |
mainWindow->Create((UINT)IDD_ALTACAST,...) | mainWindow->Create((UINT)IDD_MCASTER1,...) | Dummy.cpp:108 |
| Current | New | File |
|---|---|---|
<TargetName>dsp_altacast</TargetName> | <TargetName>dsp_mcaster1</TargetName> | altacast_winamp.vcxproj:47 |
<TargetName>foo_altacast</TargetName> | <TargetName>foo_mcaster1</TargetName> | altacast_foobar.vcxproj:47 |
$(OutDir)dsp_altacast.dll | $(OutDir)dsp_mcaster1.dll | altacast_winamp.vcxproj:101,148 |
foo_altacast.dll/.pdb/.lib | foo_mcaster1.dll/.pdb/.lib | altacast_foobar.vcxproj:93,103,141 |
$(OutDir)altacastStandalone.exe | $(OutDir)Mcaster1DSPEncoder.exe | altacastStandalone.vcxproj:96,144 |
libaltacast.lib/.pch/.bsc | libmcaster1dspencoder.lib/.pch/.bsc | libaltacast.vcxproj:76,81,105,110 |
| Current | New | File |
|---|---|---|
Project name "altacastStandalone" | "Mcaster1DSPEncoder" | altacastStandalone.sln:6 |
Project name "libaltacast" | "libmcaster1dspencoder" | all .sln files |
Project name "altacast_winamp" | "mcaster1_winamp" | altacast_winamp.sln |
Project name "altacast_foobar" | "mcaster1_foobar" | altacast_foobar.sln |
Project name "altacast_radiodj" | "mcaster1_radiodj" | altacast_radiodj.sln |
| Current | New | Files |
|---|---|---|
<RootNamespace>altacastStandalone</RootNamespace> | <RootNamespace>mcaster1dspencoder</RootNamespace> | altacastStandalone.vcxproj:19 |
<RootNamespace>altacast_*</RootNamespace> | <RootNamespace>mcaster1_*</RootNamespace> | all plugin vcxproj |
<RootNamespace>libaltacast</RootNamespace> | <RootNamespace>libmcaster1dspencoder</RootNamespace> | libaltacast.vcxproj:15 |
libaltacast;libaltacast_config (include dirs) | libmcaster1dspencoder;libmcaster1dspencoder_config | all vcxproj |
libaltacast/Debug (lib dirs) | libmcaster1dspencoder/Debug | all vcxproj |
ProjectReference Include="libaltacast\libaltacast.vcxproj" | ProjectReference Include="libmcaster1dspencoder\libmcaster1dspencoder.vcxproj" | all consumer vcxproj |
Include="altacast.h" / Include="altacast.rc" | Include="mcaster1dspencoder.h" / .rc | all vcxproj |
Include="altacast_winamp.cpp" | Include="mcaster1_winamp.cpp" | mcaster1_winamp.vcxproj |
Include="altacast_foobar.cpp" | Include="mcaster1_foobar.cpp" | mcaster1_foobar.vcxproj |
Include="altacast_radiodj.cpp" | Include="mcaster1_radiodj.cpp" | mcaster1_radiodj.vcxproj |
Include="libaltacast.cpp/h" | Include="libmcaster1dspencoder.cpp/h" | libmcaster1dspencoder.vcxproj |
All references in *.iss installer script:
Source: file paths for dsp_altacast.dll, foo_altacast.dll, altacastStandalone.exe[Registry] keys using AltaCast as a key nameOrdered sequence to avoid broken references mid-rename:
Step 1 — Rename source files inside libaltacast/: libaltacast.cpp → libmcaster1dspencoder.cpp libaltacast.h → libmcaster1dspencoder.h libaltacast_socket.cpp → libmcaster1dspencoder_socket.cpp libaltacast_socket.h → libmcaster1dspencoder_socket.h libaltacast_resample.h → libmcaster1dspencoder_resample.h libaltacast.vcxproj → libmcaster1dspencoder.vcxproj Step 2 — Rename libaltacast/ folder: src/libaltacast/ → src/libmcaster1dspencoder/ Step 3 — Rename plugin source files: altacast_winamp.cpp → mcaster1_winamp.cpp altacast_winamp.h → mcaster1_winamp.h altacast_foobar.cpp → mcaster1_foobar.cpp altacast_radiodj.cpp → mcaster1_radiodj.cpp altacast_radiodj.h → mcaster1_radiodj.h Step 4 — Rename shared files: altacast.h → mcaster1dspencoder.h altacast.rc → mcaster1dspencoder.rc altacastStandalone.h → Mcaster1DSPEncoder.h altacastStandalone.cpp → Mcaster1DSPEncoder.cpp Step 5 — Rename project files: altacastStandalone.vcxproj → Mcaster1DSPEncoder.vcxproj altacast_winamp.vcxproj → mcaster1_winamp.vcxproj altacast_foobar.vcxproj → mcaster1_foobar.vcxproj altacast_radiodj.vcxproj → mcaster1_radiodj.vcxproj Step 6 — Rename solution files: altacastStandalone.sln → Mcaster1DSPEncoder.sln altacast_winamp.sln → mcaster1_winamp.sln altacast_foobar.sln → mcaster1_foobar.sln altacast_radiodj.sln → mcaster1_radiodj.sln
In all About dialogs, splash screens, installer text, and source file headers:
Mcaster1 DSP Encoder Maintainer: Dave St. John <davestj@gmail.com> https://github.com/davestj/Mcaster1DSPEncoder Based on EdCast/Oddsock DSP encoder by Ed Zaleski Original project: https://www.oddsock.org/
All source file copyright comment blocks — ADD, do not replace original:
// Mcaster1DSPEncoder — fork of EdCast/Oddsock DSP encoder // Maintainer: Dave St. John <davestj@gmail.com> // Original Author: Ed Zaleski
AltaCast uses BladeMP3EncDLL.h — an obsolete Windows-only interface. All US MP3 patents expired
April 23, 2017. LAME (LGPL) is now freely distributable. LAME 3.100 source is in external/lame/;
also available via vcpkg mp3lame:x64-windows.
external/lame/vc_solution/vc9_libmp3lame_dll.vcproj to v143, build libmp3lame.dll#ifdef WIN32 BladeMP3 guards with #ifdef HAVE_LAME using standard lame.h APIBladeMP3EncDLL.h from external/include/libmp3lame.dll with all targetsGoal: Ship both 32-bit and 64-bit builds to maximize Windows compatibility. x64 for modern systems (Windows 10/11); x86 for legacy environments and 32-bit host applications (older Winamp builds, 32-bit RadioDJ, etc.). DSP plugins must match the bitness of the host application, so shipping both ensures broadest compatibility.
.\vcpkg install opus:x86-windows libopusenc:x86-windows mp3lame:x86-windows libyaml:x86-windows libflac:x86-windows libogg:x86-windows libvorbis:x86-windows fdk-aac[he-aac]:x86-windows libxml2:x86-windows curl:x86-windows
Each project currently has Win32|Debug and Win32|Release. Add x64|Debug
and x64|Release to each. Key changes per x64 config group:
<Platform>Win32</Platform> → <Platform>x64</Platform>MachineX86 → MachineX64x64-windows\ for x64 configs, x86-windows\ for Win32 configsx64\Release\ and Win32\Release\ are distinct<PropertyGroup> <VcpkgTriplet Condition="'$(Platform)'=='Win32'">x86-windows</VcpkgTriplet> <VcpkgTriplet Condition="'$(Platform)'=='x64'">x64-windows</VcpkgTriplet> <VcpkgInclude>$(VCPKG_ROOT)\installed\$(VcpkgTriplet)\include</VcpkgInclude> <VcpkgLib>$(VCPKG_ROOT)\installed\$(VcpkgTriplet)\lib</VcpkgLib> </PropertyGroup>
dist/
Win32/
Mcaster1DSPEncoder.exe
dsp_mcaster1.dll dsp_mcaster1_radiodj.dll foo_mcaster1.dll
libmp3lame.dll opus.dll opusenc.dll fdk-aac.dll ...
x64/
Mcaster1DSPEncoder.exe
dsp_mcaster1.dll dsp_mcaster1_radiodj.dll foo_mcaster1.dll
libmp3lame.dll opus.dll opusenc.dll fdk-aac.dll ...
| Target | x86 Build | x64 Build |
|---|---|---|
Mcaster1DSPEncoder.exe | Win7+ (32-bit) | Win10+ (64-bit) |
dsp_mcaster1.dll (Winamp) | Winamp 5.x 32-bit | Winamp 5.9+ 64-bit |
foo_mcaster1.dll (foobar2000) | foobar2000 32-bit | foobar2000 64-bit |
dsp_mcaster1_radiodj.dll | RadioDJ (32-bit) | RadioDJ 64-bit (future) |
build_all.ps1: build both Win32 and x64 for all targets, copy runtime DLLs from correct vcpkg triplet per platform, populate dist/Win32/ and dist/x64/build.ps1 and build_winamp.ps1 (consolidated into build_all.ps1).gitignore: Release/, Debug/, x64/, Win32/, dist/, *.user, *.suo, *.aps, *.pch, *.obj, *.ilk, ipch/, .vs/, *.log, *.tlogMcaster1DSPEncoder.exe launches and encodes — both x86 and x64 buildsdsp_mcaster1.dll loads in Winamp — both x86 and x64 buildsfoo_mcaster1.dll loads in foobar2000 — both x86 and x64 buildsdsp_mcaster1_radiodj.dll loads in RadioDJ — both x86 and x64 buildslibmp3lame.dll working, BladeMP3 removeddist/Win32/ and dist/x64/ populated with correct runtime DLLsbuild_all.ps1 builds both platforms in one passREADME.md updated.gitignore in placev0.2.0All MFC dialogs were modernized with resizable layout and professional flat styling
using ResizableLib (Paolo Messina, Artistic License) from
external/ResizableLib/. This was completed as part of Phase 2 to establish
a consistent UI foundation before audio codec work.
| Dialog | Class | Change |
|---|---|---|
| Main window | CMainWindow | CDialog → CResizableDialog, AddAnchor for all controls, encoder list anchors to BOTTOM_RIGHT |
| Config shell | CConfig | CResizableDialog, OnSize() to resize embedded tab page child dialogs via MoveWindow() |
| Basic Settings tab | CBasicSettings | CResizableDialog, server/encoder fields anchor TOP_LEFT→TOP_RIGHT |
| YP Settings tab | CYPSettings | CResizableDialog, stream metadata fields anchor TOP_LEFT→TOP_RIGHT |
| Advanced Settings tab | CAdvancedSettings | CResizableDialog, log/archive path fields anchor TOP_LEFT→TOP_RIGHT |
| Edit Metadata | CEditMetadata | CResizableDialog, text fields expand, OK/Cancel anchor BOTTOM |
| Find Window | CFindWindowDlg | CResizableDialog, list anchors BOTTOM_RIGHT |
mcaster1dspencoder.rc)DS_MODALFRAME → WS_THICKFRAME on all resizable dialogs (enables drag-resize)9, "Segoe UI", 400, 0, 0x1 throughout (was 8, "MS Sans Serif")DS_3DLOOK removed for flat look; WS_EX_COMPOSITED NOT used (RC compiler does not support it)DS_MODALFRAME by design (fixed-size, no resize needed)ResizableLib sources compiled into all 4 build targets (standalone EXE + 3 DLL plugins),
since all plugin projects compile the full MFC dialog source set. Each target's vcxproj has
8 ResizableLib ClCompile entries with:
<PrecompiledHeader>NotUsing</PrecompiledHeader> <AdditionalOptions>/wd4005 %(AdditionalOptions)</AdditionalOptions> <AdditionalIncludeDirectories>../external/ResizableLib;...</AdditionalIncludeDirectories>
NotUsing allows ResizableLib to find its own StdAfx.h (which defines
WINVER=0x0501) via directory-first include resolution.
/wd4005 suppresses the WINVER redefinition warning against the project-level 0x0601.
MainWindow.h which declares CMainWindow : public CResizableDialog.
ResizableDialog.h was only included via the main project's PCH, not available in plugin builds.
Fix: #include "ResizableDialog.h" added directly inside MainWindow.h
(making it self-contained regardless of which project includes it).
Goal: Remove WMA (end-of-life). Add dual MP3 paths (LAME built-in + ACM), Opus, and full HE-AAC (AAC-LC / HE-AACv1 / HE-AACv2) via fdk-aac. Replace bass.dll capture with statically-linked PortAudio (WASAPI / ASIO / MME / DS / WDM-KS). Achieve format parity with RadioBOSS: MP3 (×2), AAC-LC, AAC+, AAC++, Ogg Vorbis, Opus, FLAC.
| Package | Version | Purpose | Status |
|---|---|---|---|
opus:x64-windows | 1.5.2 | Core Opus codec | Installed |
libopusenc:x64-windows | 0.3 | High-level Opus encoding API | Installed |
fdk-aac:x64-windows[he-aac] | 2.0.3 | AAC-LC / HE-AAC / HE-AAC v2 encoding | Install Required |
libflac:x64-windows | 1.5.0 | Updated FLAC | Installed |
libvorbis:x64-windows | 1.3.7 | Updated Vorbis | Installed |
cd C:\vcpkg && .\vcpkg install fdk-aac[he-aac]:x64-windows[he-aac] feature enables HE-AAC (AAC+) and HE-AAC v2 (AAC++) alongside standard AAC-LC.
Microsoft deprecated Windows Media Services. basswma.dll is a proprietary Un4seen plugin and is EOL.
basswma.dll/.lib from external/lib/basswma.h from external/include/libmcaster1dspencoder.cppbasswma.lib from all vcxproj linker inputsvcpkg provides opus + libopusenc ready to link. Integration via libopusenc high-level API:
#include <opusenc.h>
OggOpusComments *comments = ope_comments_create();
ope_comments_add(comments, "ENCODER", "mcaster1dspencoder");
OggOpusEnc *enc = ope_encoder_create_callbacks(
&callbacks, userdata, comments, 48000, channels, 0, &error);
ope_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate * 1000));
ope_encoder_ctl(enc, OPUS_SET_COMPLEXITY(10));
ope_encoder_write(enc, pcm, samples_per_channel);
ope_encoder_drain(enc);
ope_encoder_destroy(enc);
ope_comments_destroy(comments);
MIME: audio/ogg; codecs=opus. Mount point extension: .opus.
Replace the legacy libfaac dependency with fdk-aac 2.0.3, which supports
AAC-LC, HE-AAC (AAC+), and HE-AAC v2 (AAC++/eAAC+). This achieves full RadioBOSS format parity.
| Format | Profile | Typical Bitrate | Best For |
|---|---|---|---|
| AAC-LC | Low Complexity | 96–256 kbps | High quality, broad compatibility |
| AAC+ (HE-AAC v1) | High Efficiency | 32–96 kbps | Low-bitrate streaming |
| AAC++ (HE-AAC v2) | High Efficiency v2 | 16–48 kbps | Very low bitrate (SBR + PS) |
#include <fdk-aac/aacenc_lib.h> HANDLE_AACENCODER hEncoder; aacEncOpen(&hEncoder, 0, channels); // Profile selection: // AOT_AAC_LC (2) = standard AAC-LC // AOT_SBR (5) = HE-AAC v1 (AAC+) — adds Spectral Band Replication // AOT_PS (29) = HE-AAC v2 (AAC++) — adds Parametric Stereo on top of SBR aacEncoder_SetParam(hEncoder, AACENC_AOT, AOT_SBR); // AAC+ example aacEncoder_SetParam(hEncoder, AACENC_SAMPLERATE, 44100); aacEncoder_SetParam(hEncoder, AACENC_CHANNELMODE, MODE_2); aacEncoder_SetParam(hEncoder, AACENC_BITRATE, 64000); aacEncoder_SetParam(hEncoder, AACENC_TRANSMUX, TT_MP4_ADTS); // ADTS for live streaming aacEncEncode(hEncoder, &inBufDesc, &outBufDesc, &inArgs, &outArgs); aacEncClose(&hEncoder);
| Profile | Recommended Bitrates |
|---|---|
| AAC-LC | 96, 128, 192, 256 kbps |
| AAC+ (HE-AACv1) | 32, 40, 48, 64 kbps |
| AAC++ (HE-AACv2) | 16, 24, 32 kbps |
bass.lib from Winamp / foobar2000 / RadioDJ linker deps (plugins intercept audio via host; no device capture needed)Mcaster1DSPEncoder.exe device capture path| Format | Library | Status After Phase 3 |
|---|---|---|
| MP3 | LAME (Phase 2) | Done in Phase 2 |
| AAC-LC | fdk-aac | New in Phase 3 |
| AAC+ (HE-AACv1) | fdk-aac [he-aac] | New in Phase 3 |
| AAC++ (HE-AACv2) | fdk-aac [he-aac] | New in Phase 3 |
| Ogg Vorbis | libvorbis | Existing (updated) |
| Opus | libopusenc | New in Phase 3 |
| FLAC | libFLAC | Existing (updated) |
| WMA | basswma.dll | Removed (EOL) |
basswma.dll removed, WMA code paths deletedfdk-aac:x64-windows[he-aac] installed via vcpkg and linkedlibfaac removed (replaced by fdk-aac)bass.lib removed from plugin linker depsMcaster1DSPEncoder.exelibmp3lame.dll, opus.dll, opusenc.dll, fdk-aac.dllGoal: Replace legacy flat key=value config with structured YAML supporting
multiple simultaneous stations, per-station encoder chains, ICY/YP metadata, and save-to-WAV.
libyaml:x64-windows 0.2.5 is already installed in vcpkg — ready to integrate after Phase 2 x64 migration.
altacastGlobals *g[MAX_ENCODERS] (max 10 slots, MainWindow.h:43).
On startup each active slot is assigned encoderNumber = i + 1 (1-indexed). Config filenames
are constructed in libaltacast.cpp as:sprintf(configFile, "%s_%d.cfg", g->gConfigFileName, g->encoderNumber);// → mcaster1dspencoder_1.cfg, mcaster1dspencoder_2.cfg, ..._1.cfg and _2.cfg)
each pointing at the same server with different format settings.
_1.cfg vs _2.cfg carries no semantic meaning; figuring out which slot goes where requires reading every fileMAX_ENCODERS 10) — increasing requires a recompileFile: mcaster1dspencoder.yaml
global:
log_level: info
log_file: mcaster1dspencoder.log
reconnect_delay: 5
reconnect_on_drop: true
stations:
- id: 1
name: "My Rock Station"
enabled: true
icy:
name: "My Rock Station"
genre: "Rock"
url: "https://mystation.example.com"
description: "The best rock on the internet"
pub: true
server:
type: icecast2
host: stream.example.com
port: 8000
password: "hackme"
mount: /rock
encoders:
- format: mp3
bitrate: 128
samplerate: 44100
channels: 2
quality: 5
vbr: false
- format: ogg
bitrate: 96
samplerate: 44100
channels: 2
quality: 0.6
- format: opus
bitrate: 128
samplerate: 48000
channels: 2
complexity: 10
signal: audio
- format: aac
profile: he-aacv1 # lc | he-aacv1 | he-aacv2
bitrate: 64
samplerate: 44100
channels: 2
archive:
enabled: false
path: "./recordings/"
filename_pattern: "{station_id}_{date}_{time}.wav"
max_size_mb: 2048
- id: 2
name: "My Jazz Stream"
enabled: false
icy:
name: "Smooth Jazz 24/7"
genre: "Jazz"
url: "https://jazz.example.com"
pub: false
server:
type: shoutcast2
host: shout2.example.com
port: 8002
password: "jazzpass"
mount: /jazz
encoders:
- format: mp3
bitrate: 192
samplerate: 44100
channels: 2
quality: 2
vbr: true
vbr_min: 96
vbr_max: 320
archive:
enabled: true
path: "D:/archive/jazz/"
filename_pattern: "jazz_{date}.wav"
max_size_mb: 4096
New module: libmcaster1dspencoder/config_yaml.cpp + config_yaml.h
int mcaster1_load_yaml_config(const char *path, mcaster1_config_t *cfg); int mcaster1_get_station_count(const mcaster1_config_t *cfg); int mcaster1_get_station(const mcaster1_config_t *cfg, int idx, mcaster1_station_t *st); int mcaster1_save_yaml_config(const char *path, const mcaster1_config_t *cfg);
Legacy key=value reader retained as fallback. Format auto-detected by file extension.
MAX_ENCODERS increased from 10 → 32. Each YAML station encoder entry maps to one
mcaster1Globals slot. Audio input is fanned out to all active encoder threads from
a single capture/input callback using the existing pthreadVSE threading model.
libyaml linked from vcpkg (x86-windows + x64-windows, static)config_yaml.cpp / config_yaml.h module in libmcaster1dspencodermcaster1dspencoder.yaml sample config with 2 stationsMAX_ENCODERS increased 10 → 32Mcaster1DSPEncoder.exeGoal: Transform the encoder into a first-class podcast platform. When a DJ saves a live stream to a local file it becomes an archived podcast episode. Phase 5 adds a dedicated Podcast Settings UI tab, automatic RSS 2.0 + iTunes feed generation alongside each saved recording, and full ICY 2.2 extended header injection on every Icecast2 / Mcaster1DNAS connect. Config save hardening ensures settings are never lost on exit.
The existing “Advanced Settings” config tab has been redesigned as a proper Podcast Settings tab (IDD_PROPPAGE_LARGE2). Log settings are now in their own group box at the bottom. New controls:
IDC_GENERATE_RSS)IDC_RSS_USE_YP)IDC_BROWSE_ARCHIVE)Field resolution order (first non-empty wins): Podcast Settings → YP Settings → safe defaults.
New files src/podcast_rss_gen.h and src/podcast_rss_gen.cpp implement a pure-C RSS writer with zero extra dependencies (pure fprintf). Triggered automatically from closeArchiveFile() when Generate RSS is enabled.
| Feature | Detail |
|---|---|
| Output file | Same directory + base name as audio file, .rss extension |
| Format | RSS 2.0 with xmlns:itunes + xmlns:content namespaces |
| MIME detection | .mp3 → audio/mpeg | .ogg/.opus → audio/ogg | .aac/.m4a → audio/aac | .wav → audio/wav |
| Duration | WAV: estimated from bytes ÷ (samplerate × channels × 2); encoded: omitted |
| iTunes tags | <itunes:author>, <itunes:category>, <itunes:image>, <itunes:duration>, <itunes:explicit> |
| XML safety | All text fields XML-escaped (& < > " ') |
A new 4th config tab “ICY 2.2 Extended” (IDD_PROPPAGE_LARGE3) exposes all 12 ICY 2.2 header categories from the ICY 2.2 spec. On every Icecast2 SOURCE connect, appendICY22Headers() appends all non-empty fields after the standard ICE headers.
| Category | Key Fields |
|---|---|
| Station Identity | station-id, station-logo, verify-status |
| Show / Programming | show-title, show-start/end, next-show, schedule-url, autodj, playlist-name |
| DJ / Host | dj-handle, dj-bio, dj-genre, dj-rating |
| Social & Discovery | creator-handle, Twitter, Twitch, Instagram, TikTok, YouTube, Facebook, LinkedIn, Linktree, emoji, hashtags |
| Listener Engagement | request-url, chat-url, tip-url, events-url |
| Broadcast Distribution | crosspost platforms, session-id (UUID auto-gen), cdn-region, relay-origin |
| Compliance / Content | nsfw, ai-generator, geo-region, license-type, royalty-free, license-territory |
| Station Notice | notice-text, notice-url, notice-expires |
| Video / Simulcast | video-type, video-link, video-title, video-poster, platform, video-live, codec, fps, resolution, rating |
| Audio Technical | audio-codec (auto), samplerate (auto), channels (auto), encoder: Mcaster1DSPEncoder/1.0, loudness (LUFS) |
The session-id is auto-generated as a UUID via CoCreateGuid() on the first connect if the field is blank. Shoutcast servers silently ignore the unknown headers — safe to send always.
writeMainConfig() called before exit(1) to flush config on every close pathPa_Terminate() added to pay the PortAudio shutdown debtwriteMainConfig() added so NumEncoders + window state persists whenever the encoder OK button is clicked, not only on app exitIDD_PROPPAGE_LARGE2) — two group boxes, Browse button, RSS checkboxes, 8 podcast fieldsIDD_PROPPAGE_LARGE3) — ~55 fields across 10 header categoriespodcast_rss_gen.cpp/.h — RSS 2.0 + iTunes NS feed generator (pure C)ICY22Settings.cpp/.h — full MFC dialog for 4th config tab, UUID auto-genlibmcaster1dspencoder.h — 11 podcast + ~55 ICY 2.2 fields added to mcaster1Globalslibmcaster1dspencoder.cpp — path capture in openArchiveFile(), RSS call in closeArchiveFile(), appendICY22Headers(), Icecast2 integrationconfig_yaml.cpp — 75+ new YAML keys for podcast + ICY 2.2 round-trip persistConfig.cpp/.h — 4th tab wired; GlobalsToDialog/DialogToGlobals extendedMainWindow.cpp — 3 config save hardening fixesGoal: First-class integration between Mcaster1DSPEncoder and the Mcaster1DNAS streaming server. They are already ecosystem companions — this phase makes them work together seamlessly as a matched pair, better together than either is with generic third-party tools.
| Feature | Description |
|---|---|
| Auto-discovery | Encoder detects local Mcaster1DNAS instances on the LAN and offers them in a server picker (mDNS / simple UDP broadcast). No manual IP/port entry for local setups. |
| Shared YAML config | Encoder and DNAS share a common mcaster1.yaml with station definitions, eliminating duplicate configuration between the two applications. |
| Live stream health | DNAS reports listener count, bitrate stability, and buffer state back to the encoder UI in real time. Red/amber/green status indicator per encoder slot. |
| Song history push | Encoder pushes structured now-playing data directly to the DNAS ring buffer API on each track change — no polling delay, no metadata lag. |
| Admin API auth | Encoder uses the same API key as the DNAS admin panel. Single credential, single configuration point. |
| Relay/fallback coordination | Encoder can trigger a DNAS relay switch if the live source drops, and signal DNAS to resume live source when the connection recovers. |
Goal: Resurrect and modernize Castit — the original live broadcast scheduling and automation tool built for casterclub.com in 2001 with Ed Zaleski's guidance. Castit was written against VC6 and will be upgraded to Visual Studio 2022 as a first-class member of the Mcaster1 ecosystem.
| Component | Old (VC6) | New (VS2022) |
|---|---|---|
| Compiler / Toolset | VC6 (v60 toolset) | VS2022 (v143 toolset) |
| Windows SDK | Windows 98/ME/2000 era | Windows 10 SDK (10.0.x) |
| XML processing | Bundled libxml2 (VC6 era) | libxml2:x86-windows via vcpkg |
| HTTP client | Custom / early libcurl build | libcurl:x86-windows via vcpkg (TLS 1.3, HTTP/2) |
| Database | MySQL direct client (VC6) | libmariadb via vcpkg (preferred) or libmysql — to be confirmed |
| UI Framework | MFC VC6 dialogs (fixed-size) | MFC VS2022 + ResizableLib (matching Mcaster1DSPEncoder) |
| Configuration | Legacy flat config | YAML via libyaml (matching Mcaster1DSPEncoder) |
| Encoding integration | Castit-internal encoder | Delegates to Mcaster1DSPEncoder engine via shared lib |
| Metadata | Legacy ICY text | ICY 2.2 extended headers — podcast, social, DJ, compliance, video (Phase 5) |
libmariadb is preferred over the Oracle MySQL client. MariaDB is a true open-source,
community-maintained drop-in replacement for MySQL with the same wire protocol, no Oracle license
constraints, and active development. This will be confirmed before implementation begins.
vcpkg package: libmariadb:x86-windows.
libxml2 integrated via vcpkg replacing bundled VC6 buildlibcurl integrated via vcpkg with TLS 1.3 supportlibmariadb (or libmysql) integrated via vcpkgGoal: Leverage the ICY 2.1 protocol and the Mcaster1DNAS song history ring buffer to build a statistical metrics and analytics layer that gives station operators actionable listener engagement data — without requiring third-party analytics services or sharing data with external platforms.
[Listeners] ──ICY 2.1 engagement signals──► [Mcaster1DNAS]
│
[Analytics Ring Buffer]
│
┌────────────────────────┼──────────────────────────┐
▼ ▼ ▼
[Track Play Stats] [Listener Peaks] [Format Stats]
Most played, rotation Hour-by-hour curve Codec popularity
│
[Mcaster1Castit Scheduler]
│
[Optimize playlist rotation
based on engagement data]
| Metric | Source | Use Case |
|---|---|---|
| Track play count | DNAS ring buffer | Most-played tracks, rotation analysis, eliminate overplayed songs |
| Listener count at track change | DNAS stats API | Measure which tracks retain vs lose listeners |
| Format & bitrate popularity | DNAS mount stats | Which codec/bitrate combinations listeners actually use |
| Peak listening hours | DNAS listener timeseries | Schedule high-value content at real peak times |
| Tune-in / tune-out moments | ICY 2.1 engagement signal | Identify tracks and times that cause listeners to disconnect |
| Music service click-through | Track History page analytics | Which tracks generate Spotify/Apple Music lookups from listeners |
| Geographic distribution | DNAS connection metadata | Where your listeners are — optimize relay server placement |
The ICY 2.1 metadata block includes a history_url field pointing to the DNAS track history API.
Listener-side players that support ICY 2.1 can display:
This creates a direct engagement path from the audio stream to music service platforms,
measurable through history_url click analytics on the DNAS side — without
requiring a third-party ad network or analytics vendor.
Phase 8 closes the loop: analytics data collected by Mcaster1DNAS feeds back into Mcaster1Castit's scheduling engine. High-engagement tracks get scheduled more frequently at the hours when their listener segments are most active. Tracks that consistently cause tune-outs get flagged for rotation review. All processing happens on-premises — no data leaves your infrastructure.
| Dependency | Purpose | License | Distribute | Phase | vcpkg |
|---|---|---|---|---|---|
libmp3lame.dll | MP3 encoding | LGPL v2 | Yes (patents expired 2017) | 2 | mp3lame:x64 |
vorbis.dll / vorbisenc.dll | Ogg Vorbis | BSD | Yes | existing | libvorbis:x64 |
ogg.dll | Ogg container | BSD | Yes | existing | libogg:x64 |
libFLAC.dll | FLAC encoding | BSD | Yes | existing | libflac:x64 |
fdk-aac.dll | AAC-LC / AAC+ / AAC++ encoding | BSD-2 (HE-AAC patent license) | Yes | 3 | fdk-aac:x64[he-aac] — install required |
libcurl.dll | HTTP / streaming | MIT/curl | Yes | existing | curl:x64 |
pthreadVSE.dll | Threading | LGPL | Yes | existing | pthreads:x64 |
opus.dll | Opus core codec | BSD | Yes | 3 | opus:x64 ✓ |
opusenc.dll | Opus encoding API | BSD | Yes | 3 | libopusenc:x64 ✓ |
| PortAudio 19.7.0 | Device capture (WASAPI/ASIO/MME/DS/WDM-KS) | MIT | Static link — baked into EXE only, no DLL | 3 ✓ | Built from source with Steinberg ASIO SDK |
libyaml | YAML config parser | MIT | Yes (static) | 4 | libyaml:x86-windows ✓ |
| ResizableLib | Resizable MFC dialog framework | Artistic | Source compiled in | 2 ✓ | external/ResizableLib/ |
bass.dll | Audio capture (legacy) | Proprietary | — | REMOVED Phase 3 | — |
basswma.dll | WMA encoding (legacy) | Proprietary | — | REMOVED Phase 3 | — |
libmariadb | MariaDB/MySQL client (Castit) | LGPL | Yes | 7 | libmariadb:x86-windows |
libxml2.dll | XML / YP | MIT | Yes | existing | libxml2:x64 |
iconv.dll | Encoding conv. | LGPL | Yes | existing | libiconv:x64 |
git init git remote add origin https://github.com/davestj/Mcaster1DSPEncoder.git git add . git commit -m "feat: Phase 5 — Podcast RSS, ICY 2.2 headers, Podcast + ICY 2.2 UI tabs" git tag v0.2.0 git push -u origin main
| Tag | Phase | Status | Description |
|---|---|---|---|
v0.1.0 | Phase 1 | TAGGED | VS2022 build fixes — all 4 targets compile and run |
v0.2.0 | Phase 2 | TAGGED | Full rebrand, ResizableLib UI, Segoe UI, native LAME, all 4 targets clean |
v0.3.0 | Phase 3 | COMPLETE | MP3/LAME, Opus, HE-AAC/fdk-aac, OggVorbis, FLAC. PortAudio WASAPI. Volume slider fixed. |
v0.4.0 | Phase 4 | COMPLETE | YAML-first config across all 4 targets, per-encoder YAML files, legacy .cfg migration |
v0.5.0 | Phase 5 | IN PROGRESS | Podcast RSS feed gen, ICY 2.2 extended headers (all 12 categories), Podcast + ICY 2.2 UI tabs, config save hardening |
v0.6.0 | Phase 6 | PLANNED | Mcaster1DNAS deep integration — auto-discovery, shared YAML, live health, song push |
v1.0.0 | Phase 7 | PLANNED | Mcaster1Castit — Castit VC6 → VS2022, full Mcaster1 platform launch |
v1.1.0 | Phase 8 | PLANNED | Analytics, metrics & platform engagement via ICY 2.1 signals |
| Phase | Name | Status | Key Outputs |
|---|---|---|---|
| 1 | VS2022 Build Fix | COMPLETE | All 4 targets compile and run |
| 2 | Rebrand & Refactor | COMPLETE | Full rename, ResizableLib UI, Segoe UI, native LAME, all 4 targets clean |
| 3 | Audio Modernization | IN PROGRESS | Dual MP3, Opus, HE-AAC, WMA removed, PortAudio — pending E2E verification |
| 4 | YAML Multi-Station | PLANNED | YAML config, multi-station, per-station encoders, archive |
| 5 | ICY 2.1 Enhanced Metadata | PLANNED | JSON blocks, album art, track IDs, DNAS history API linkage |
| 6 | Mcaster1DNAS Integration | PLANNED | Auto-discovery, shared config, live health feedback, song history push |
| 7 | Mcaster1Castit | PLANNED | Castit VC6 → VS2022, libxml2/libcurl/libmariadb via vcpkg, broadcast scheduler |
| 8 | Analytics & Engagement | PLANNED | Listener metrics, tune signals, platform engagement via ICY 2.1 |