Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
2d60cb4
Add Melanchall.DryWetMidi
collectioncard Feb 4, 2025
8cc7c05
[WIP] Add initial support for midi reading
collectioncard Feb 11, 2025
f93bb64
Add midi test files
collectioncard Feb 14, 2025
88e3fe3
Adding Reading visual notes from midi
LifeHckr Feb 14, 2025
ddf5ab7
Visual Update
LifeHckr Feb 20, 2025
dfa7550
Make make navigateable with keys
LifeHckr Feb 21, 2025
1412851
added heal note
cornerloan Feb 21, 2025
c53c06c
Add csharp stuff to gitignore
collectioncard Feb 21, 2025
7595ae4
Refactoring for map room types
LifeHckr Feb 21, 2025
7a93dc4
Remove dotsettings from repo
collectioncard Feb 21, 2025
5276383
Refactoring for map room types
LifeHckr Feb 21, 2025
3a11a5b
Merge branch 'Robust-Map' of https://github.com/Project-Funk-Engine/P…
LifeHckr Feb 21, 2025
fdcac57
Add star shader to battle scene
collectioncard Feb 21, 2025
fbdbaf7
Add blood droplets to enemy 1
collectioncard Feb 21, 2025
af12534
added vampire note
cornerloan Feb 21, 2025
0f950db
added quarter note
cornerloan Feb 21, 2025
0626651
Merge branch 'new-note-types' into game_beautification
cornerloan Feb 22, 2025
5471c93
Add gradient and timescale modifier to star shader
collectioncard Feb 23, 2025
df12691
Removed old looping Backgrounds
LifeHckr Feb 24, 2025
6f7bc92
added art to placed notes
cornerloan Feb 24, 2025
1f003c2
Merge pull request #73 from Project-Funk-Engine/game_beautification
LifeHckr Feb 24, 2025
0ee00a4
Refactored quarter note
LifeHckr Feb 24, 2025
9815194
Merge branch 'Sprint-3' into player-notes-art
LifeHckr Feb 24, 2025
35ba1e7
Merge pull request #74 from Project-Funk-Engine/player-notes-art
LifeHckr Feb 24, 2025
8678563
Refactoring note sprites
LifeHckr Feb 24, 2025
2aaaf78
Chest Room finished
LifeHckr Feb 22, 2025
7aa6bf1
Added room transition animation
LifeHckr Feb 22, 2025
e3841cc
Added loop marker
LifeHckr Feb 24, 2025
5676fc7
Update frame
LifeHckr Feb 24, 2025
4936f27
Make Loopable visible before battle start
LifeHckr Feb 24, 2025
9958590
Merge pull request #75 from Project-Funk-Engine/Robust-Map
LifeHckr Feb 24, 2025
d9bae26
Create Remap.tscn
Rmojarro1 Feb 21, 2025
2b75171
Remapping for arrow, WASD, QWER
Rmojarro1 Feb 24, 2025
ed934a3
Icons and fix default
Rmojarro1 Feb 24, 2025
ba77149
Oops my bad
LifeHckr Feb 25, 2025
0ca318e
Merge pull request #76 from Project-Funk-Engine/Remap_Menu
LifeHckr Feb 25, 2025
12f211c
Fix merge and integration issues with remap menu
LifeHckr Feb 25, 2025
d0099b2
added second song
cornerloan Feb 26, 2025
f32dffd
Merge branch 'Sprint-3' into Midi-Readin
cornerloan Feb 26, 2025
60f25e5
added song templates to scribe
cornerloan Feb 26, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@


.idea/.idea.Funk Engine/.idea/

*.DotSettings.user
export_presets.cfg
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[remap]

importer="wav"
type="AudioStreamWAV"
uid="uid://cq1c2vs11cp5e"
path="res://.godot/imported/620230__josefpres__dark-loops-220-octave-piano-with-efect-short-loop-60-bpm.wav-e288d05020bddeea68e6248ede74a475.sample"

[deps]

source_file="res://Audio/620230__josefpres__dark-loops-220-octave-piano-with-efect-short-loop-60-bpm.wav"
dest_files=["res://.godot/imported/620230__josefpres__dark-loops-220-octave-piano-with-efect-short-loop-60-bpm.wav-e288d05020bddeea68e6248ede74a475.sample"]

[params]

force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0
Binary file added Audio/midi/florestan-subset.sf2
Binary file not shown.
Binary file added Audio/midi/midiTest.mid
Binary file not shown.
Binary file added Audio/midi/midiTest2.mid
Binary file not shown.
110 changes: 110 additions & 0 deletions Classes/MidiMaestro/MidiMaestro.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
using System;
using System.Linq;
using FunkEngine;
using Godot;
using Melanchall.DryWetMidi.Core;
using Melanchall.DryWetMidi.Interaction;
using Melanchall.DryWetMidi.Multimedia;

