A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using System;
5using ManagedBass;
6using ManagedBass.Mix;
7using osu.Framework.Audio;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.Shapes;
10using osu.Framework.Graphics.Sprites;
11using osuTK;
12using osuTK.Graphics;
13
14namespace osu.Framework.Graphics.Visualisation.Audio
15{
16 public class AudioChannelDisplay : CompositeDrawable
17 {
18 private const int sample_window = 30;
19 private const int peak_hold_time = 3000;
20
21 public readonly int ChannelHandle;
22
23 private readonly Drawable volBarL;
24 private readonly Drawable volBarR;
25 private readonly SpriteText peakText;
26 private readonly SpriteText maxPeakText;
27
28 private float maxPeak = float.MinValue;
29 private double lastMaxPeakTime;
30
31 public AudioChannelDisplay(int channelHandle)
32 {
33 ChannelHandle = channelHandle;
34
35 RelativeSizeAxes = Axes.Y;
36 AutoSizeAxes = Axes.X;
37
38 InternalChild = new GridContainer
39 {
40 RelativeSizeAxes = Axes.Y,
41 AutoSizeAxes = Axes.X,
42 ColumnDimensions = new[]
43 {
44 new Dimension(GridSizeMode.AutoSize)
45 },
46 RowDimensions = new[]
47 {
48 new Dimension(),
49 new Dimension(GridSizeMode.AutoSize)
50 },
51 Content = new[]
52 {
53 new Drawable[]
54 {
55 new FillFlowContainer
56 {
57 RelativeSizeAxes = Axes.Y,
58 AutoSizeAxes = Axes.X,
59 Direction = FillDirection.Horizontal,
60 Spacing = new Vector2(5),
61 Children = new[]
62 {
63 volBarL = new Box
64 {
65 Anchor = Anchor.BottomLeft,
66 Origin = Anchor.BottomLeft,
67 RelativeSizeAxes = Axes.Y,
68 Width = 30,
69 Colour = Color4.YellowGreen
70 },
71 volBarR = new Box
72 {
73 Anchor = Anchor.BottomLeft,
74 Origin = Anchor.BottomLeft,
75 RelativeSizeAxes = Axes.Y,
76 Width = 30,
77 Colour = Color4.YellowGreen
78 }
79 }
80 }
81 },
82 new Drawable[]
83 {
84 new FillFlowContainer
85 {
86 AutoSizeAxes = Axes.Y,
87 RelativeSizeAxes = Axes.X,
88 Direction = FillDirection.Vertical,
89 Children = new Drawable[]
90 {
91 peakText = new SpriteText { Text = "N/A", Font = FrameworkFont.Condensed.With(size: 14f) },
92 maxPeakText = new SpriteText { Text = "N/A", Font = FrameworkFont.Condensed.With(size: 14f) },
93 }
94 }
95 }
96 }
97 };
98 }
99
100 protected override void Update()
101 {
102 base.Update();
103
104 float[] levels = new float[2];
105 BassMix.ChannelGetLevel(ChannelHandle, levels, 1 / 1000f * sample_window, LevelRetrievalFlags.Stereo);
106
107 var curPeakL = levels[0];
108 var curPeakR = levels[1];
109 var curPeak = (curPeakL + curPeakR) / 2f;
110
111 if (curPeak > maxPeak || Clock.CurrentTime - lastMaxPeakTime > peak_hold_time)
112 {
113 lastMaxPeakTime = Clock.CurrentTime;
114 maxPeak = float.MinValue;
115 }
116
117 maxPeak = Math.Max(maxPeak, curPeak);
118
119 volBarL.ResizeHeightTo(curPeakL, sample_window * 4);
120 volBarR.ResizeHeightTo(curPeakR, sample_window * 4);
121
122 var peakDisplay = curPeak == 0 ? "-∞ " : $"{BassUtils.LevelToDb(curPeak):F}";
123 var maxPeakDisplay = maxPeak == 0 ? "-∞ " : $"{BassUtils.LevelToDb(maxPeak):F}";
124 peakText.Text = $"curr: {peakDisplay}dB";
125 maxPeakText.Text = $"peak: {maxPeakDisplay}dB";
126 peakText.Colour = BassUtils.LevelToDb(curPeak) > 0 ? Colour4.Red : Colour4.White;
127 maxPeakText.Colour = BassUtils.LevelToDb(maxPeak) > 0 ? Colour4.Red : Colour4.White;
128 }
129 }
130}