5
// Chris Howie <cdhowie@gmail.com>
6
// Nicholas Parker <nickbp@gmail.com>
8
// Copyright (c) 2009 Chris Howie
9
// Copyright (c) 2011 Nicholas Parker
11
// Permission is hereby granted, free of charge, to any person obtaining a copy
12
// of this software and associated documentation files (the "Software"), to deal
13
// in the Software without restriction, including without limitation the rights
14
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
// copies of the Software, and to permit persons to whom the Software is
16
// furnished to do so, subject to the following conditions:
18
// The above copyright notice and this permission notice shall be included in
19
// all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
36
namespace Banshee.OpenVP.Visualizations
38
// Horizontal, right -> left
39
public class SpectrumScan : IRenderer, IDisposable
41
// what percent of the screen's width should the analyzer take up
42
private const double analyzerWidthPct = 0.15,
43
analyzerWidthOrtho = 2 / analyzerWidthPct;
45
private const double scale = 0.5;
46
private int bufferLength = -1, voiceprintWidthPx, viewHeightPx = -1;
47
private float[] spectrumBuffer, smoothBuffer,
48
bufferScaling;//for each index in *Buffer, how wide (in px) that index should be
50
private TextureHandle voiceprintBuffer;
56
private void GetSpectrum(IController controller)
58
//initialize/populate things that depend on spectrum length
60
if (controller.PlayerData.NativeSpectrumLength != bufferLength) {
61
bufferLength = controller.PlayerData.NativeSpectrumLength;
63
spectrumBuffer = new float[bufferLength];
64
smoothBuffer = new float[bufferLength];
65
bufferScaling = new float[bufferLength];
69
controller.PlayerData.GetSpectrum(spectrumBuffer);
71
for (int i = 0; i < bufferLength; i++) {
72
// go with a linear decrease - avoids appearance of
73
// disconnectedness between analyzer and voiceprint, without
74
// making analyzer look too jittery
76
Math.Max(spectrumBuffer[i], smoothBuffer[i] - 0.05f);
79
//initialize/populate things that depend on view size
80
voiceprintWidthPx = (int)(controller.Width * (1 - analyzerWidthPct));
81
if (viewHeightPx != controller.Height) {
82
viewHeightPx = controller.Height;
87
//formula: pxlen = (dataLen - dataI)^scale / dataLen^scale
88
//integrate over dataI from 0 to dataLen: sum(pxlen) = dataLen / (scale + 1)
89
//scaled formula: pxlen = (dataLen - dataI)^scale * viewLen * (scale + 1) / dataLen^(scale + 1)
90
double multiplier = viewHeightPx * (scale + 1) / Math.Pow(bufferLength, scale + 1);
91
for (int i = 0; i < bufferLength; ++i) {
92
bufferScaling[i] = (float)(Math.Pow(bufferLength-i, scale) * multiplier);
97
public void Render(IController controller)
99
GetSpectrum(controller);
101
Gl.glPushAttrib(Gl.GL_ENABLE_BIT);
102
Gl.glDisable(Gl.GL_DEPTH_TEST);
103
Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_DECAL);
107
Gl.glMatrixMode(Gl.GL_PROJECTION);
111
Glu.gluOrtho2D(0, controller.Width, 0, controller.Height);
113
Gl.glMatrixMode(Gl.GL_TEXTURE);
117
Gl.glScalef(1f / controller.Width, 1f / controller.Height, 1f);
119
if (voiceprintBuffer == null) {
120
voiceprintBuffer = new TextureHandle(controller.Width, controller.Height);
122
voiceprintBuffer.SetTextureSize(controller.Width, controller.Height);
125
Gl.glBindTexture(Gl.GL_TEXTURE_2D, voiceprintBuffer.TextureId);
126
// OPTIMIZATION: could just copy texture of width voiceprintWidthPx
127
// but then the coordinates get tricky
128
Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, 0, 0,
129
controller.Width, controller.Height, 0);
132
Gl.glClearColor(0, 0, 0, 1);
133
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
135
// paste voiceprint region of copied texture, shifted left 1px
136
Gl.glEnable(Gl.GL_TEXTURE_2D);
137
Gl.glBegin(Gl.GL_QUADS);
139
Gl.glTexCoord2f(1, 0);
142
Gl.glTexCoord2f(1, controller.Height);
143
Gl.glVertex2f(0, controller.Height);
145
Gl.glTexCoord2f(voiceprintWidthPx, controller.Height);
146
Gl.glVertex2f(voiceprintWidthPx - 1, controller.Height);
148
Gl.glTexCoord2f(voiceprintWidthPx, 0);
149
Gl.glVertex2f(voiceprintWidthPx - 1, 0);
152
Gl.glDisable(Gl.GL_TEXTURE_2D);
154
Gl.glBegin(Gl.GL_QUADS);
155
// instead of using a smoothed buffer like with the analyzer,
156
// use the direct buffer for nicer looking output (less blurry)
158
for (int i = 0; i < bufferLength; i++) {
159
float v = spectrumBuffer[i];
160
Color.FromHSL(120 * (1 - v), 1, Math.Min(0.5f, v)).Use();
162
// draw a little beyond the edge to ensure no visible seams
163
Gl.glVertex2f(voiceprintWidthPx-1, y);
164
Gl.glVertex2f(voiceprintWidthPx+1, y);
165
y += bufferScaling[i];
166
Gl.glVertex2f(voiceprintWidthPx+1, y);
167
Gl.glVertex2f(voiceprintWidthPx-1, y);
171
Gl.glMatrixMode(Gl.GL_TEXTURE);
174
Gl.glMatrixMode(Gl.GL_PROJECTION);
179
Gl.glMatrixMode(Gl.GL_PROJECTION);
183
// analyzer at right side of screen
184
Glu.gluOrtho2D(1 - analyzerWidthOrtho, 1, -1, 1);
186
RenderAnalyzer();// coordinates: [-1,+1] in x,y
188
Gl.glMatrixMode(Gl.GL_PROJECTION);
194
private void RenderAnalyzer()
196
Gl.glBegin(Gl.GL_QUADS);
198
// instead of directly using the raw buffer like with voiceprint,
199
// use a smoothed buffer for nicer looking output (less jittery)
201
float analyzerConv = 2f / viewHeightPx;//px -> [-1,1]
202
for (int i = 0; i < bufferLength; i++) {
203
float v = smoothBuffer[i];
204
Color.FromHSL(120 * (1 - v), 1, Math.Min(0.5f, v)).Use();
205
//Color.FromHSL(120 * (1 - v), 1, 0.5f).Use();
207
Gl.glVertex2f(-1, y);
208
Gl.glVertex2f(2*v - 1, y);
209
y += analyzerConv * bufferScaling[i];
210
Gl.glVertex2f(2*v - 1, y);
211
Gl.glVertex2f(-1, y);
217
public void Dispose()
219
if (voiceprintBuffer != null) {
220
voiceprintBuffer.Dispose();
221
voiceprintBuffer = null;
227
// Vertical, top -> bottom
228
public class SpectrumRain : IRenderer, IDisposable
230
// what percent of the screen's height should the analyzer take up
231
private const double analyzerHeightPct = 0.25,
232
analyzerHeightOrtho = 2 / analyzerHeightPct;
234
private const double scale = 0.5;
235
private int bufferLength = -1, voiceprintHeightPx, viewWidthPx = -1;
236
private float[] spectrumBuffer, smoothBuffer,
237
bufferScaling;//for each index in *Buffer, how wide (in px) that index should be
239
private TextureHandle voiceprintBuffer;
241
public SpectrumRain()
245
private void GetSpectrum(IController controller)
247
//initialize/populate things that depend on spectrum length
248
bool rescale = false;
249
if (controller.PlayerData.NativeSpectrumLength != bufferLength) {
250
bufferLength = controller.PlayerData.NativeSpectrumLength;
252
spectrumBuffer = new float[bufferLength];
253
smoothBuffer = new float[bufferLength];
254
bufferScaling = new float[bufferLength];
258
controller.PlayerData.GetSpectrum(spectrumBuffer);
260
for (int i = 0; i < bufferLength; i++) {
261
// go with a linear decrease - avoids appearance of
262
// disconnectedness between analyzer and voiceprint, without
263
// making analyzer look too jittery
265
Math.Max(spectrumBuffer[i], smoothBuffer[i] - 0.05f);
268
voiceprintHeightPx = (int)(controller.Height * (1 - analyzerHeightPct));
269
if (viewWidthPx != controller.Width) {
270
viewWidthPx = controller.Width;
275
//formula: pxlen = (dataLen - dataI)^scale / dataLen^scale
276
//integrate over dataI from 0 to dataLen: sum(pxlen) = dataLen / (scale + 1)
277
//scaled formula: pxlen = (dataLen - dataI)^scale * viewLen * (scale + 1) / dataLen^(scale + 1)
278
double multiplier = viewWidthPx * (scale + 1) / Math.Pow(bufferLength, scale + 1);
279
for (int i = 0; i < bufferLength; ++i) {
280
bufferScaling[i] = (float)(Math.Pow(bufferLength-i, scale) * multiplier);
285
public void Render(IController controller)
287
GetSpectrum(controller);
289
Gl.glPushAttrib(Gl.GL_ENABLE_BIT);
290
Gl.glDisable(Gl.GL_DEPTH_TEST);
291
Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_DECAL);
295
Gl.glMatrixMode(Gl.GL_PROJECTION);
299
Glu.gluOrtho2D(0, controller.Width, 0, controller.Height);
301
Gl.glMatrixMode(Gl.GL_TEXTURE);
305
Gl.glScalef(1f / controller.Width, 1f / controller.Height, 1f);
307
if (voiceprintBuffer == null) {
308
voiceprintBuffer = new TextureHandle(controller.Width, controller.Height);
310
voiceprintBuffer.SetTextureSize(controller.Width, controller.Height);
313
Gl.glBindTexture(Gl.GL_TEXTURE_2D, voiceprintBuffer.TextureId);
314
// OPTIMIZATION: could just copy texture of width voiceprintHeightPx
315
// but then the coordinates get tricky
316
Gl.glCopyTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGB, 0, 0,
317
controller.Width, controller.Height, 0);
320
Gl.glClearColor(0, 0, 0, 1);
321
Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
323
// paste voiceprint region of copied texture, shifted down 1px
324
Gl.glEnable(Gl.GL_TEXTURE_2D);
325
Gl.glBegin(Gl.GL_QUADS);
327
Gl.glTexCoord2f(0, 1);
330
Gl.glTexCoord2f(0, voiceprintHeightPx);
331
Gl.glVertex2f(0, voiceprintHeightPx - 1);
333
Gl.glTexCoord2f(controller.Width, voiceprintHeightPx);
334
Gl.glVertex2f(controller.Width, voiceprintHeightPx - 1);
336
Gl.glTexCoord2f(controller.Width, 1);
337
Gl.glVertex2f(controller.Width, 0);
340
Gl.glDisable(Gl.GL_TEXTURE_2D);
342
Gl.glBegin(Gl.GL_QUADS);
343
// instead of using a smoothed buffer like with the analyzer,
344
// use the direct buffer for nicer looking output (less blurry)
346
for (int i = 0; i < bufferLength; i++) {
347
float v = spectrumBuffer[i];
348
Color.FromHSL(120 * (1 - v), 1, Math.Min(0.5f, v)).Use();
350
// draw a little beyond the edge to ensure no visible seams
351
Gl.glVertex2f(x, voiceprintHeightPx-1);
352
Gl.glVertex2f(x, voiceprintHeightPx+1);
353
x += bufferScaling[i];
354
Gl.glVertex2f(x, voiceprintHeightPx+1);
355
Gl.glVertex2f(x, voiceprintHeightPx-1);
359
Gl.glMatrixMode(Gl.GL_TEXTURE);
362
Gl.glMatrixMode(Gl.GL_PROJECTION);
367
Gl.glMatrixMode(Gl.GL_PROJECTION);
371
// analyzer at top side of screen
372
Glu.gluOrtho2D(-1, 1, 1 - analyzerHeightOrtho, 1);
374
RenderAnalyzer();// coordinates: [-1,+1] in x,y
376
Gl.glMatrixMode(Gl.GL_PROJECTION);
382
private void RenderAnalyzer()
384
Gl.glBegin(Gl.GL_QUADS);
386
// instead of directly using the raw buffer like with voiceprint,
387
// use a smoothed buffer for nicer looking output (less jittery)
389
float analyzerConv = 2f / viewWidthPx;//px -> [-1,1]
390
for (int i = 0; i < bufferLength; i++) {
391
float v = smoothBuffer[i];
392
Color.FromHSL(120 * (1 - v), 1, Math.Min(0.5f, v)).Use();
393
//Color.FromHSL(120 * (1 - v), 1, 0.5f).Use();
395
Gl.glVertex2f(x, -1);
396
Gl.glVertex2f(x, 2*v - 1);
397
x += analyzerConv * bufferScaling[i];
398
Gl.glVertex2f(x, 2*v - 1);
399
Gl.glVertex2f(x, -1);
405
public void Dispose()
407
if (voiceprintBuffer != null) {
408
voiceprintBuffer.Dispose();
409
voiceprintBuffer = null;