public IEnumerable <Song> GetSongs(string filename) { filename = Path.GetFullPath(filename); if (!File.Exists(filename)) { throw new FileNotFoundException("Cannot find VGM file", filename); } string json; using (var p = new ProcessWrapper( _multiDumperPath, $"\"{filename}\" --json")) { json = string.Join("", p.Lines()); } if (string.IsNullOrEmpty(json)) { throw new Exception("Failed to get song data from MultiDumper"); } // Extract metadata // Example result: // { // "channels":[ // "SEGA PSG #0","SEGA PSG #1","SEGA PSG #2","SEGA PSG #3", // "YM2413 #0","YM2413 #1","YM2413 #2","YM2413 #3","YM2413 #4","YM2413 #5","YM2413 #6", // "YM2413 #7","YM2413 #8","YM2413 #9","YM2413 #10","YM2413 #11","YM2413 #12","YM2413 #13"], // "containerinfo": // { // "copyright":"1988/08/14", // "dumper":"sherpa", // "game":"Golvellius - Valley of Doom", // "system":"Sega Master System" // }, // "songs":[ // { // "author":"Masatomo Miyamoto, Takeshi Santo, Shin-kun, Pazu", // "comment":"", // "name":"Title Screen" // }], // "subsongCount":1 // } dynamic metadata = JsonConvert.DeserializeObject(json); var channels = metadata.channels.ToObject <List <string> >(); var songs = (JArray)metadata.songs; var i = 0; // This helps us reject any junk strings MultiDumper gives us for empty tags string Clean(string s) => string.IsNullOrEmpty(s) || s.Any(char.IsControl) ? string.Empty : s; return(songs.Cast <dynamic>().Select(s => new Song { Filename = filename, Index = i++, Name = Clean(s.name), Author = Clean(s.author), Channels = channels, Comment = Clean(s.comment), Copyright = Clean(metadata.containerinfo.copyright), Dumper = Clean(metadata.containerinfo.dumper), Game = Clean(metadata.containerinfo.game), System = Clean(metadata.containerinfo.system) })); }