public partial class MidiMaestro : Resource
{
private MidiFile _midiFile;

//The four note rows that we care about
private midiNoteInfo[] _upNotes;
private midiNoteInfo[] _downNotes;
private midiNoteInfo[] _leftNotes;
private midiNoteInfo[] _rightNotes;

private MidiFile strippedSong;

Check warning on line 19 in Classes/MidiMaestro/MidiMaestro.cs

View workflow job for this annotation

GitHub Actions / build

The field 'MidiMaestro.strippedSong' is never used

Check warning on line 19 in Classes/MidiMaestro/MidiMaestro.cs

View workflow job for this annotation

GitHub Actions / build

The field 'MidiMaestro.strippedSong' is never used
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove unused field.

The field strippedSong is declared but never used in the code.

-    private MidiFile strippedSong;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private MidiFile strippedSong;
🧰 Tools
🪛 GitHub Check: build

[warning] 19-19:
The field 'MidiMaestro.strippedSong' is never used


[warning] 19-19:
The field 'MidiMaestro.strippedSong' is never used


private SongData songData;

//The path relative to the Audio folder. Will change later
public MidiMaestro(string filePath)
{
if (!FileAccess.FileExists(filePath))
{
GD.PrintErr("ERROR: Unable to load level Midi file: " + filePath);
}

_midiFile = MidiFile.Read(filePath);

//Strip out the notes from the midi file
foreach (var track in _midiFile.GetTrackChunks())
{
string trackName = track.Events.OfType<SequenceTrackNameEvent>().FirstOrDefault()?.Text;
midiNoteInfo[] noteEvents = track
.GetNotes()
.Select(note => new midiNoteInfo(note, _midiFile.GetTempoMap()))
.ToArray();

switch (trackName)
{
case "Up":
_upNotes = noteEvents;
break;
case "Down":
_downNotes = noteEvents;
break;
case "Left":
_leftNotes = noteEvents;
break;
case "Right":
_rightNotes = noteEvents;
break;
}
}

//Populate the song data
songData = new SongData
{
//TODO: Allow for changes in this data
Bpm = 120,
//Fudge the numbers a bit if we have a really short song
SongLength =
_midiFile.GetDuration<MetricTimeSpan>().Seconds < 20
? 20
: _midiFile.GetDuration<MetricTimeSpan>().Seconds,
NumLoops = 1,
};
}
Comment on lines +24 to +71
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

MIDI file loading and track processing looks solid.

The constructor correctly handles file existence checks, loads the MIDI file, and processes tracks based on named categories. The approach to map track names to arrow directions is intuitive and maintainable.

However, there's a hardcoded BPM value:

-            //TODO: Allow for changes in this data
-            Bpm = 120,
+            Bpm = _midiFile.GetTempoMap().GetTempoAtTime(0).BeatsPerMinute,

Consider extracting the actual BPM from the MIDI file's tempo map instead of hardcoding it.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public MidiMaestro(string filePath)
{
if (!FileAccess.FileExists(filePath))
{
GD.PrintErr("ERROR: Unable to load level Midi file: " + filePath);
}
_midiFile = MidiFile.Read(filePath);
//Strip out the notes from the midi file
foreach (var track in _midiFile.GetTrackChunks())
{
string trackName = track.Events.OfType<SequenceTrackNameEvent>().FirstOrDefault()?.Text;
midiNoteInfo[] noteEvents = track
.GetNotes()
.Select(note => new midiNoteInfo(note, _midiFile.GetTempoMap()))
.ToArray();
switch (trackName)
{
case "Up":
_upNotes = noteEvents;
break;
case "Down":
_downNotes = noteEvents;
break;
case "Left":
_leftNotes = noteEvents;
break;
case "Right":
_rightNotes = noteEvents;
break;
}
}
//Populate the song data
songData = new SongData
{
//TODO: Allow for changes in this data
Bpm = 120,
//Fudge the numbers a bit if we have a really short song
SongLength =
_midiFile.GetDuration<MetricTimeSpan>().Seconds < 20
? 20
: _midiFile.GetDuration<MetricTimeSpan>().Seconds,
NumLoops = 1,
};
}
public MidiMaestro(string filePath)
{
if (!FileAccess.FileExists(filePath))
{
GD.PrintErr("ERROR: Unable to load level Midi file: " + filePath);
}
_midiFile = MidiFile.Read(filePath);
//Strip out the notes from the midi file
foreach (var track in _midiFile.GetTrackChunks())
{
string trackName = track.Events.OfType<SequenceTrackNameEvent>().FirstOrDefault()?.Text;
midiNoteInfo[] noteEvents = track
.GetNotes()
.Select(note => new midiNoteInfo(note, _midiFile.GetTempoMap()))
.ToArray();
switch (trackName)
{
case "Up":
_upNotes = noteEvents;
break;
case "Down":
_downNotes = noteEvents;
break;
case "Left":
_leftNotes = noteEvents;
break;
case "Right":
_rightNotes = noteEvents;
break;
}
}
//Populate the song data
songData = new SongData
{
Bpm = _midiFile.GetTempoMap().GetTempoAtTime(0).BeatsPerMinute,
//Fudge the numbers a bit if we have a really short song
SongLength =
_midiFile.GetDuration<MetricTimeSpan>().Seconds < 20
? 20
: _midiFile.GetDuration<MetricTimeSpan>().Seconds,
NumLoops = 1,
};
}


public midiNoteInfo[] GetNotes(ArrowType arrowType)
{
return arrowType switch
{
ArrowType.Up => _upNotes,
ArrowType.Down => _downNotes,
ArrowType.Left => _leftNotes,
ArrowType.Right => _rightNotes,
_ => throw new ArgumentOutOfRangeException(nameof(arrowType), arrowType, null),
};
}

public SongData GetSongData()
{
return songData;
}
}

