@@ -85,72 +85,87 @@ void MusicDriver_ExtMidi::StopSong()
this->song.clear();
this->DoStop();
}
bool MusicDriver_ExtMidi::IsSongPlaying()
{
if (this->pid != -1 && waitpid(this->pid, nullptr, WNOHANG) == this->pid) {
this->pid = -1;
if (this->pid == -1 && !this->song.empty()) this->DoPlay();
return this->pid != -1;
void MusicDriver_ExtMidi::SetVolume(byte)
Debug(driver, 1, "extmidi: set volume not implemented");
void MusicDriver_ExtMidi::DoPlay()
this->pid = fork();
switch (this->pid) {
case 0: {
close(0);
int d = open("/dev/null", O_RDONLY);
if (d != -1 && dup2(d, 1) != -1 && dup2(d, 2) != -1) {
/* execvp is nasty as it *allows* the passed parameters to be written
* for backward compatibility, however we are a fork so do not care. */
std::vector<char *> parameters;
for (auto &token : this->command_tokens) parameters.emplace_back(token.data());
parameters.emplace_back(this->song.data());
parameters.emplace_back(nullptr);
execvp(parameters[0], parameters.data());
_exit(1);
case -1:
Debug(driver, 0, "extmidi: couldn't fork: {}", strerror(errno));
FALLTHROUGH;
default:
break;
/**
* Try to end child process with kill/waitpid for up to 1 second.
* @param pid The process ID to end.
* @param signal The signal type to send.
* @return True if the process has been ended.
*/
static bool KillWait(pid_t &pid, int signal)
/* First try to stop for about a second;
* 1 seconds = 1000 milliseconds, 50 ms per cycle => 20 cycles. */
for (int i = 0; i < 20; i++) {
kill(pid, signal);
if (waitpid(pid, nullptr, WNOHANG) == pid) {
/* It has shut down, so we are done */
pid = -1;
return true;
/* Wait 50 milliseconds. */
CSleep(50);
return false;
void MusicDriver_ExtMidi::DoStop()
if (this->pid <= 0) return;
/* First try to gracefully stop for about five seconds;
* 5 seconds = 5000 milliseconds, 10 ms per cycle => 500 cycles. */
for (int i = 0; i < 500; i++) {
kill(this->pid, SIGTERM);
if (waitpid(this->pid, nullptr, WNOHANG) == this->pid) {
return;
/* Wait 10 milliseconds. */
CSleep(10);
if (KillWait(this->pid, SIGINT)) return;
if (KillWait(this->pid, SIGTERM)) return;
Debug(driver, 0, "extmidi: gracefully stopping failed, trying the hard way");
/* Gracefully stopping failed. Do it the hard way
* and wait till the process finally died. */
kill(this->pid, SIGKILL);
waitpid(this->pid, nullptr, 0);
Status change: