User:Mjb/Winamp & SHOUTcast

From Offset
Jump to navigationJump to search

Winamp playlist regeneration on Windows

Every day, I want to:

  1. Regenerate a UTF-8-encoded playlist, picking up any changes made on my file system
  2. Load the new playlist in Winamp

I used to have a clever Python script for this purpose, but installing Python on Windows is an awfully steep prequisite. As it turns out, a single Windows command-shell command does the trick. This all goes on one line:

cmd.exe /C "chcp 65001 && dir Z:\music /b /s | findstr /e /l ".mp3 .flac .m4a .wav .wma .ogg .mp2"
| findstr /e /l /v ".lnk" > playlist.m3u8" && start /b playlist.m3u8

Here's an explanation:

  • && = if the previous command completed without error, do what comes next.
  • | = pipe the text output of the previous command as input to the next.
  • > … = put the output of the previous command into the designated file.
  • cmd.exe /C "" = open a command shell window and run the quoted command(s).
  • chcp 65001 = set the active code page to UTF-8
  • dir /b /s = recursively list the files in the given folder(s), only listing their full paths.
  • findstr /e /l "" = output only lines ending with any of these literal strings.
  • findstr /e /l /v "" = output lines not ending with any of these literal strings.
  • start /b … = open the designated file as a background process (no window).

To get it to run every day, you need to add it to the Task Scheduler, where you also tell it what directory to run from (i.e. where you want the playlist file to go, if you didn't specify a path in the command line). You access the Task Scheduler GUI through the Management Console or with a command-line command (schtasks), the syntax of which is too painful to deal with. Just use the GUI.

SHOUTcast stream transcoding

In 2008, I was looking into offering a low-bitrate MP3 version of an audio stream by connecting to it as a client, transcoding it, and feeding it to another server.

I didn't get very far in my research, but did find several options for grabbing a stream to a file on Unix:

mpg123 -b 4096 -s streamURL > out.mp3
gnetcat -l -p 8081 streamIP streamPort | lame --mp3input -b 64 - out.mp3
mpg123 -b 4096 -s streamURL | lame --mp3input -b 64 - out.mp3

And then there was also fIcy: an icecast/shoutcast stream grabber suite.

I don't remember if I tried any of these. The idea, though, was that if I could send the data to a file, the file could actually be a named pipe into icecast or something. However it didn't occur to me that I'd probably lose the protocol data (stream info and song titles). So it was starting to look like the only real option was going to be streamTranscoder, last updated in 2004.


In December 2015, I began using the Liquidsoap command-line scripting language to generate crossfaded AAC streams for my SHOUTcast and Icecast servers. It works a little better with Icecast.

I installed the standard Windows build, modified the test.liq script as shown below (but with working hosts, ports, and passwords), and ran "liquidsoap mytest.liq" on the command line.

If I recall correctly, there were a number of problems:

  • Windows builds were no longer supported.
  • No title info was streamed for files without artist & title metadata.
  • Non-ASCII characters in filenames weren't supported.
  • Non-ASCII characters in metadata were problematic too.

As of 2019, it seems they're now doing Windows builds regularly again, so I'm giving it another shot.

There was one issue (no Windows support for FLAC inputs) which looked like a dealbreaker, so I filed a feature request on GitHub, and it turned out to just be a build error which they remedied immediately.

So far it is working OK with this configuration:

# configure logging

# allow sending commands to server while it's running
#   sample commands: playlist(dot)m3u8.reload
set("server.telnet.port", 2425)

# create a source from a playlist
s = playlist(mime_type="application/x-mpegURL","/docs/private/Desktop/playlist.m3u8")

# make the source generate stereo from mono sources (otherwise they fail)
s = audio_to_stereo(s)

# if no artist and title, use filename
def fix_song(m) =
	filename = string.extract(pattern="^(.+)\.",basename(m["filename"]))
	filename = filename["1"]
	[("title",'$(if $(y),"$(y)","$(f)")' % [("y",m["title"]),("f",filename)])]
s = map_metadata(fix_song, s)

# make the source go to next track when raw volume dips below -78 dBFS for more than 2s
s = skip_blank(threshold=-78.,max_blank=2.,s)

# make the source adjust output volume based on ReplayGain Track Gain tag
s = amplify(1.,override="REPLAYGAIN_TRACK_GAIN",s)

# simple crossfade is easier to use than smart_crossfade, but all tracks fade the same way
#  start_next = crossfade duration, i.e. how far before end of current track to start playing next track
#  fade_in = fade-in duration of next track
#  fade_out = fade-out duration of current track
s = crossfade(start_next=15.,fade_in=10.,fade_out=15.,s)

# Output to the local soundcard,s)

# Output raw 128 kbps AAC-LC to a file
#output.file(fallible=true, %fdkaac(channels=2, samplerate=44100, bitrate=128, afterburner=false, aot="mpeg2_aac_lc", transmux="adts", sbr_mode=false), "out.aac", s)

# Output 128 kbps AAC-LC to skew's Icecast server
#output.icecast(fallible=true, %fdkaac(channels=2, samplerate=44100, bitrate=128, afterburner=false, aot="mpeg2_aac_lc", transmux="adts", sbr_mode=false), host="REPLACE_ME", port=REPLACE_ME, password="REPLACE_ME", mount="/", encoding="UTF-8", description="REPLACE_ME", url="REPLACE_ME", public=false, s)

# Output 256 kbps AAC-LC to a local SHOUTcast server
output.icecast(fallible=true, %fdkaac(channels=2, samplerate=44100, bitrate=256, afterburner=false, aot="mpeg2_aac_lc", transmux="adts", sbr_mode=false), protocol="icy", icy_metadata="true", host="localhost", port=REPLACE_ME, password="REPLACE_ME", mount="stream", encoding="UTF-8", description="REPLACE_ME", url="REPLACE_ME", public=false, s)