File: AudioDisplayVolume.cpp

package info (click to toggle)
camstream 0.27%2Bdfsg-3
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 12,320 kB
  • ctags: 5,393
  • sloc: cpp: 17,031; sh: 8,154; asm: 455; ansic: 440; makefile: 343
file content (357 lines) | stat: -rw-r--r-- 9,002 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <malloc.h>
#include <sys/types.h>
//#include <unistd.h>

#include <qapplication.h>
#include <qdatetime.h>
#include <qsize.h>

#include "AudioDisplayVolume.h"


/**
  \class CAudioDisplayVolume

  This class display a bunch of LedBars that show the volume(s) of
  a device or stream, read through a CAudioRingBufferReader. It uses
  a thread to minimize delays and be independant of other readers

  Note: the CAudioRingBufferReader supplied with this object is destroyed
  when this is deleted.
*/

/**
  \brief Constructor

  This is the contructor of the volume LED bar display.
  Do not call this function with qApp->lock()ed!
*/

CAudioDisplayVolume::CAudioDisplayVolume(QWidget *parent, const char *name)
	: QWidget(parent, name)
{
qDebug("CAudioDisplayVolume::CAudioDisplayVolume()");
   setBackgroundMode(QWidget::NoBackground); // do no repaint background, since we are completely covered by the bars
   m_Bars.setAutoDelete(true);

   m_pReader = 0;
   m_DisplayMode = SkyLine;
   m_Done = false;
   m_Samples = 1024;
   m_MaxShift = 0;
   m_Length = 100;
   resize(sizeHint());
}


CAudioDisplayVolume::CAudioDisplayVolume(CAudioRingBufferReader *reader, DisplayMode mode, QWidget *parent, const char *name)
	: QWidget(parent, name)
{
qDebug("CAudioDisplayVolume::CAudioDisplayVolume(*reader)");
   setBackgroundMode(QWidget::NoBackground); // do no repaint background, since we are completely covered by the bars
   m_Bars.setAutoDelete(true);

   m_pReader = reader;
   m_DisplayMode = mode;
   m_Done = false;
   m_SndAttr = reader->GetSoundAttributes();
   m_Samples = 1024;
   m_MaxShift = 0;
   m_Length = 100;

   SetSoundAttributes(m_SndAttr);
   resize(sizeHint());
}

CAudioDisplayVolume::~CAudioDisplayVolume()
{
qDebug("CAudioDisplayVolume::~CAudioDisplayVolume()");
   delete m_pReader;
   m_pReader = 0;
}

// private

void CAudioDisplayVolume::RecalculateSizes()
{
   int i;

   m_Mutex.lock();
   switch (m_DisplayMode)
   {
     case SkyLine:
       m_SizeHint.setWidth(16 * m_SndAttr.Channels);
       m_SizeHint.setHeight(m_Length);
       break;

     case Stereo:
       m_SizeHint.setWidth(m_Length);
       m_SizeHint.setHeight(16);
       break;

     case Stack:
       m_SizeHint.setWidth(m_Length);
       m_SizeHint.setHeight(16 * m_SndAttr.Channels);
       break;
   }

   for (i = 0; i < m_SndAttr.Channels; i++)
   {
      int s1, s2;
      CLedBar *lb = m_Bars[i];

      if (lb == 0)
        continue;
      switch (m_DisplayMode)
      {
         case SkyLine:
           lb->move(16 * i, 0);
           lb->resize(16, m_Length);
           break;
         case Stereo:
           s1 =  i      * m_Length / m_SndAttr.Channels;
           s2 = (i + 1) * m_Length / m_SndAttr.Channels;
           lb->move(s1, 0);
           lb->resize(s2 - s1, 16);
           break;
         case Stack:
           lb->move(0, 16 * i);
           lb->resize(m_Length, 16);
           break;
      }
   }
   m_Mutex.unlock();
   updateGeometry();
}


// protected

void CAudioDisplayVolume::run()
{
qDebug(">> CAudioDisplayVolume::run() started.");
   int v, t, fall, FallRem = 0;
   QTime Elaps;
   CAudioSample Piece;
   SamplePos len, i;

   m_pReader->SetLowWaterMark(m_Samples);
   Elaps.start();
   while (!m_Done) {
      Piece = m_pReader->ReadFromHead(m_Samples, true, 1000);
      if (Piece.IsNull()) {
        qDebug("CAudioDisplayVolume: got empty sample.");
        continue;
      }
      if (m_Done)
        break;

      m_Mutex.lock(); // prevent messing with, for example, m_SndAttr.Channels :-P
      // Interpret data
      for (t = 0; t < m_SndAttr.Channels; t++)
         m_Max[t] = 0;

      len = Piece.TotalLength();
      for (i = 0; i < len; i++) {
         for (t = 0; t < m_SndAttr.Channels; t++) {
            v = Piece.GetValue(i, m_SndAttr.ChannelPosition[t]);

            if (v > m_Max[t])
              m_Max[t] = v;
            if (-v > m_Max[t])
              m_Max[t] = -v;
         }
      }

      // Normalize
      for (t = 0; t < m_SndAttr.Channels; t++)
      {
         m_Max[t] >>= m_MaxShift;
      }

      /* Fallback of LED bars is governed by real, elapsed time */
      fall = FallRem + Elaps.restart();
      FallRem = fall % 16; // divide to prevent a very quick drop, but don't discard the remainder,
      fall = fall / 16;    // because in a tight loop (< 16 ms) you might end up with a divisor of 0
      for (t = 0; t < m_SndAttr.Channels; t++) {
         if (fall < 0) fall = 0; // 24 hour wrap safeguard
         m_OldMax[t] -= fall;
         if (m_OldMax[t] < 0)
           m_OldMax[t] = 0;
         if (m_Max[t] > m_OldMax[t])
           m_OldMax[t] = m_Max[t];
      }
      //qApp->lock();
      for (t = 0; t < m_SndAttr.Channels; t++)
      {
         m_Bars[t]->setValue(m_OldMax[t]);
      }
      //qApp->unlock(); // this unlock is mainly here to wake up the GUI thread (see Qt-docs)
      m_Mutex.unlock();
      qApp->wakeUpGuiThread();
      //update();
   }
qDebug("<< CAudioDisplayVolume::run() ended.");
   delete m_pReader;
   m_pReader = 0;
}

