~ubuntu-branches/debian/jessie/banshee-community-extensions/jessie

« back to all changes in this revision

Viewing changes to src/OpenVP/Banshee.OpenVP.Visualizations/SpectrumScan.cs

  • Committer: Package Import Robot
  • Author(s): Chow Loong Jin
  • Date: 2011-09-20 18:45:46 UTC
  • mfrom: (1.2.9 upstream) (5.1.8 experimental)
  • Revision ID: package-import@ubuntu.com-20110920184546-3ahue2qplydc4t0e
Tags: 2.2.0-1
* [4940fab] Imported Upstream version 2.2.0
  + Notable bug fixes:
    - Karaoke: Fix crash when switching to Now Playing
    - Lyrics: Fix crash when switching to Now Playing

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// SpectrumScan.cs
 
3
//
 
4
// Author:
 
5
//       Chris Howie <cdhowie@gmail.com>
 
6
//       Nicholas Parker <nickbp@gmail.com>
 
7
//
 
8
// Copyright (c) 2009 Chris Howie
 
9
// Copyright (c) 2011 Nicholas Parker
 
10
//
 
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:
 
17
//
 
18
// The above copyright notice and this permission notice shall be included in
 
19
// all copies or substantial portions of the Software.
 
20
//
 
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
 
27
// THE SOFTWARE.
 
28
 
 
29
using System;
 
30
 
 
31
using OpenVP;
 
32
using OpenVP.Core;
 
33
 
 
34
using Tao.OpenGl;
 
35
 
 
36
namespace Banshee.OpenVP.Visualizations
 
37
{
 
38
    // Horizontal, right -> left
 
39
    public class SpectrumScan : IRenderer, IDisposable
 
40
    {
 
41
        // what percent of the screen's width should the analyzer take up
 
42
        private const double analyzerWidthPct = 0.15,
 
43
            analyzerWidthOrtho = 2 / analyzerWidthPct;
 
44
 
 
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
 
49
 
 
50
        private TextureHandle voiceprintBuffer;
 
51
 
 
52
        public SpectrumScan()
 
53
        {
 
54
        }
 
55
 
 
56
        private void GetSpectrum(IController controller)
 
57
        {
 
58
            //initialize/populate things that depend on spectrum length
 
59
            bool rescale = false;
 
60
            if (controller.PlayerData.NativeSpectrumLength != bufferLength) {
 
61
                bufferLength = controller.PlayerData.NativeSpectrumLength;
 
62
 
 
63
                spectrumBuffer = new float[bufferLength];
 
64
                smoothBuffer = new float[bufferLength];
 
65
                bufferScaling = new float[bufferLength];
 
66
                rescale = true;
 
67
            }
 
68
 
 
69
            controller.PlayerData.GetSpectrum(spectrumBuffer);
 
70
 
 
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
 
75
                smoothBuffer[i] =
 
76
                    Math.Max(spectrumBuffer[i], smoothBuffer[i] - 0.05f);
 
77
            }
 
78
 
 
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;
 
83
                rescale = true;
 
84
            }
 
85
 
 
86
            if (rescale) {
 
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);
 
93
                }
 
94
            }
 
95
        }
 
96
 
 
97
        public void Render(IController controller)
 
98
        {
 
99
            GetSpectrum(controller);
 
100
 
 
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);
 
104
 
 
105
            // voiceprint
 
106
 
 
107
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
108
            Gl.glPushMatrix();
 
109
            Gl.glLoadIdentity();
 
110
 
 
111
            Glu.gluOrtho2D(0, controller.Width, 0, controller.Height);
 
112
 
 
113
            Gl.glMatrixMode(Gl.GL_TEXTURE);
 
114
            Gl.glPushMatrix();
 
115
            Gl.glLoadIdentity();
 
116
 
 
117
            Gl.glScalef(1f / controller.Width, 1f / controller.Height, 1f);
 
118
 
 
119
            if (voiceprintBuffer == null) {
 
120
                voiceprintBuffer = new TextureHandle(controller.Width, controller.Height);
 
121
            } else {
 
122
                voiceprintBuffer.SetTextureSize(controller.Width, controller.Height);
 
123
            }
 
124
 
 
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);
 
130
 
 
131
            // clean palette
 
