Skip to content

2.0.0 #21

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 44 commits into from
Feb 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
fa1d5f9
Add drawBaseline option
ACrazyTown Nov 18, 2024
9fbaf2c
regen bitmap in draw()
ACrazyTown Nov 18, 2024
3ec2180
drawBaseline changes
ACrazyTown Nov 18, 2024
f4a0f10
fix no rendering
ACrazyTown Nov 18, 2024
4315ea2
baseline & resize fixes
ACrazyTown Nov 18, 2024
18db8db
Get rid of a lot of internal helpers
ACrazyTown Nov 20, 2024
cf6ff53
Change samplesPerPixel to int
ACrazyTown Nov 23, 2024
23ed078
log
ACrazyTown Nov 23, 2024
2142926
Fix wrong int min value
ACrazyTown Dec 4, 2024
b88c833
Add FlxWaveformBuffer
ACrazyTown Jan 3, 2025
989480e
A lot of docs
ACrazyTown Jan 3, 2025
1a66117
reorganize
ACrazyTown Jan 4, 2025
c8d96c0
WIP Sample graph
ACrazyTown Jan 10, 2025
44abbdc
Add RMS drawing, rendering improvements among other things
ACrazyTown Jan 11, 2025
79812dd
Remove leftover debugging...
ACrazyTown Jan 11, 2025
b4d933c
bars!
ACrazyTown Jan 14, 2025
95124d2
add drawDataDirty
ACrazyTown Jan 14, 2025
f961cdc
slight optimization
ACrazyTown Jan 14, 2025
c6022cc
WIP changelog/version bump
ACrazyTown Jan 14, 2025
b4f4528
tags as well
ACrazyTown Jan 14, 2025
92c0933
consistency
ACrazyTown Jan 14, 2025
ad23ecb
Update draw data in first draw call instead
ACrazyTown Jan 16, 2025
14e5570
Fixes for Flash
ACrazyTown Jan 16, 2025
c95221e
Solving more TODOs
ACrazyTown Jan 17, 2025
01a485b
Add WaveformDrawMode.SINGLE_CHANNEL
ACrazyTown Jan 18, 2025
602b4bd
Sync docs
ACrazyTown Jan 18, 2025
fa6dc87
Losing my mind
ACrazyTown Jan 19, 2025
d4aa228
docs
ACrazyTown Jan 19, 2025
434a8d3
Fixed a bug so revert old move code in sample
ACrazyTown Jan 19, 2025
acff497
rework sample
ACrazyTown Jan 19, 2025
acc5a01
match sample version with lib ver
ACrazyTown Jan 19, 2025
46588d8
Fix this mistake in readme (#24)
Vortex2Oblivion Jan 27, 2025
7aa7871
Replace references of 1.3.0 with 2.0.0
ACrazyTown Feb 12, 2025
23ee37f
Formatting fixes
ACrazyTown Feb 12, 2025
ba0b7cd
Move BytesExt to _internal folder
ACrazyTown Feb 12, 2025
70625e8
Move FlxSound loading code to FlxWaveformBuffer
ACrazyTown Feb 12, 2025
341d8ff
More formatting
ACrazyTown Feb 13, 2025
d8653fe
oof
ACrazyTown Feb 13, 2025
d797f21
Initial implementation of waveformTime/waveformDuration
ACrazyTown Feb 15, 2025
e203256
Pretty CHANGELOG
ACrazyTown Feb 15, 2025
bd40b00
Update sample to use waveformTime and waveformDuration
ACrazyTown Feb 15, 2025
7688f6c
Cleanup & flash fixes
ACrazyTown Feb 15, 2025
ec301ae
Some resizing fixes
ACrazyTown Feb 15, 2025
a468ab7
Merge branch 'main' into rendering-changes
ACrazyTown Feb 15, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ samples/**/dump
samples/test
.DS_Store
ignored/
flixel-waveform.zip
flixel-waveform.zip
3 changes: 2 additions & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"recommendations": [
"openfl.lime-vscode-extension",
"redhat.vscode-xml"
"redhat.vscode-xml",
"aaron-bond.better-comments"
]
}
31 changes: 24 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,33 @@
## 1.2.1
- Fix waveform not properly scaling when resizing
## 2.0.0
- ✨ **Added** `waveformTime` and `waveformDuration`
- Set `waveformDuration` to the length (in miliseconds) you want to visualize.
- Set `waveformTime` to set the audio time (in miliseconds) the waveform will start at.
- 🗑️ **Removed** `setDrawRange()` in favor of `waveformTime` and `waveformDuration`
- ✨ **Added** `waveformBarSize`, `waveformBarPadding`,
- Allows for more customizable waveform designs!
- Increase the bar size for more blockier waveforms which are also less expensive to compute
- ✨ **Added** `waveformRMSColor`, `waveformDrawRMS`
- Allows for visualizing the RMS (root mean square) of the audio data.
- The RMS represents the average/effective loudness of audio.
- ✨ **Added** `waveformDrawBaseline`
- Simply draws a line in the middle of the waveform to represent 0
- ✨ **Added** sample graph renderer when trying to view very low time ranges
- A seamless way of transitioning from the peak-based renderer and the graphed samples is planned for a future release.
- ✨ **Added** `SINGLE_CHANNEL(channel)` to `WaveformDrawMode` to allow drawing a single channel across the entire waveform area.
- 🛠️ **Moved** `flixel.addons.display.waveform.BytesExt` to `flixel.addons.display.waveform._internal.BytesExt`
- This was never meant to be a public class anyways...
- A good chunk of code was refactored, so probably quite a few bug fixes?