//A facade to wrap the midi notes. This is a simple class that wraps a Note object from the DryWetMidi library.
public class midiNoteInfo
{
private readonly Melanchall.DryWetMidi.Interaction.Note _note;
private readonly TempoMap _tempoMap;

public midiNoteInfo(Melanchall.DryWetMidi.Interaction.Note note, TempoMap tempoMap)
{
_note = note;
_tempoMap = tempoMap;
}

public long GetStartTimeTicks() => _note.Time;

public int GetStartTimeSeconds() => _note.TimeAs<MetricTimeSpan>(_tempoMap).Seconds;

public long GetEndTime() => _note.EndTime;

public long GetDuration() => _note.Length;
}
15 changes: 15 additions & 0 deletions Classes/MidiMaestro/SongTemplate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace FunkEngine.Classes.MidiMaestro;

public partial class SongTemplate
{
public string Name;
public string AudioLocation;
public string MIDILocation;

public SongTemplate(string name = "", string audioLocation = "", string midiLocation = "")
{
Name = name;
AudioLocation = audioLocation;
MIDILocation = midiLocation;
}
}
7 changes: 5 additions & 2 deletions Classes/Notes/Note.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public partial class Note : Resource
public PuppetTemplate Owner;
public string Name;
private int _baseVal;
public float CostModifier { get; private set; }
private Action<BattleDirector, Note, Timing> NoteEffect; //TODO: Where/How to deal with timing.

public string Tooltip;
Expand All @@ -22,7 +23,8 @@ public Note(
Texture2D texture = null,
PuppetTemplate owner = null,
int baseVal = 1,
Action<BattleDirector, Note, Timing> noteEffect = null
Action<BattleDirector, Note, Timing> noteEffect = null,
float costModifier = 1.0f
)
{
Name = name;
Expand All @@ -38,6 +40,7 @@ public Note(
_baseVal = baseVal;
Texture = texture;
Tooltip = tooltip;
CostModifier = costModifier;
}

public void OnHit(BattleDirector BD, Timing timing)
Expand All @@ -49,7 +52,7 @@ public Note Clone()
{
//Eventually could look into something more robust, but for now shallow copy is preferable.
//We only would want val and name to be copied by value
Note newNote = new Note(Name, Tooltip, Texture, Owner, _baseVal, NoteEffect);
Note newNote = new Note(Name, Tooltip, Texture, Owner, _baseVal, NoteEffect, CostModifier);
return newNote;
}
}
Binary file added Classes/Notes/assets/heal_note.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions Classes/Notes/assets/heal_note.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://cdf3g3174du4r"
path="res://.godot/imported/heal_note.png-09ca289a296eee82d33c64101a4e593a.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://Classes/Notes/assets/heal_note.png"
dest_files=["res://.godot/imported/heal_note.png-09ca289a296eee82d33c64101a4e593a.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file added Classes/Notes/assets/quarter_note.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions Classes/Notes/assets/quarter_note.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://uksjoqp7p0gq"
path="res://.godot/imported/quarter_note.png-2b4c9985d99038807abfd63e553c2d6a.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://Classes/Notes/assets/quarter_note.png"
dest_files=["res://.godot/imported/quarter_note.png-2b4c9985d99038807abfd63e553c2d6a.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
Binary file added Classes/Notes/assets/vampire_note.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions Classes/Notes/assets/vampire_note.png.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[remap]

importer="texture"
type="CompressedTexture2D"
uid="uid://dg0lmu0pip4lr"
path="res://.godot/imported/vampire_note.png-4331f817a6feee1f1066a9e9f95934c8.ctex"
metadata={
"vram_texture": false
}

[deps]

source_file="res://Classes/Notes/assets/vampire_note.png"
dest_files=["res://.godot/imported/vampire_note.png-4331f817a6feee1f1066a9e9f95934c8.ctex"]

[params]

compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
3 changes: 2 additions & 1 deletion Funk Engine.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Godot.NET.Sdk/4.3.0">
<Project Sdk="Godot.NET.Sdk/4.3.0">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework Condition=" '$(GodotTargetPlatform)' == 'android' ">net7.0</TargetFramework>
Expand All @@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<Content Include="SaveData\SaveData.json" />
<PackageReference Include="Melanchall.DryWetMidi" Version="7.2.0" />
</ItemGroup>
<Target Name="Husky" BeforeTargets="Restore;CollectPackageReferences" Condition="'$(HUSKY)' != 0">
<Exec Command="dotnet tool restore" StandardOutputImportance="Low" StandardErrorImportance="High" />
Expand Down
3 changes: 0 additions & 3 deletions Funk Engine.sln.DotSettings.user

This file was deleted.

Loading