132
            Gl.glClearColor(0, 0, 0, 1);
 
133
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
 
134
 
 
135
            // paste voiceprint region of copied texture, shifted left 1px
 
136
            Gl.glEnable(Gl.GL_TEXTURE_2D);
 
137
            Gl.glBegin(Gl.GL_QUADS);
 
138
 
 
139
            Gl.glTexCoord2f(1, 0);
 
140
            Gl.glVertex2f(0, 0);
 
141
 
 
142
            Gl.glTexCoord2f(1, controller.Height);
 
143
            Gl.glVertex2f(0, controller.Height);
 
144
 
 
145
            Gl.glTexCoord2f(voiceprintWidthPx, controller.Height);
 
146
            Gl.glVertex2f(voiceprintWidthPx - 1, controller.Height);
 
147
 
 
148
            Gl.glTexCoord2f(voiceprintWidthPx, 0);
 
149
            Gl.glVertex2f(voiceprintWidthPx - 1, 0);
 
150
 
 
151
            Gl.glEnd();
 
152
            Gl.glDisable(Gl.GL_TEXTURE_2D);
 
153
 
 
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)
 
157
            float y = 0;
 
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();
 
161
 
 
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);
 
168
            }
 
169
            Gl.glEnd();
 
170
 
 
171
            Gl.glMatrixMode(Gl.GL_TEXTURE);
 
172
            Gl.glPopMatrix();
 
173
 
 
174
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
175
            Gl.glPopMatrix();
 
176
 
 
177
            // analyzer
 
178
 
 
179
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
180
            Gl.glPushMatrix();
 
181
            Gl.glLoadIdentity();
 
182
 
 
183
            // analyzer at right side of screen
 
184
            Glu.gluOrtho2D(1 - analyzerWidthOrtho, 1, -1, 1);
 
185
 
 
186
            RenderAnalyzer();// coordinates: [-1,+1] in x,y
 
187
 
 
188
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
189
            Gl.glPopMatrix();
 
190
 
 
191
            Gl.glPopAttrib();
 
192
        }
 
193
 
 
194
        private void RenderAnalyzer()
 
195
        {
 
196
            Gl.glBegin(Gl.GL_QUADS);
 
197
 
 
198
            // instead of directly using the raw buffer like with voiceprint,
 
199
            // use a smoothed buffer for nicer looking output (less jittery)
 
200
            float y = -1;
 
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();
 
206
 
 
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);
 
212
            }
 
213
 
 
214
            Gl.glEnd();
 
215
        }
 
216
 
 
217
        public void Dispose()
 
218
        {
 
219
            if (voiceprintBuffer != null) {
 
220
                voiceprintBuffer.Dispose();
 
221
                voiceprintBuffer = null;
 
222
            }
 
223
        }
 
224
    }
 
225
 
 
226
 
 
227
    // Vertical, top -> bottom
 
228
    public class SpectrumRain : IRenderer, IDisposable
 
229
    {
 
230
        // what percent of the screen's height should the analyzer take up
 
231
        private const double analyzerHeightPct = 0.25,
 
232
            analyzerHeightOrtho = 2 / analyzerHeightPct;
 
233
 
 
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
 
238
 
 
239
        private TextureHandle voiceprintBuffer;
 
240
 
 
241
        public SpectrumRain()
 
242
        {
 
243
        }
 
244
 
 
245
        private void GetSpectrum(IController controller)
 
246
        {
 
247
            //initialize/populate things that depend on spectrum length
 
248
            bool rescale = false;
 
249
            if (controller.PlayerData.NativeSpectrumLength != bufferLength) {
 
250
                bufferLength = controller.PlayerData.NativeSpectrumLength;
 
251
 
 
252
                spectrumBuffer = new float[bufferLength];
 
253
                smoothBuffer = new float[bufferLength];
 
254
                bufferScaling = new float[bufferLength];
 
255
                rescale = true;
 
256
            }
 
257
 
 
258
            controller.PlayerData.GetSpectrum(spectrumBuffer);
 
259
 
 
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
 
264
                smoothBuffer[i] =
 
265
                    Math.Max(spectrumBuffer[i], smoothBuffer[i] - 0.05f);
 
266
            }
 
267
 
 
268
            voiceprintHeightPx = (int)(controller.Height * (1 - analyzerHeightPct));
 
269
            if (viewWidthPx != controller.Width) {
 
270
                viewWidthPx = controller.Width;
 
271
                rescale = true;
 
272
            }
 
273
 
 
274
            if (rescale) {
 
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);
 
281
                }
 