## 1.2
- Added experimental HTML5 support
## 1.2.0
- ✨ **Added** experimental HTML5 support
- 32bit audio now assumes it's stored as in Float32 format
- This is temporary until a proper way to differentiate the two is found. See https://github.com/ACrazyTown/flixel-waveform/issues/9
- Samples are now normalized in the range (-1, 1) instead of (0, 1)
- This is an internal change and should have no effect on anything public.

## 1.1
- Added support for 24bit audio
- Fix crashing when using mono sounds.
## 1.1.0
- ✨ **Added** support for 24bit audio
- 🛠️ **Fix** crashing when using mono sounds.

## 1.0.1
- Bugfixes
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ A simple (I hope) `FlxSprite` extension that allows for rendering waveforms from

1. Install via haxelib: `haxelib install flixel-waveform`
- If you want to use the latest (possibly unstable) development version, install via haxelib git: `haxelib git flixel-waveform https://github.com/ACrazyTown/flixel-waveform`
3. And then include it in your Project.xml: `<haxelib name="flixel-waveform">`
3. And then include it in your Project.xml: `<haxelib name="flixel-waveform" />`

4. Finally, import it in your code when needed: `import flixel.addons.display.waveform.FlxWaveform;`
6 changes: 4 additions & 2 deletions haxelib.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
{
"name": "flixel-waveform",
"url": "https://github.com/ACrazyTown/flixel-waveform",
"description": "A `FlxWaveform` is an `FlxSprite` extension that provides a simple yet powerful API for drawing waveforms from audio data.",
"tags": ["audio", "waveform", "haxe", "haxeflixel", "flixel"],
"contributors": ["ACrazyTown"],
"classPath": "src",
"license": "MIT",
"version": "1.2.1",
"releasenote": "Bugfixes; See CHANGELOG.md"
"version": "2.0.0",
"releasenote": "Stylization options, Graph samples, RMS; See CHANGELOG.md"
}
13 changes: 13 additions & 0 deletions samples/basic/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Lime",
"type": "lime",
"request": "launch"
}
]
}
10 changes: 6 additions & 4 deletions samples/basic/Project.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<!-- _________________________ Application Settings _________________________ -->

<app title="flixel-waveform-sample" file="flixel-waveform-sample" main="Main" version="0.0.1" company="ACrazyTown" />
<app title="flixel-waveform-sample" file="flixel-waveform-sample" main="Main" version="2.0.0" company="ACrazyTown" />

<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
Expand All @@ -16,7 +16,7 @@
<!-- ____________________________ Window Settings ___________________________ -->

<!--These window settings apply to all targets-->
<window width="400" height="400" fps="60" background="#000000" hardware="true" vsync="false" />
<window width="400" height="450" fps="60" background="#000000" hardware="true" vsync="false" />

<!--HTML5-specific-->
<window if="html5" resizable="true" />
Expand All @@ -31,7 +31,9 @@

<set name="BUILD_DIR" value="export" />
<source path="source" />
<assets path="assets" />

<assets path="assets" exclude="*.ogg" if="flash"/>
<assets path="assets" exclude="*.mp3" unless="flash"/>

<!-- _______________________________ Libraries ______________________________ -->

Expand All @@ -42,7 +44,7 @@
<!--<haxelib name="flixel-addons" />-->

<!--In case you want to use the ui package-->
<!--<haxelib name="flixel-ui" />-->
<haxelib name="flixel-ui" />

<!--In case you want to use nape with flixel-->
<!--<haxelib name="nape-haxe4" />-->
Expand Down
Binary file added samples/basic/assets/beeper.mp3
Binary file not shown.
43 changes: 43 additions & 0 deletions samples/basic/source/InitState.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package;

import flixel.FlxG;
import flixel.FlxState;
import flixel.text.FlxText;
import flixel.util.FlxTimer;

