Site Admin, Skilled player (1235)
Joined: 4/17/2010
Posts: 11264
Location: RU
https://github.com/TASEmulators/hourglass-win32/releases/tag/r88 https://github.com/vadosnaprimer/midilogra/releases/tag/1
  • When running the game that uses MIDI music in Hourglass, the above build will create midilog.txt in the game directory.
  • Put it near midi.lua and execute the script (just drag'n'drop it into fceux or run via lExecutor). midilogra.txt will be created.
  • Run midilogra.bat. midilogra.mid will be created.
  • Put that file near fluidsynth binaries, along with whatever soundfont you want to use (natt used WeedsGM3.sf2 for Nikujin and Spyman), and run fluidsynth -F outfile.wav soundfont.sf2 midilogra.mid.
    • You can preview the soundfont (and convert to wav) in foobar by installing BASSMIDI, just disable all the looping in MIDI synthesizer host options.
  • Mix the resulting wav file with audio you got from hourglass AVI (time shift may be needed?)
Mirroring the code files here for paranoid backup: Download midi.lua
Language: lua

function logs(s) local logfile = io.open("midilogra.txt", "a+") logfile:write(s) logfile:close() end function dooutput (msg) local timestr = msg[1] local i = 3 while (i <= #msg) do local evtype = msg[i]:match ("^.") --io.write ("##", evtype, "\n") if evtype == "c" or evtype == "d" then logs (timestr..",".."0000"..msg[i + 1]..msg[i].."\n") i = i + 2 else logs (timestr..",".."00"..msg[i + 2]..msg[i + 1]..msg[i].."\n") i = i + 3 end end end for line in io.lines ("midilog.txt") do -- for now, try ignoring special messages if line:find ("sysex") then local msg = {} for token in line:gmatch ("([^,]+)") do table.insert (msg, token) end dooutput (msg) elseif line:find ("finalizing...") then local timestr = line:match ("(%d+)") local channums = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"} for i, v in ipairs (channums) do logs (timestr..",00007bb"..v.."\n") -- all notes off end end end
Download midilogra.c
Language: c

#include <stdio.h> #include <stdlib.h> #include <string.h> int totalbytes = 0; void decode (unsigned time, unsigned event, FILE *f) { static unsigned lasttime = 0; static int laststatus = 0; int status = event & 0xff; int d1, d2; if (status & 0x80) { laststatus = status; d1 = event >> 8 & 0xff; d2 = event >> 16 & 0xff; } else { d1 = status; status = laststatus; d2 = event >> 8 & 0xff; } unsigned delta = time - lasttime; lasttime = time; // write varlen char writer[4] = {0, 0, 0, 0}; int pos = 0; while (delta) { writer[pos] = delta & 127; if (pos) writer[pos] |= 0x80; pos++; delta /= 128; } if (!pos) { writer[0] = 0; fwrite (writer, 1, 1, f); totalbytes++; } else { while (pos > 0) { fwrite (writer + pos - 1, 1, 1, f); totalbytes++; pos--; } } // aren't actually writing out running status switch (status & 0xf0) { case 0x80: // off case 0x90: // on case 0xa0: // aftertouch case 0xb0: // controller case 0xe0: // bend fwrite (&status, 1, 1, f); fwrite (&d1, 1, 1, f); fwrite (&d2, 1, 1, f); totalbytes += 3; break; case 0xc0: // program case 0xd0: // channel aftertouch fwrite (&status, 1, 1, f); fwrite (&d1, 1, 1, f); totalbytes += 2; break; default: fprintf (stderr, "OH MY GOD\n"); exit (0); } return; } int main (void) { FILE *f = fopen ("midilogra.mid", "wb"); char buff[256]; char bigheader[] = {'M', 'T', 'h', 'd', 0, 0, 0, 6, 0, 0, 0, 1, 0, 1}; // midi clocks per quarternote fwrite (bigheader, 1, 14, f); char chunkheader[] = {'M', 'T', 'r', 'k', 0xde, 0xad, 0xbe, 0xef}; fwrite (chunkheader, 1, 8, f); // tempo event (microseconds per quarternote char openevent[] = {0x00, 0xff, 0x51, 0x03, 0x00, 0x03, 0xe8}; fwrite (openevent, 1, 7, f); totalbytes += 6; while (fgets (buff, 256, stdin)) { unsigned time; unsigned ev; if (sscanf (buff, "%u,%x", &time, &ev) != 2) { fprintf (stderr, "NO WAY MAN\n"); exit (0); } decode (time, ev, f); } // end of track event // lets give ~10 seconds // 10000ms = 78, 16 (ce 10) char endevent[] = {0xce, 0x10, 0xff, 0x2f, 0x00}; fwrite (endevent, 1, 5, f); totalbytes += 5; fseek (f, 18, SEEK_SET); char scratch; scratch = totalbytes >> 24; fwrite (&scratch, 1, 1, f); scratch = totalbytes >> 16; fwrite (&scratch, 1, 1, f); scratch = totalbytes >> 8; fwrite (&scratch, 1, 1, f); scratch = totalbytes; fwrite (&scratch, 1, 1, f); fclose (f); return 0; }
^ build by simply running gcc midilogra.c
Warning: When making decisions, I try to collect as much data as possible before actually deciding. I try to abstract away and see the principles behind real world events and people's opinions. I try to generalize them and turn into something clear and reusable. I hate depending on unpredictable and having to make lottery guesses. Any problem can be solved by systems thinking and acting.