Skip to content

Commit

Permalink
Add: WAV file music playback with the Win32 music driver
Browse files Browse the repository at this point in the history
  • Loading branch information
nielsmh committed Nov 4, 2018
1 parent 6e7768e commit 3a3e080
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/base_media_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,7 @@ byte *GetMusicCatEntryData(const char *filename, size_t entrynum, size_t &entryl
enum MusicTrackType {
MTT_STANDARDMIDI, ///< Standard MIDI file
MTT_MPSMIDI, ///< MPS GM driver MIDI format (contained in a CAT file)
MTT_WAVE, ///< WAV file
};

/** Metadata about a music track. */
Expand Down
14 changes: 10 additions & 4 deletions src/music.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#define SET_TYPE "music"
#include "base_media_func.h"

#include "music/midifile.hpp"
#include "safeguards.h"
#include "fios.h"

Expand Down Expand Up @@ -128,9 +129,10 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
IniGroup *timingtrim = ini->GetGroup("timingtrim");
uint tracknr = 1;
for (uint i = 0; i < lengthof(this->songinfo); i++) {
this->songinfo[i].songname[0] = '\0';

const char *filename = this->files[i].filename;
if (names == NULL || StrEmpty(filename) || this->files[i].check_result == MD5File::CR_NO_FILE) {
this->songinfo[i].songname[0] = '\0';
continue;
}

Expand All @@ -144,13 +146,17 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
char *songname = GetMusicCatEntryName(filename, this->songinfo[i].cat_index);
if (songname == NULL) {
DEBUG(grf, 0, "Base music set song missing from CAT file: %s/%d", filename, this->songinfo[i].cat_index);
this->songinfo[i].songname[0] = '\0';
continue;
}
strecpy(this->songinfo[i].songname, songname, lastof(this->songinfo[i].songname));
free(songname);
} else {
this->songinfo[i].filetype = MTT_STANDARDMIDI;
SMFHeader smfheader;
if (MidiFile::ReadSMFHeader(filename, smfheader)) {
this->songinfo[i].filetype = MTT_STANDARDMIDI;
} else {
this->songinfo[i].filetype = MTT_WAVE;
}
}

const char *trimmed_filename = filename;
Expand All @@ -166,7 +172,7 @@ bool MusicSet::FillSetDetails(IniFile *ini, const char *path, const char *full_f
if (item != NULL && !StrEmpty(item->value)) break;
}

if (this->songinfo[i].filetype == MTT_STANDARDMIDI) {
if (this->songinfo[i].songname[0] == '\0') {
if (item != NULL && !StrEmpty(item->value)) {
strecpy(this->songinfo[i].songname, item->value, lastof(this->songinfo[i].songname));
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/music/midifile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,8 @@ bool MidiFile::LoadSong(const MusicSongInfo &song)
return false;
}
}
case MTT_WAVE:
return false;
default:
NOT_REACHED();
}
Expand Down
125 changes: 108 additions & 17 deletions src/music/win32_m.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "midifile.hpp"
#include "midi.h"
#include "../base_media_base.h"
#include "../mixer.h"

#include "../safeguards.h"

Expand Down Expand Up @@ -50,6 +51,92 @@ static struct {
byte channel_volumes[16]; ///< last seen volume controller values in raw data
} _midi;

static void WaveStreamProvider(int16 *buffer, size_t samples);
static struct {
FILE *file;
size_t samplecount;
uint32 samplerate;
bool playing;
byte volume;

bool Open(const std::string &filename)
{
file = FioFOpenFile(filename.c_str(), "rb", Subdirectory::BASESET_DIR);
if (!file) return false;

static char TAG_RIFF[4] = { 'R', 'I', 'F', 'F' };
static char TAG_WAVE[4] = { 'W', 'A', 'V', 'E' };
static char TAG_fmt [4] = { 'f', 'm', 't', ' ' };
static char TAG_data[4] = { 'd', 'a', 't', 'a' };

char header[16] = { 0 };
fread(header, 4, 4, file);
if (MemCmpT(header, TAG_RIFF, 4) != 0 || MemCmpT(header+8, TAG_WAVE, 4) != 0 || MemCmpT(header+12, TAG_fmt, 4) != 0) {
return Close(), false;
}

uint32 chunksize;
fread(&chunksize, sizeof(chunksize), 1, file);
chunksize = (chunksize + 1) & ~1;
if (chunksize < 16) return Close(), false;

uint16 compression, channels, bitpersample;
fread(&compression, 2, 1, file);
fread(&channels, 2, 1, file);
fread(&samplerate, 4, 1, file);
fread(header, 6, 1, file); // junk
fread(&bitpersample, 2, 1, file);
if (compression != 1 || channels != 2 || bitpersample != 16) {
/* not acceptable PCM */
return Close(), false;
}
chunksize -= 16;

do {
fseek(file, chunksize, SEEK_CUR);
fread(header, 4, 1, file);
fread(&chunksize, 4, 1, file);
chunksize = (chunksize + 1) & ~1;
} while (!feof(file) && MemCmpT(header, TAG_data, 4) != 0);

// found data chunk
samplecount = chunksize / 4;
// ready to play
if (MxSetMusicSource(WaveStreamProvider) != samplerate) return Close(), false;
playing = true;
return true;
}

void Close()
{
playing = false;
MxSetMusicSource(NULL);
if (file) fclose(file);
file = NULL;
}

} _wave;

static void WaveStreamProvider(int16 *buffer, size_t samples)
{
if (!_wave.file || !_wave.playing) return;
if (_wave.samplecount == 0 || feof(_wave.file)) {
_wave.Close();
return;
}
if (_wave.samplecount < samples) samples = _wave.samplecount;

fread(buffer, 4, samples, _wave.file);
_wave.samplecount -= samples;

for (size_t s = 0; s < samples; s++) {
*buffer = (int16)((int32)*buffer * _wave.volume / 127);
buffer++;
*buffer = (int16)((int32)*buffer * _wave.volume / 127);
buffer++;
}
}

static FMusicDriver_Win32 iFMusicDriver_Win32;


Expand Down Expand Up @@ -315,22 +402,22 @@ void MusicDriver_Win32::PlaySong(const MusicSongInfo &song)
DEBUG(driver, 2, "Win32-MIDI: PlaySong: entry");
EnterCriticalSection(&_midi.lock);

if (!_midi.next_file.LoadSong(song)) {
LeaveCriticalSection(&_midi.lock);
return;
}

_midi.next_segment.start = song.override_start;
_midi.next_segment.end = song.override_end;
_midi.next_segment.loop = song.loop;

DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag");
_midi.do_stop = _midi.playing;
_midi.do_start = true;

if (_midi.timer_id == 0) {
DEBUG(driver, 2, "Win32-MIDI: PlaySong: starting timer");
_midi.timer_id = timeSetEvent(_midi.time_period, _midi.time_period, TimerCallback, (DWORD_PTR)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
if (song.filetype == MTT_WAVE) {
_midi.do_stop = _midi.playing;
_wave.Open(song.filename);
} else if (_midi.next_file.LoadSong(song)) {
_midi.next_segment.start = song.override_start;
_midi.next_segment.end = song.override_end;
_midi.next_segment.loop = song.loop;

DEBUG(driver, 2, "Win32-MIDI: PlaySong: setting flag");
_midi.do_stop = _midi.playing;
_midi.do_start = true;

if (_midi.timer_id == 0) {
DEBUG(driver, 2, "Win32-MIDI: PlaySong: starting timer");
_midi.timer_id = timeSetEvent(_midi.time_period, _midi.time_period, TimerCallback, (DWORD_PTR)this, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
}
}

LeaveCriticalSection(&_midi.lock);
Expand All @@ -342,18 +429,20 @@ void MusicDriver_Win32::StopSong()
EnterCriticalSection(&_midi.lock);
DEBUG(driver, 2, "Win32-MIDI: StopSong: setting flag");
_midi.do_stop = true;
_wave.Close();
LeaveCriticalSection(&_midi.lock);
}

bool MusicDriver_Win32::IsSongPlaying()
{
return _midi.playing || _midi.do_start;
return _wave.playing || _midi.playing || _midi.do_start;
}

void MusicDriver_Win32::SetVolume(byte vol)
{
EnterCriticalSection(&_midi.lock);
_midi.new_volume = vol;
_wave.volume = vol;
LeaveCriticalSection(&_midi.lock);
}

Expand All @@ -362,6 +451,7 @@ const char *MusicDriver_Win32::Start(const char * const *parm)
DEBUG(driver, 2, "Win32-MIDI: Start: initializing");

InitializeCriticalSection(&_midi.lock);
_wave.Close();

int resolution = GetDriverParamInt(parm, "resolution", 5);
int port = GetDriverParamInt(parm, "port", -1);
Expand Down Expand Up @@ -406,6 +496,7 @@ const char *MusicDriver_Win32::Start(const char * const *parm)
void MusicDriver_Win32::Stop()
{
EnterCriticalSection(&_midi.lock);
_wave.Close();

if (_midi.timer_id) {
timeKillEvent(_midi.timer_id);
Expand Down

0 comments on commit 3a3e080

Please sign in to comment.