class InitState extends FlxState
{
override function create():Void
{
#if js
// On JavaScript/HTML5 we have to get a user interaction first
// otherwise we cannot create an AudioContext and the app
// will crash.
var text:FlxText = new FlxText(0, 0, 0, "Click to start", 32);
text.screenCenter();
add(text);
#else
// If on a different target we can just proceed as usual.
FlxG.switchState(PlayState.new);
#end
}

var switching:Bool = false;
override function update(elapsed:Float):Void
{
super.update(elapsed);

// Check for user interaction & switch state if we get one.
#if js
if (FlxG.mouse.justPressed && !switching)
{
switching = true;

// Wait a small amount of time before switching to the state
// to give time for the audio context to start and avoid a crash
// See: https://github.com/ACrazyTown/flixel-waveform/issues/8#issuecomment-2585483164
FlxTimer.wait(0.1, () -> FlxG.switchState(PlayState.new));
}
#end
}
}
2 changes: 1 addition & 1 deletion samples/basic/source/Main.hx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ class Main extends Sprite
public function new()
{
super();
addChild(new FlxGame(0, 0, PlayState));
addChild(new FlxGame(0, 0, InitState));
}
}
156 changes: 126 additions & 30 deletions samples/basic/source/PlayState.hx
Original file line number Diff line number Diff line change
@@ -1,63 +1,85 @@
package;

import flixel.addons.ui.FlxUIDropDownMenu;
import flixel.addons.ui.FlxUINumericStepper;
import flixel.addons.ui.FlxUICheckBox;
import flixel.text.FlxText;
import flixel.addons.ui.FlxUI;
import flixel.addons.ui.FlxUIState;
import flixel.ui.FlxButton;
import flixel.FlxG;
import flixel.FlxState;
import flixel.addons.display.waveform.FlxWaveform;

