{"id":16985675,"url":"https://github.com/sinshu/meltysynth","last_synced_at":"2025-05-16T01:07:24.887Z","repository":{"id":39996745,"uuid":"346887214","full_name":"sinshu/meltysynth","owner":"sinshu","description":"A SoundFont MIDI synthesizer for .NET","archived":false,"fork":false,"pushed_at":"2025-03-20T03:24:19.000Z","size":2932,"stargazers_count":159,"open_issues_count":5,"forks_count":18,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-08T13:02:57.886Z","etag":null,"topics":["audio","csharp","dotnet","midi","monogame","naudio","openal","opentk","sdl","sf2","sfml","silk","soundfont","synthesizer"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sinshu.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-03-12T00:56:41.000Z","updated_at":"2025-04-03T16:20:10.000Z","dependencies_parsed_at":"2024-10-26T21:20:36.444Z","dependency_job_id":"3c6adfc2-3a5e-4ff4-9aa0-15104a746034","html_url":"https://github.com/sinshu/meltysynth","commit_stats":{"total_commits":381,"total_committers":1,"mean_commits":381.0,"dds":0.0,"last_synced_commit":"465b65047534d5f85f92c8ab271058a163fd13a0"},"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinshu%2Fmeltysynth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinshu%2Fmeltysynth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinshu%2Fmeltysynth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sinshu%2Fmeltysynth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sinshu","download_url":"https://codeload.github.com/sinshu/meltysynth/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254448579,"owners_count":22072764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["audio","csharp","dotnet","midi","monogame","naudio","openal","opentk","sdl","sf2","sfml","silk","soundfont","synthesizer"],"created_at":"2024-10-14T02:43:58.477Z","updated_at":"2025-05-16T01:07:19.831Z","avatar_url":"https://github.com/sinshu.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# MeltySynth\n\nMeltySynth is a SoundFont synthesizer written in C#.\nThe purpose of this project is to provide a MIDI music playback functionality for any .NET applications without complicated dependencies.\nThe codebase is lightweight and can be applied to any audio drivers which support streaming audio, such as [SFML.Net](https://github.com/SFML/SFML.Net), [Silk.NET](https://github.com/dotnet/Silk.NET), [OpenTK](https://github.com/opentk/opentk), and [NAudio](https://github.com/naudio/NAudio).\n\nThe entire code is heavily inspired by the following projects:\n\n* [C# Synth](https://github.com/sinshu/CSharpSynthProject) by Alex Veltsistas\n* [TinySoundFont](https://github.com/schellingb/TinySoundFont) by Bernhard Schelling\n\nAn example code to synthesize a simple chord:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Play some notes (middle C, E, G).\nsynthesizer.NoteOn(0, 60, 100);\nsynthesizer.NoteOn(0, 64, 100);\nsynthesizer.NoteOn(0, 67, 100);\n\n// The output buffer (3 seconds).\nvar left = new float[3 * sampleRate];\nvar right = new float[3 * sampleRate];\n\n// Render the waveform.\nsynthesizer.Render(left, right);\n```\n\nAnother example code to synthesize a MIDI file:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Read the MIDI file.\nvar midiFile = new MidiFile(\"flourish.mid\");\nvar sequencer = new MidiFileSequencer(synthesizer);\nsequencer.Play(midiFile, false);\n\n// The output buffer.\nvar left = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\nvar right = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\n\n// Render the waveform.\nsequencer.Render(left, right);\n```\n\n\n\n## Features\n\n* Suitable for both real-time and offline synthesis.\n* Support for standard MIDI files with additional functionalities like dynamic tempo change.\n* No dependencies other than .NET Standard 2.1.\n\n\n\n## Installation\n\n[The NuGet package](https://www.nuget.org/packages/MeltySynth/) is available:\n\n```ps1\nInstall-Package MeltySynth\n```\n\nAll the classes are in the `MeltySynth` namespace:\n\n```cs\nusing MeltySynth;\n```\n\nIf you don't like DLLs, copy [all the .cs files](https://github.com/sinshu/meltysynth/tree/main/MeltySynth/src) to your project.\n\n\n\n## Playing sound\n\nMeltySynth can only generate PCM waveforms; MeltySynth itself does not have the ability to play sound from speakers. To make the sound audible, export the generated waveform as an audio file (e.g., WAV file) or pass it to some audio driver (e.g., NAudio). If you are not very familiar with how to handle PCM audio, [NAudio's tutorials](https://github.com/naudio/NAudio#tutorials) should be helpful.\n\n\u003e [!CAUTION]\n\u003e Note that MeltySynth does not provide thread safety. If you want to send notes and render the waveform in separate threads, you must ensure that the related methods will not be called simultaneously.\n\n\n\n## Demo\n\n### A demo song generated with [Arachno SoundFont](http://www.arachnosoft.com/main/soundfont.php)\n\nhttps://www.youtube.com/watch?v=xNgsIJKxPkI\n\n[![Youtube video](https://img.youtube.com/vi/xNgsIJKxPkI/0.jpg)](https://www.youtube.com/watch?v=xNgsIJKxPkI)\n\n### [A Doom port written in C#](https://github.com/sinshu/managed-doom) with MIDI music playback\n\nhttps://www.youtube.com/watch?v=_j1izHgIT4U\n\n[![Youtube video](https://img.youtube.com/vi/_j1izHgIT4U/0.jpg)](https://www.youtube.com/watch?v=_j1izHgIT4U)\n\n### A virtual keyboard made with [Raylib-CsLo](https://github.com/NotNotTech/Raylib-CsLo)\n\nhttps://www.youtube.com/watch?v=a8vuIq4JKhs\n\n[![Youtube video](https://img.youtube.com/vi/a8vuIq4JKhs/0.jpg)](https://www.youtube.com/watch?v=a8vuIq4JKhs)\n\n### Use MeltySynth as a MIDI device ([NAudio](https://github.com/naudio/NAudio) + [loopMIDI](https://www.tobias-erichsen.de/software/loopmidi.html))\n\nhttps://www.youtube.com/watch?v=BiFxvzs0jUI\n\n[![Youtube video](https://img.youtube.com/vi/BiFxvzs0jUI/0.jpg)](https://www.youtube.com/watch?v=BiFxvzs0jUI)\n\n### Use MeltySynth as a VST plugin ([VST.NET](https://github.com/obiwanjacobi/vst.net))\n\nhttps://www.youtube.com/watch?v=IUKIEWvw6Ik\n\n[![Youtube video](https://img.youtube.com/vi/IUKIEWvw6Ik/0.jpg)](https://www.youtube.com/watch?v=IUKIEWvw6Ik)\n\n\n\n## Examples\n\n### MIDI file player for various audio drivers\n\n* [MIDI file player for SFML.Net](https://github.com/sinshu/meltysynth/tree/main/Examples/SFML.Net)\n* [MIDI file player for Silk.NET (OpenAL)](https://github.com/sinshu/meltysynth/tree/main/Examples/Silk.NET.OpenAL)\n* [MIDI file player for Silk.NET (SDL)](https://github.com/sinshu/meltysynth/tree/main/Examples/Silk.NET.SDL)\n* [MIDI file player for OpenTK](https://github.com/sinshu/meltysynth/tree/main/Examples/OpenTK)\n* [MIDI file player for SDL2#](https://github.com/sinshu/meltysynth/tree/main/Examples/SDL2)\n* [MIDI file player for Sokol_csharp](https://github.com/sinshu/meltysynth/tree/main/Examples/Sokol)\n* [MIDI file player for MonoGame](https://github.com/sinshu/meltysynth/tree/main/Examples/MonoGame)\n* [MIDI file player for FNA.NET](https://github.com/sinshu/meltysynth/tree/main/Examples/FNA.NET)\n* [MIDI file player for Raylib-cs](https://github.com/sinshu/meltysynth/tree/main/Examples/Raylib_cs)\n* [MIDI file player for Raylib-CsLo](https://github.com/sinshu/meltysynth/tree/main/Examples/Raylib_CsLo)\n* [MIDI file player for DotFeather](https://github.com/sinshu/meltysynth/tree/main/Examples/DotFeather)\n* [MIDI file player for NAudio](https://github.com/sinshu/meltysynth/tree/main/Examples/NAudio)\n* [MIDI file player for CSCore](https://github.com/sinshu/meltysynth/tree/main/Examples/CSCore)\n* [MIDI file player for TinyAudio](https://github.com/sinshu/meltysynth/tree/main/Examples/TinyAudio)\n* [MIDI file player for SoundFlow](https://github.com/sinshu/meltysynth/tree/main/Examples/SoundFlow)\n* [MIDI file player for DrippyAL](https://github.com/sinshu/meltysynth/tree/main/Examples/DrippyAL)\n\n### Handling SoundFont\n\nTo enumerate samples in the SoundFont:\n\n```cs\nvar soundFont = new SoundFont(\"TimGM6mb.sf2\");\n\nforeach (var sample in soundFont.SampleHeaders)\n{\n    Console.WriteLine(sample.Name);\n}\n```\n\nTo enumerate instruments in the SoundFont:\n\n```cs\nvar soundFont = new SoundFont(\"TimGM6mb.sf2\");\n\nforeach (var instrument in soundFont.Instruments)\n{\n    Console.WriteLine(instrument.Name);\n}\n```\n\nTo enumerate presets in the SoundFont:\n\n```cs\nvar soundFont = new SoundFont(\"TimGM6mb.sf2\");\n\nforeach (var preset in soundFont.Presets)\n{\n    var bankNumber = preset.BankNumber.ToString(\"000\");\n    var patchNumber = preset.PatchNumber.ToString(\"000\");\n\n    Console.WriteLine($\"{bankNumber}:{patchNumber} {preset.Name}\");\n}\n```\n\n### Handling synthesizer\n\nTo change the instrument to play, send a [program change command](https://en.wikipedia.org/wiki/General_MIDI#Program_change_events) (0xC0) to the synthesizer:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Change the instrument to electric guitar (#30).\nsynthesizer.ProcessMidiMessage(0, 0xC0, 30, 0);\n\n// Play some notes (middle C, E, G).\nsynthesizer.NoteOn(0, 60, 100);\nsynthesizer.NoteOn(0, 64, 100);\nsynthesizer.NoteOn(0, 67, 100);\n\n// The output buffer (3 seconds).\nvar left = new float[3 * sampleRate];\nvar right = new float[3 * sampleRate];\n\n// Render the waveform.\nsynthesizer.Render(left, right);\n```\n\nTo play a melody, render the sound as a sequence of short blocks:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// The length of a block is 0.1 sec.\nvar blockSize = sampleRate / 10;\n\n// The entire output is 3 sec.\nvar blockCount = 30;\n\n// Define the melody.\n// A single row indicates the start timing, end timing, and pitch.\nvar data = new int[][]\n{\n    new int[] {  5, 10, 60 },\n    new int[] { 10, 15, 64 },\n    new int[] { 15, 25, 67 }\n};\n\n// The output buffer.\nvar left = new float[blockSize * blockCount];\nvar right = new float[blockSize * blockCount];\n\nfor (var t = 0; t \u003c blockCount; t++)\n{\n    // Process the melody.\n    foreach (var row in data)\n    {\n        if (t == row[0]) synthesizer.NoteOn(0, row[2], 100);\n        if (t == row[1]) synthesizer.NoteOff(0, row[2]);\n    }\n\n    // Render the block.\n    var blockLeft = left.AsSpan(blockSize * t, blockSize);\n    var blockRight = right.AsSpan(blockSize * t, blockSize);\n    synthesizer.Render(blockLeft, blockRight);\n}\n```\n\n### Handling MIDI file sequencer\n\nTo change the playback speed:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Read the MIDI file.\nvar midiFile = new MidiFile(@\"C:\\Windows\\Media\\flourish.mid\");\nvar sequencer = new MidiFileSequencer(synthesizer);\n\n// Play the MIDI file.\nsequencer.Play(midiFile, false);\n\n// Change the playback speed.\nsequencer.Speed = 1.5F;\n\n// The output buffer.\nvar left = new float[(int)(sampleRate * midiFile.Length.TotalSeconds / sequencer.Speed)];\nvar right = new float[(int)(sampleRate * midiFile.Length.TotalSeconds / sequencer.Speed)];\n\n// Render the waveform.\nsequencer.Render(left, right);\n```\n\nTo mute a certain track:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Read the MIDI file.\nvar midiFile = new MidiFile(@\"C:\\Windows\\Media\\flourish.mid\");\nvar sequencer = new MidiFileSequencer(synthesizer);\n\n// Discard MIDI messages if its channel is the percussion channel.\nsequencer.OnSendMessage = (synthesizer, channel, command, data1, data2) =\u003e\n{\n    if (channel == 9)\n    {\n        return;\n    }\n\n    synthesizer.ProcessMidiMessage(channel, command, data1, data2);\n};\n\n// Play the MIDI file.\nsequencer.Play(midiFile, false);\n\n// The output buffer.\nvar left = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\nvar right = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\n\n// Render the waveform.\nsequencer.Render(left, right);\n```\n\nTo change the instruments used in the MIDI file:\n\n```cs\n// Create the synthesizer.\nvar sampleRate = 44100;\nvar synthesizer = new Synthesizer(\"TimGM6mb.sf2\", sampleRate);\n\n// Read the MIDI file.\nvar midiFile = new MidiFile(@\"C:\\Windows\\Media\\flourish.mid\");\nvar sequencer = new MidiFileSequencer(synthesizer);\n\n// Turn all the instruments into electric guitars.\nsequencer.OnSendMessage = (synthesizer, channel, command, data1, data2) =\u003e\n{\n    if (command == 0xC0)\n    {\n        data1 = 30;\n    }\n\n    synthesizer.ProcessMidiMessage(channel, command, data1, data2);\n};\n\n// Play the MIDI file.\nsequencer.Play(midiFile, false);\n\n// The output buffer.\nvar left = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\nvar right = new float[(int)(sampleRate * midiFile.Length.TotalSeconds)];\n\n// Render the waveform.\nsequencer.Render(left, right);\n```\n\n\n\n## Todo\n\n* __Wave synthesis__\n    - [x] SoundFont reader\n    - [x] Waveform generator\n    - [x] Envelope generator\n    - [x] Low-pass filter\n    - [x] Vibrato LFO\n    - [x] Modulation LFO\n* __MIDI message processing__\n    - [x] Note on/off\n    - [x] Bank selection\n    - [x] Modulation\n    - [x] Volume control\n    - [x] Pan\n    - [x] Expression\n    - [x] Hold pedal\n    - [x] Program change\n    - [x] Pitch bend\n    - [x] Tuning\n* __Effects__\n    - [x] Reverb\n    - [x] Chorus\n* __Other things__\n    - [x] Standard MIDI file support\n    - [x] Loop extension support\n    - [x] Performace optimization\n\n\n\n## License\n\nMeltySynth is available under [the MIT license](LICENSE.txt).\n\n\n\n## References\n\n* __SoundFont\u0026reg; Technical Specification__  \nhttp://www.synthfont.com/SFSPEC21.PDF\n\n* __Polyphone Soundfont Editor__  \nSome of the test cases were generated with Polyphone.  \nhttps://www.polyphone-soundfonts.com/\n\n* __Freeverb by Jezar at Dreampoint__  \nThe implementation of the reverb effect is based on Freeverb.  \nhttps://music.columbia.edu/pipermail/music-dsp/2001-October/045433.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinshu%2Fmeltysynth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsinshu%2Fmeltysynth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsinshu%2Fmeltysynth/lists"}