void CAudioDisplayVolume::paintEvent(QPaintEvent *ev)
{
    qApp->lock();
    for (int t = 0; t < m_SndAttr.Channels; t++)
       m_Bars[t]->repaint(false);
    qApp->unlock();
}

void CAudioDisplayVolume::resizeEvent(QResizeEvent *ev)
{
qDebug(">> CAudioDisplayVolume::resizeEvent");
   switch (m_DisplayMode)
   {
     case SkyLine:
       SetLength(ev->size().height());
       break;
     case Stereo:
     case Stack:
       SetLength(ev->size().width());
       break;
   }
qDebug("<< CAudioDisplayVolume::resizeEvent");
}


// public

void CAudioDisplayVolume::SetMode(DisplayMode mode)
{
   m_DisplayMode = mode;
   RecalculateSizes(); // does mutex-lock
}


/**
  \brief Set new reader



*/
void CAudioDisplayVolume::SetReader(CAudioRingBufferReader *reader)
{
  if (running())
  {
    Quit();
  }
  m_pReader = reader;
  if (m_pReader != 0)
  {
    m_SndAttr = reader->GetSoundAttributes();
    SetSoundAttributes(m_SndAttr);
    start();
  }
}



/**
  \brief Set width or height, depending on the \ref DisplayMode
  \param length In pixels

  The widget can determine its width or height, depending on the number of
  bars that need to be displayed. However, the length of these bars cannot
  be determined, thus must be set by the calling application.

  For SkyLine mode, this sets the height; for Stereo, the total width
  of the widget. For Stack, the width of the widget and thus all the bars.
*/
void CAudioDisplayVolume::SetLength(int length)
{
qDebug(">> CAudioDisplayVolume::SetLength(%d)", length);
   if (length != m_Length)
   {
     m_Length = length;
     RecalculateSizes();
     resize(m_SizeHint);
   }
qDebug("<< CAudioDisplayVolume::SetLength()");
}


QSize CAudioDisplayVolume::sizeHint()
{
   return m_SizeHint;
}

/*
  \brief Stop this thread
  \param Wait When true, wait for thread to finish

  When Wait = false, you have to manually wait() for the thread to finish.
*/
void CAudioDisplayVolume::Quit(bool Wait)
{
qDebug("CAudioDisplayVolume::Quit() called.");
   m_Done = true;
   if (Wait)
   {
     wait();
   }
}

// public slots

void CAudioDisplayVolume::SetSoundAttributes(const SoundAttributes &attr)
{
   unsigned int i;
   QColor orange = QColor(255, 200, 0); // Better for color-blind people
   QColor LedBarColors[10] = { green, green, green, green, green,
                               green, green, orange, orange, red };
   CLedBar *lb = 0;

   m_Mutex.lock();
   qApp->lock();
   m_Bars.resize(0); // delete all current bars
   m_Bars.resize(m_SndAttr.Channels);
   m_OldMax.resize(m_SndAttr.Channels);
   m_Max.resize(m_SndAttr.Channels);
   // Use shift to divide the values we read in the run() loop
   // and normalize them to 128, which we initialize the LED bars with.
   // The max is 128, not 256, because 8 bits sound samples from -128...127
   m_MaxShift = m_SndAttr.FormatWidth() - 8;
   if (m_MaxShift < 0)
     m_MaxShift = 0; // should not happen, but you never know...

   for (i = 0; i < m_SndAttr.Channels; i++) {
      switch (m_DisplayMode)
      {
        case SkyLine:
          lb = new CLedBar(CLedBar::North, 0, 128, CLedBar::SegmentBar, this,  "volume bar");
          break;

        case Stereo:
          if (i & 1) // odd
            lb = new CLedBar(CLedBar::East, 0, 128, CLedBar::SegmentBar, this, "volume bar");
          else
            lb = new CLedBar(CLedBar::West, 0, 128, CLedBar::SegmentBar, this, "volume bar");
          break;

        case Stack:
          lb = new CLedBar(CLedBar::East, 0, 128, CLedBar::SegmentBar, this, "volume bar");
          break;
      }
      // The bars are moved to the correct position and length in RecalculateSizes
      //lb->move(0, 16 * t);
      //lb->resize(256, 16);
      lb->SetSegments(10, LedBarColors);
      lb->SetMargin(2);
      m_Bars.insert(i, lb);
      m_OldMax.at(i) = 0;
   }
   qApp->unlock();
   m_Mutex.unlock();
   RecalculateSizes();
}