282
            }
 
283
        }
 
284
 
 
285
        public void Render(IController controller)
 
286
        {
 
287
            GetSpectrum(controller);
 
288
 
 
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);
 
292
 
 
293
            // voiceprint
 
294
 
 
295
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
296
            Gl.glPushMatrix();
 
297
            Gl.glLoadIdentity();
 
298
 
 
299
            Glu.gluOrtho2D(0, controller.Width, 0, controller.Height);
 
300
 
 
301
            Gl.glMatrixMode(Gl.GL_TEXTURE);
 
302
            Gl.glPushMatrix();
 
303
            Gl.glLoadIdentity();
 
304
 
 
305
            Gl.glScalef(1f / controller.Width, 1f / controller.Height, 1f);
 
306
 
 
307
            if (voiceprintBuffer == null) {
 
308
                voiceprintBuffer = new TextureHandle(controller.Width, controller.Height);
 
309
            } else {
 
310
                voiceprintBuffer.SetTextureSize(controller.Width, controller.Height);
 
311
            }
 
312
 
 
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);
 
318
 
 
319
            // clean palette
 
320
            Gl.glClearColor(0, 0, 0, 1);
 
321
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT);
 
322
 
 
323
            // paste voiceprint region of copied texture, shifted down 1px
 
324
            Gl.glEnable(Gl.GL_TEXTURE_2D);
 
325
            Gl.glBegin(Gl.GL_QUADS);
 
326
 
 
327
            Gl.glTexCoord2f(0, 1);
 
328
            Gl.glVertex2f(0, 0);
 
329
 
 
330
            Gl.glTexCoord2f(0, voiceprintHeightPx);
 
331
            Gl.glVertex2f(0, voiceprintHeightPx - 1);
 
332
 
 
333
            Gl.glTexCoord2f(controller.Width, voiceprintHeightPx);
 
334
            Gl.glVertex2f(controller.Width, voiceprintHeightPx - 1);
 
335
 
 
336
            Gl.glTexCoord2f(controller.Width, 1);
 
337
            Gl.glVertex2f(controller.Width, 0);
 
338
 
 
339
            Gl.glEnd();
 
340
            Gl.glDisable(Gl.GL_TEXTURE_2D);
 
341
 
 
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)
 
345
            float x = 0;
 
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();
 
349
 
 
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);
 
356
            }
 
357
            Gl.glEnd();
 
358
 
 
359
            Gl.glMatrixMode(Gl.GL_TEXTURE);
 
360
            Gl.glPopMatrix();
 
361
 
 
362
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
363
            Gl.glPopMatrix();
 
364
 
 
365
            // analyzer
 
366
 
 
367
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
368
            Gl.glPushMatrix();
 
369
            Gl.glLoadIdentity();
 
370
 
 
371
            // analyzer at top side of screen
 
372
            Glu.gluOrtho2D(-1, 1, 1 - analyzerHeightOrtho, 1);
 
373
 
 
374
            RenderAnalyzer();// coordinates: [-1,+1] in x,y
 
375
 
 
376
            Gl.glMatrixMode(Gl.GL_PROJECTION);
 
377
            Gl.glPopMatrix();
 
378
 
 
379
            Gl.glPopAttrib();
 
380
        }
 
381
 
 
382
        private void RenderAnalyzer()
 
383
        {
 
384
            Gl.glBegin(Gl.GL_QUADS);
 
385
 
 
386
            // instead of directly using the raw buffer like with voiceprint,
 
387
            // use a smoothed buffer for nicer looking output (less jittery)
 
388
            float x = -1;
 
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();
 
394
 
 
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);
 
400
            }
 
401
 
 
402
            Gl.glEnd();
 
403
        }
 
404
 
 
405
        public void Dispose()
 
406
        {
 
407
            if (voiceprintBuffer != null) {
 
408
                voiceprintBuffer.Dispose();
 
409
                voiceprintBuffer = null;
 
410
            }
 
411
        }
 
412
    }
 
413
}