class PlayState extends FlxState
class PlayState extends FlxUIState
{
var ui:FlxUI;
var playPauseBtn:FlxButton;

var waveform:FlxWaveform;
var pixelsPerMs:Int;

override public function create():Void
{
super.create();

// Setup some stuff
#if debug
FlxG.console.registerEnum(WaveformDrawMode);
#end
FlxG.autoPause = false;
FlxG.camera.bgColor = 0xFF152E5A;

FlxG.sound.playMusic("assets/beeper.ogg");
FlxG.sound.music.stop();
FlxG.sound.music.looped = true;
FlxG.sound.music = FlxG.sound.load("assets/beeper" + #if flash ".mp3" #else ".ogg" #end, 1.0, true);

// NOTE: Due to a limitation, on HTML5
// you have to play the audio source
// before trying to make a waveform from it.
// See: https://github.com/ACrazyTown/flixel-waveform/issues/8
FlxG.sound.music.play(true);

// Check if bitmap max texture size is available.
#if FLX_OPENGL_AVAILABLE
if (FlxG.bitmap.maxTextureSize != -1)
{
// Calculate how far can we stretch the waveform before hitting bitmap max limits?
pixelsPerMs = Math.ceil(FlxG.sound.music.length / FlxG.bitmap.maxTextureSize);
}
else
{
// In case we don't have a hardware accelerated renderer, we can't accurately
// get a maximum texture size, so let's just arbitrarily set it to a random number.
pixelsPerMs = 16;
}
#else
pixelsPerMs = 16;
#end

// Create a new FlxWaveform instance.
waveform = new FlxWaveform(0, 0, Std.int(FlxG.sound.music.length / pixelsPerMs), FlxG.height, 0xFF1C98E0);
waveform = new FlxWaveform(0, 50, FlxG.width, FlxG.height - 50);

// Load data from the FlxSound so the waveform renderer can process it.
waveform.loadDataFromFlxSound(FlxG.sound.music);
// We set our draw range.
// When we leave it blank it'll default to a range from the beginning to the full length of the sound.
waveform.setDrawRange();

// Set our waveform's starting time at 0ms.
waveform.waveformTime = 0;

// We want to visualize up to 5000ms (5s) ahead
waveform.waveformDuration = 5000;

// We'll render both channels of the waveform seperately.
waveform.waveformDrawMode = SPLIT_CHANNELS;

// We don't have to manually generate the bitmap here, because `FlxWaveform.autoUpdateBitmap`
// is true by default, and changing the waveform draw mode above will trigger a redraw.
// is true by default, and changing anything that visually affects the wavefrom will trigger a redraw.
// waveform.generateWaveformBitmap();

// Set the color of the waveform.
waveform.waveformColor = 0xFF4577BE;

// Set the color of the waveform's background.
waveform.waveformBgColor = 0xFF253475;

// Toggle whether the RMS (root mean square) of the waveform should be drawn.
// The RMS represents the average/effective loudness of audio.
waveform.waveformDrawRMS = true;

// Set the color of the RMS waveform.
waveform.waveformRMSColor = 0xFF68C3FF;

// Whether the waveform baseline (line in the middle representing 0.0 of the sample) should be drawn
waveform.waveformDrawBaseline = true;

// For extra style points we can adjust the size and spacing of the waveform bars!
// As a demonstration, we'll set the size to 4px and the padding to 2px
waveform.waveformBarSize = 4;
waveform.waveformBarPadding = 2;

add(waveform);

setupUI();
}

override public function update(elapsed:Float):Void
Expand All @@ -66,13 +88,87 @@ class PlayState extends FlxState

if (FlxG.sound.music.playing)
{
// Make our camera follow the audio time.
camera.scroll.x = FlxG.sound.music.time / pixelsPerMs;
// Set our waveform's time to the music's time, keeping them in sync.
waveform.waveformTime = FlxG.sound.music.time;
}

if (FlxG.keys.justPressed.SPACE)
playPause();
}

// --- Beyond this point is UI code you should not care about --

function setupUI():Void
{
ui = new FlxUI();
add(ui);

playPauseBtn = new FlxButton(5, 0, "Pause Music", playPause);
playPauseBtn.y = 5;
ui.add(playPauseBtn);

var drawRMSCheckbox:FlxUICheckBox = new FlxUICheckBox(10, 0, null, null, "Draw RMS");
drawRMSCheckbox.y = 30;
drawRMSCheckbox.checked = true;
drawRMSCheckbox.callback = () ->
{
waveform.waveformDrawRMS = !waveform.waveformDrawRMS;
};
ui.add(drawRMSCheckbox);

var paddingStepper:FlxUINumericStepper = new FlxUINumericStepper(drawRMSCheckbox.x + drawRMSCheckbox.width - 10, 0, 1, 0, 0, 100, 0);
paddingStepper.y = 10;
paddingStepper.value = waveform.waveformBarPadding;
paddingStepper.name = "s_padding";
ui.add(paddingStepper);
var paddingLabel:FlxText = new FlxText(0, 0, 0, "Bar Padding");
paddingLabel.x = paddingStepper.x + paddingStepper.width;
paddingLabel.y = paddingStepper.y;
ui.add(paddingLabel);

var sizeStepper:FlxUINumericStepper = new FlxUINumericStepper(drawRMSCheckbox.x + drawRMSCheckbox.width - 10, 0, 1, 1, 1, 100, 0);
sizeStepper.y = paddingStepper.y + 20;
sizeStepper.value = waveform.waveformBarSize;
sizeStepper.name = "s_size";
ui.add(sizeStepper);
var sizeLabel:FlxText = new FlxText(0, 0, 0, "Bar Size");
sizeLabel.x = sizeStepper.x + sizeStepper.width;
sizeLabel.y = sizeStepper.y;
ui.add(sizeLabel);

var drawModeLabel:FlxText = new FlxText(paddingLabel.x + 80, 5, 0, "Waveform Draw Mode");
ui.add(drawModeLabel);
var drawModeDropdown:FlxUIDropDownMenu = new FlxUIDropDownMenu(drawModeLabel.x, 20, FlxUIDropDownMenu.makeStrIdLabelArray(["Combined", "Split Channels", "Single Channel (Left)", "Single Channel (Right)"]), (select) ->
{
switch (select)
{
case "Combined": waveform.waveformDrawMode = COMBINED;
case "Split Channels": waveform.waveformDrawMode = SPLIT_CHANNELS;
case "Single Channel (Left)": waveform.waveformDrawMode = SINGLE_CHANNEL(0);
case "Single Channel (Right)": waveform.waveformDrawMode = SINGLE_CHANNEL(1);
}
});
drawModeDropdown.selectedLabel = "Split Channels";
ui.add(drawModeDropdown);
}

function playPause():Void
{
FlxG.sound.music.playing ? FlxG.sound.music.pause() : FlxG.sound.music.resume();
playPauseBtn.text = (FlxG.sound.music.playing ? "Pause" : "Play") + " Music";
}

override public function getEvent(id:String, sender:Dynamic, data:Dynamic, ?params:Array<Dynamic>):Void
{
super.getEvent(id, sender, data, params);

if (id == FlxUINumericStepper.CHANGE_EVENT && sender is FlxUINumericStepper)
{
FlxG.sound.music.playing ? FlxG.sound.music.pause() : FlxG.sound.music.resume();
var stepper:FlxUINumericStepper = cast sender;
if (stepper.name == "s_padding")
waveform.waveformBarPadding = Std.int(stepper.value);
else if (stepper.name == "s_size")
waveform.waveformBarSize = Std.int(stepper.value);
}
}
}
Loading