Restarting Winamp when idle to commit the Media Library changes

Problem

There has been an age old problem with Winamp where the Media Library changes are only saved to disk once the app is closed.

I listen to Winamp a lot, but sometimes I pause the app and forget about it.

Especially when paused (rather than stopped), Winamp is sensitive to sound device changes which can happen when (dis)connecting bluetooth headsets, or connecting to remote computers.

Sometimes this can cause Winamp to hang and crash. If the app was running for a long time, all that playback history and ratings will be lost.

I remember the Winamp forums were talking about this issue some time ago, without a fix, but I can’t seem to find the link right now.

Solution

I wrote the following PowerShell script that monitors Winamp using internal Windows API calls, and restarts it when it was idle (stopped or paused) for a specified amount of time.

Once the app was restarted, it will be left alone until new playback activity is detected and the app is paused/stopped again.

https://github.com/levid0s/winamp-autosavedb

How it works?

I found a neat library on github.com/sergueik that can trigger the WM_COMMAND and WM_USER Windows API functions. Because PowerShell is based on .NET, it allows access to such low level functions, just needs a lot of typing. My script only uses a tiny fraction from sergueik’s library, but I left the code unmodified.

An obscure post from the Winamp forums describes the functions implemented in Winamp using WM_COMMAND and WM_USER:

I’m using the following API calls:

  • [System.Windows.Win32Window]::FromProcessName("winamp"): Get the hWnd identifier of Winamp. Using this we can target the API calls to the right application/instance.
  • $window.SendMessage($WM_USER, 0, 104): Get the playback status (playing, stopped, paused).
  • $window.SendMessage($WM_USER, 0, 105): Get the seek position so we can skip to the same position after restarting.
  • $window.SendMessage($WM_USER, 0, 135): Tell Winamp to gracefully restart itself.
  • $window.SendMessage($WM_COMMAND, 40045, 0): Simulate pressing Play if the state was Paused before the restart (so we can seek the playback position).
  • $window.SendMessage($WM_USER, $SeekPosMS, 106): Seek to the previous cursor position.
  • $window.SendMessage($WM_COMMAND, 40046, 0): Simulate pressing Pause as the previous state was also Paused.

Running as a Scheduled Task

The script supports the -Install [CurrentUser|AllUsers] parameter. This creates a Windows Scheded Task that runs perpetually, and restarts Winamp when it has been idle. I’ve been using the script for a few days and fixed most of the bugs.

There’s a bug in the Windows Scheduled Task PowerShell library, where it’s basically impossible to create a job that runs a PowerShell script hidden, so I had to use a .vbs wrapper.

Caveats

Haven’t tested this in a multi-user environment, where Winamp is used by multiple users, or where multiple instances of Winamp.exe are running. This would probably result in unexpected behavior because there’s no logic in the script to deal with multiple copies of the process.

See the script Readme (<# .. #>) for more details.

Happy listening!

Updated: