1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-12-26 15:07:14 +00:00

Vocal group work

This commit is contained in:
Eric Myllyoja 2022-12-13 17:38:55 -05:00
parent 19b2f3799a
commit 234dc0ac19
6 changed files with 406 additions and 21 deletions

View file

@ -135,6 +135,14 @@
<haxelib name="hxcpp-debug-server" if="desktop debug" />
<!--
With these options enabled, console popup and beep no longer occur.
You can still see the log messages by opening the console (F2).
Be sure to remove these during cleanup and bugfix testing!
-->
<haxedef name="FLX_NO_ERROR_SOUND" />
<haxedef name="FLX_NO_ERROR_CONSOLE" />
<!--Disable the Flixel core focus lost screen-->
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />

View file

@ -21,13 +21,6 @@
"ref": "157eaf3",
"url": "https://github.com/MasterEric/flixel-addons"
},
{
"name": "flixel-addons",
"type": "git",
"dir": null,
"ref": "dev",
"url": "https://github.com/MasterEric/flixel-addons"
},
{
"name": "flixel-ui",
"type": "haxelib",
@ -100,8 +93,8 @@
"name": "openfl",
"type": "git",
"dir": null,
"ref": "4f999ac",
"url": "https://github.com/openfl/openfl"
"ref": "3fd5763",
"url": "https://github.com/MasterEric/openfl/"
},
{
"name": "polymod",

View file

@ -0,0 +1,206 @@
package funkin.audio;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.system.FlxSound;
/**
* A group of FlxSounds which can be controlled as a whole.
*
* Add sounds to the group using `add()`, and then control them
* as a whole using the properties and methods of this class.
*
* It is assumed that all the sounds will play at the same time,
* and have the same duration.
*/
class FlxAudioGroup extends FlxTypedGroup<FlxSound>
{
/**
* The position in time of the sounds in the group.
* Measured in milliseconds.
*/
public var time(get, set):Float;
function get_time():Float
{
if (getFirstAlive() != null)
return getFirstAlive().time;
else
return 0;
}
function set_time(time:Float):Float
{
forEachAlive(function(sound:FlxSound)
{
// account for different offsets per sound?
sound.time = time;
});
return time;
}
/**
* The volume of the sounds in the group.
*/
public var volume(get, set):Float;
function get_volume():Float
{
if (getFirstAlive() != null)
return getFirstAlive().volume;
else
return 1.0;
}
function set_volume(volume:Float):Float
{
forEachAlive(function(sound:FlxSound)
{
sound.volume = volume;
});
return volume;
}
/**
* The pitch of the sounds in the group, as a multiplier of 1.0x.
* `2.0` would play the audio twice as fast with a higher pitch,
* and `0.5` would play the audio at half speed with a lower pitch.
*/
public var pitch(get, set):Float;
function get_pitch():Float
{
#if FLX_PITCH
if (getFirstAlive() != null)
return getFirstAlive().pitch;
else
#end
return 1;
}
function set_pitch(val:Float):Float
{
#if FLX_PITCH
trace('Setting audio pitch to ' + val);
forEachAlive(function(sound:FlxSound)
{
sound.pitch = val;
});
#end
return val;
}
/**
* Whether members of the group should be destroyed when they finish playing.
*/
public var autoDestroyMembers(default, set):Bool = false;
function set_autoDestroyMembers(value:Bool):Bool
{
autoDestroyMembers = value;
forEachAlive(function(sound:FlxSound)
{
sound.autoDestroy = value;
});
return value;
}
/**
* Add a sound to the group.
*/
public override function add(sound:FlxSound):FlxSound
{
var result:FlxSound = super.add(sound);
if (result == null)
return;
// Apply parameters to the new sound.
result.autoDestroy = this.autoDestroyMembers;
result.pitch = this.pitch;
result.volume = this.volume;
// We have to play, then pause the sound to set the time,
// else the sound will restart immediately when played.
result.play(true, 0.0);
result.pause();
result.time = this.time;
}
/**
* Pause all the sounds in the group.
*/
public function pause()
{
forEachAlive(function(sound:FlxSound)
{
sound.pause();
});
}
/**
* Play all the sounds in the group.
*/
public function play(forceRestart:Bool = false, startTime:Float = 0.0, ?endTime:Float)
{
forEachAlive(function(sound:FlxSound)
{
sound.play(forceRestart, startTime, endTime);
});
}
/**
* Resume all the sounds in the group.
*/
public function resume()
{
forEachAlive(function(sound:FlxSound)
{
sound.resume();
});
}
/**
* Stop all the sounds in the group.
*/
public function stop()
{
forEachAlive(function(sound:FlxSound)
{
sound.stop();
});
}
public override function clear():Void {
this.stop();
super.clear();
}
/**
* Calculates the deviation of the sounds in the group from the target time.
*
* @param targetTime The time to compare the sounds to.
* If null, the current time of the first sound in the group is used.
* @return The largest deviation of the sounds in the group from the target time.
*/
public function calcDeviation(?targetTime:Float):Float
{
var deviation:Float = 0;
forEachAlive(function(sound:FlxSound)
{
if (targetTime == null)
targetTime = sound.time;
else
{
var diff:Float = sound.time - targetTime;
if (Math.abs(diff) > Math.abs(deviation))
deviation = diff;
}
});
return deviation;
}
}

View file

@ -0,0 +1,119 @@
package funkin.audio;
import flixel.system.FlxSound;
/**
* An audio group that allows for specific control of vocal tracks.
*/
class VocalGroup extends FlxAudioGroup
{
/**
* The player's vocal track.
*/
var playerVocals:FlxSound;
/**
* The opponent's vocal track.
*/
var opponentVocals:FlxSound;
/**
* The volume of the player's vocal track.
* Nore that this value is multiplied by the overall volume of the group.
*/
public var playerVolume(default, set):Float;
function set_playerVolume(value:Float):Float
{
playerVolume = value;
if (playerVocals != null)
{
// Make sure volume is capped at 1.0.
playerVocals.volume = Math.min(playerVolume * this.volume, 1.0);
}
return playerVolume;
}
/**
* The volume of the opponent's vocal track.
* Nore that this value is multiplied by the overall volume of the group.
*/
public var opponentVolume(default, set):Float;
function set_opponentVolume(value:Float):Float
{
opponentVolume = value;
if (opponentVocals != null)
{
// Make sure volume is capped at 1.0.
opponentVocals.volume = opponentVolume * this.volume;
}
return opponentVolume;
}
/**
* Sets up the player's vocal track.
* Stops and removes the existing player track if one exists.
*/
public function setPlayerVocals(sound:FlxSound):FlxSound
{
if (playerVocals != null)
{
playerVocals.stop();
remove(playerVocals);
playerVocals = null;
}
playerVocals = add(sound);
playerVocals.volume = this.playerVolume * this.volume;
return playerVocals;
}
/**
* Sets up the opponent's vocal track.
* Stops and removes the existing player track if one exists.
*/
public function setOpponentVocals(sound:FlxSound):FlxSound
{
if (opponentVocals != null)
{
opponentVocals.stop();
remove(opponentVocals);
opponentVocals = null;
}
opponentVocals = add(sound);
opponentVocals.volume = this.opponentVolume * this.volume;
return opponentVocals;
}
/**
* In this extension of FlxAudioGroup, there is a separate overall volume
* which affects all the members of the group.
*/
var _volume = 1.0;
override function get_volume():Float
{
return _volume;
}
override function set_volume(value:Float):Float
{
_volume = super.set_volume(value);
if (playerVocals != null)
{
playerVocals.volume = playerVolume * _volume;
}
if (opponentVocals != null)
{
opponentVocals.volume = opponentVolume * _volume;
}
return _volume;
}
}

View file

@ -196,17 +196,11 @@ class HealthIcon extends FlxSprite
// Make the health icons bump (the update function causes them to lerp back down).
if (this.width > this.height)
{
var targetSize = Std.int(CoolUtil.coolLerp(this.width + HEALTH_ICON_SIZE * 0.2, HEALTH_ICON_SIZE, 0.15));
targetSize = Std.int(Math.min(targetSize, HEALTH_ICON_SIZE * 1.2));
setGraphicSize(targetSize, 0);
setGraphicSize(this.width + (HEALTH_ICON_SIZE * this.size.x * 0.2), 0);
}
else
{
var targetSize = Std.int(CoolUtil.coolLerp(this.height + HEALTH_ICON_SIZE * 0.2, HEALTH_ICON_SIZE, 0.15));
targetSize = Std.int(Math.min(targetSize, HEALTH_ICON_SIZE * 1.2));
setGraphicSize(0, targetSize);
setGraphicSize(0, this.height + (HEALTH_ICON_SIZE * this.size.y * 0.2));
}
this.updateHitbox();
}

View file

@ -278,6 +278,26 @@ class ChartEditorState extends HaxeUIState
return isViewDownscroll;
}
/**
* Whether hitsounds are enabled for at least one character.
*/
var hitsoundsEnabled(get, null):Bool;
function get_hitsoundsEnabled():Bool
{
return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
}
/**
* Whether hitsounds are enabled for the player.
*/
var hitsoundsEnabledPlayer:Bool = true;
/**
* Whether hitsounds are enabled for the opponent.
*/
var hitsoundsEnabledOpponent:Bool = true;
/**
* Whether the user's mouse cursor is hovering over a SOLID component of the HaxeUI.
* If so, ignore mouse events underneath.
@ -1063,13 +1083,25 @@ class ChartEditorState extends HaxeUIState
});
setUISelected('menubarItemMetronomeEnabled', shouldPlayMetronome);
addUIChangeListener('menubarItemPlayerHitsounds', (event:UIEvent) ->
{
hitsoundsEnabledPlayer = event.value;
});
setUISelected('menubarItemPlayerHitsounds', hitsoundsEnabledPlayer);
addUIChangeListener('menubarItemOpponentHitsounds', (event:UIEvent) ->
{
hitsoundsEnabledOpponent = event.value;
});
setUISelected('menubarItemOpponentHitsounds', hitsoundsEnabledOpponent);
var instVolumeLabel:Label = findComponent('menubarLabelVolumeInstrumental', Label);
addUIChangeListener('menubarItemVolumeInstrumental', (event:UIEvent) ->
{
var volume:Float = event.value / 100.0;
if (audioInstTrack != null)
audioInstTrack.volume = volume;
instVolumeLabel.text = 'Instrumental - ${event.value}%';
instVolumeLabel.text = 'Instrumental - ${Std.int(event.value)}%';
});
var vocalsVolumeLabel:Label = findComponent('menubarLabelVolumeVocals', Label);
@ -1078,7 +1110,7 @@ class ChartEditorState extends HaxeUIState
var volume:Float = event.value / 100.0;
if (audioVocalTrackGroup != null)
audioVocalTrackGroup.volume = volume;
vocalsVolumeLabel.text = 'Vocals - ${event.value}%';
vocalsVolumeLabel.text = 'Vocals - ${Std.int(event.value)}%';
});
var playbackSpeedLabel:Label = findComponent('menubarLabelPlaybackSpeed', Label);
@ -1091,7 +1123,7 @@ class ChartEditorState extends HaxeUIState
if (audioVocalTrackGroup != null)
audioVocalTrackGroup.pitch = pitch;
#end
playbackSpeedLabel.text = 'Playback Speed - ${pitch}x';
playbackSpeedLabel.text = 'Playback Speed - ${Std.int(event.value * 100) / 100}x';
});
addUIChangeListener('menubarItemToggleToolboxTools', (event:UIEvent) ->
@ -2265,7 +2297,9 @@ class ChartEditorState extends HaxeUIState
// If middle mouse panning during song playback, we move ONLY the playhead, without scrolling. Neat!
var oldStepTime = Conductor.currentStepTime;
var oldSongPosition = Conductor.songPosition;
Conductor.update(audioInstTrack.time);
handleHitsounds(oldSongPosition, Conductor.songPosition);
// Resync vocals.
if (Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
audioVocalTrackGroup.time = audioInstTrack.time;
@ -2279,8 +2313,9 @@ class ChartEditorState extends HaxeUIState
else
{
// Else, move the entire view.
var oldSongPosition = Conductor.songPosition;
Conductor.update(audioInstTrack.time);
handleHitsounds(oldSongPosition, Conductor.songPosition);
// Resync vocals.
if (audioVocalTrackGroup != null && Math.abs(audioInstTrack.time - audioVocalTrackGroup.time) > 100)
audioVocalTrackGroup.time = audioInstTrack.time;
@ -2302,6 +2337,36 @@ class ChartEditorState extends HaxeUIState
}
}
/**
* Handle the playback of hitsounds.
*/
function handleHitsounds(oldSongPosition:Float, newSongPosition:Float):Void {
if (!hitsoundsEnabled)
return;
// Assume notes are sorted by time.
for (noteData in currentSongChartNoteData) {
if (noteData.time < oldSongPosition)
// Note is in the past.
continue;
if (noteData.time >= newSongPosition)
// Note is in the future.
return;
// Note was just hit.
switch (noteData.getStrumlineIndex()) {
case 0: // Player
if (hitsoundsEnabledPlayer)
playSound(Paths.sound('funnyNoise/funnyNoise-09'));
case 1: // Opponent
if (hitsoundsEnabledOpponent)
playSound(Paths.sound('funnyNoise/funnyNoise-010'));
}
}
}
function startAudioPlayback()
{
if (audioInstTrack != null)