|
|
|
/***************************************************************************
|
|
|
|
recording-monitor-widget.cpp - description
|
|
|
|
-------------------
|
|
|
|
begin : So Sep 7 2003
|
|
|
|
copyright : (C) 2003 by Martin Witte
|
|
|
|
email : witte@kawo1.rwth-aachen.de
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
#include "recording-datamonitor.h"
|
|
|
|
//#include "recording-context.h"
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqpixmap.h>
|
|
|
|
#include <kimageeffect.h> // fading, blending, ...
|
|
|
|
#include <kpixmapio.h> // fast conversion between TQPixmap/TQImage
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
|
|
|
#include <tdelocale.h>
|
|
|
|
|
|
|
|
#define CHANNEL_H_MIN 20
|
|
|
|
#define BLOCK_W_MIN 10
|
|
|
|
#define W_MIN (20 * (BLOCK_W_MIN))
|
|
|
|
|
|
|
|
RecordingDataMonitor::RecordingDataMonitor(TQWidget *parent, const char *name)
|
|
|
|
: TQFrame(parent, name),
|
|
|
|
m_channelsMax(NULL),
|
|
|
|
m_channelsAvg(NULL),
|
|
|
|
m_maxValue(INT_MAX),
|
|
|
|
m_channels(0),
|
|
|
|
m_pActiveBlocks(NULL)
|
|
|
|
{
|
|
|
|
setFrameStyle(Box | Sunken);
|
|
|
|
setLineWidth(1);
|
|
|
|
setMidLineWidth(1);
|
|
|
|
|
|
|
|
setChannels(2);
|
|
|
|
|
|
|
|
setColors(TQColor(20, 244, 20),
|
|
|
|
TQColor(10, 117, 10));
|
|
|
|
|
|
|
|
setSizePolicy(TQSizePolicy(TQSizePolicy::Expanding, TQSizePolicy::Expanding));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RecordingDataMonitor::~RecordingDataMonitor()
|
|
|
|
{
|
|
|
|
if (m_channelsMax) delete[] m_channelsMax;
|
|
|
|
if (m_channelsAvg) delete[] m_channelsAvg;
|
|
|
|
if (m_pActiveBlocks) delete[] m_pActiveBlocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// own stuff
|
|
|
|
|
|
|
|
void RecordingDataMonitor::setChannels(int n)
|
|
|
|
{
|
|
|
|
if (n != m_channels) {
|
|
|
|
if (m_channelsMax) delete[] m_channelsMax;
|
|
|
|
if (m_channelsAvg) delete[] m_channelsAvg;
|
|
|
|
if (m_pActiveBlocks) delete[] m_pActiveBlocks;
|
|
|
|
m_channels = n > 0 ? n : 0;
|
|
|
|
if (m_channels > 0) {
|
|
|
|
m_channelsMax = new int[m_channels];
|
|
|
|
m_channelsAvg = new double[m_channels];
|
|
|
|
m_pActiveBlocks = new int[m_channels];
|
|
|
|
for (int i = 0; i < m_channels; ++i) {
|
|
|
|
m_pActiveBlocks[i] = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
m_channelsMax = NULL;
|
|
|
|
m_channelsAvg = NULL;
|
|
|
|
m_pActiveBlocks = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 0; i < m_channels; ++i) {
|
|
|
|
m_channelsMax[i] = 0;
|
|
|
|
m_channelsAvg[i] = 0;
|
|
|
|
}
|
|
|
|
setMinimumSize(TQSize(W_MIN, (m_channels + 1 )* CHANNEL_H_MIN));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TQt/TDE ...
|
|
|
|
|
|
|
|
void RecordingDataMonitor::drawContents(TQPainter *painter)
|
|
|
|
{
|
|
|
|
if (painter)
|
|
|
|
internalDrawContents(*painter, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void RecordingDataMonitor::internalDrawContents(TQPainter &painter, bool repaintAll)
|
|
|
|
{
|
|
|
|
if (m_channels <= 0) return;
|
|
|
|
TQRect r = contentsRect();
|
|
|
|
|
|
|
|
TQPen activePen (colorGroup().color(TQColorGroup::Text), 1);
|
|
|
|
TQPen inactivePen (colorGroup().color(TQColorGroup::Mid), 1);
|
|
|
|
TQBrush activeBrush = colorGroup().brush(TQColorGroup::Text);
|
|
|
|
TQBrush inactiveBrush = colorGroup().brush(TQColorGroup::Mid);
|
|
|
|
TQBrush yellowBrush(TQColor(255,255,0));
|
|
|
|
TQBrush orangeBrush(TQColor(255,192,0));
|
|
|
|
TQBrush redBrush (TQColor(255,0, 0));
|
|
|
|
|
|
|
|
|
|
|
|
double ranges [5] = { 0.75, 0.83, 0.91, 1.0, 999 };
|
|
|
|
TQBrush *brushes[5] = { &activeBrush, &yellowBrush, &orangeBrush, &redBrush, &redBrush };
|
|
|
|
|
|
|
|
painter.setBrush( isEnabled() ? activeBrush : inactiveBrush);
|
|
|
|
|
|
|
|
int nBlocks = (r.width()-1) / BLOCK_W_MIN;
|
|
|
|
int xoffs = (r.width()-1) % BLOCK_W_MIN;
|
|
|
|
int chHeight = (r.height()-1-CHANNEL_H_MIN) / m_channels;
|
|
|
|
int yoffs = (r.height()-1) % m_channels;
|
|
|
|
|
|
|
|
double min_dB = 20*log10(1 / (double)m_maxValue );
|
|
|
|
|
|
|
|
int x0 = xoffs/2 + r.top();
|
|
|
|
int y = yoffs/2 + r.left();
|
|
|
|
for (int c = 0; c < m_channels; ++c) {
|
|
|
|
int x = x0;
|
|
|
|
|
|
|
|
|
|
|
|
int startBlock = 0;
|
|
|
|
int endBlock = nBlocks - 1;
|
|
|
|
int oldActiveBlocks = m_pActiveBlocks[c];
|
|
|
|
|
|
|
|
double dBMax = isEnabled() ? 20*log10(m_channelsMax[c] / (double)m_maxValue ) : min_dB;
|
|
|
|
|
|
|
|
m_pActiveBlocks[c] = m_channelsMax[c] ? (int)rint(nBlocks * (min_dB - dBMax) / min_dB) : 0;
|
|
|
|
|
|
|
|
if (!repaintAll) {
|
|
|
|
if (oldActiveBlocks > m_pActiveBlocks[c]) {
|
|
|
|
startBlock = m_pActiveBlocks[c];
|
|
|
|
endBlock = oldActiveBlocks - 1;
|
|
|
|
} else {
|
|
|
|
startBlock = oldActiveBlocks;
|
|
|
|
endBlock = m_pActiveBlocks[c]-1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int range = 0;
|
|
|
|
|
|
|
|
x += BLOCK_W_MIN * startBlock;
|
|
|
|
for (int b = startBlock; b <= endBlock; ++b) {
|
|
|
|
while (b >= nBlocks * ranges[range]) ++range;
|
|
|
|
painter.fillRect(x+1, y+1, BLOCK_W_MIN-1, chHeight-1,
|
|
|
|
b < m_pActiveBlocks[c] ? *brushes[range] : inactiveBrush);
|
|
|
|
x += BLOCK_W_MIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
y += chHeight;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (repaintAll) {
|
|
|
|
TQFont f("Helvetica");
|
|
|
|
painter.setPen (activePen);
|
|
|
|
f.setPixelSize(CHANNEL_H_MIN);
|
|
|
|
painter.setFont(f);
|
|
|
|
|
|
|
|
int maxW = TQFontMetrics(f).width(i18n("%1 dB").arg((int)min_dB));
|
|
|
|
int delta_dB = 5;
|
|
|
|
while (abs((long)min_dB) / delta_dB * maxW * 2 > r.width()) delta_dB *= 2;
|
|
|
|
|
|
|
|
for (int dB = 0; dB >= min_dB; dB -= delta_dB) {
|
|
|
|
TQString txt = i18n("%1 dB").arg(dB);
|
|
|
|
int w = TQFontMetrics(f).width(txt);
|
|
|
|
int x = x0 + (int)(nBlocks * BLOCK_W_MIN * (min_dB - dB) / min_dB) - w;
|
|
|
|
if (x < x0) continue;
|
|
|
|
painter.drawText(x, y + CHANNEL_H_MIN, txt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RecordingDataMonitor::setColors(const TQColor &activeText,
|
|
|
|
const TQColor &button)
|
|
|
|
{
|
|
|
|
m_colorActiveText = activeText;
|
|
|
|
m_colorButton = button;
|
|
|
|
|
|
|
|
TQPalette pl = palette();
|
|
|
|
TQColorGroup cg = pl.inactive();
|
|
|
|
|
|
|
|
TQBrush fg = cg.brush(TQColorGroup::Foreground),
|
|
|
|
btn = cg.brush(TQColorGroup::Button),
|
|
|
|
lgt = cg.brush(TQColorGroup::Light),
|
|
|
|
drk = cg.brush(TQColorGroup::Dark),
|
|
|
|
mid = cg.brush(TQColorGroup::Mid),
|
|
|
|
txt = cg.brush(TQColorGroup::Text),
|
|
|
|
btx = cg.brush(TQColorGroup::BrightText),
|
|
|
|
bas = cg.brush(TQColorGroup::Base),
|
|
|
|
bg = cg.brush(TQColorGroup::Background);
|
|
|
|
|
|
|
|
fg.setColor (m_colorActiveText);
|
|
|
|
btn.setColor(m_colorButton);
|
|
|
|
lgt.setColor(m_colorButton.light(180));
|
|
|
|
drk.setColor(m_colorButton.light( 50));
|
|
|
|
mid.setColor(m_colorButton.light( 75));
|
|
|
|
txt.setColor(m_colorActiveText);
|
|
|
|
btx.setColor(m_colorActiveText);
|
|
|
|
bas.setColor(m_colorButton);
|
|
|
|
bg.setColor (m_colorButton);
|
|
|
|
|
|
|
|
TQColorGroup ncg(fg, btn, lgt, drk, mid, txt, btx, bas, bg);
|
|
|
|
pl.setInactive(ncg);
|
|
|
|
pl.setActive(ncg);
|
|
|
|
setPalette(pl);
|
|
|
|
|
|
|
|
if (parentWidget() && parentWidget()->backgroundPixmap() ){
|
|
|
|
KPixmapIO io;
|
|
|
|
TQImage i = io.convertToImage(*parentWidget()->backgroundPixmap());
|
|
|
|
KImageEffect::fade(i, 0.5, colorGroup().color(TQColorGroup::Dark));
|
|
|
|
setPaletteBackgroundPixmap(io.convertToPixmap(i));
|
|
|
|
setBackgroundOrigin(WindowOrigin);
|
|
|
|
} else {
|
|
|
|
setBackgroundColor(colorGroup().color(TQColorGroup::Button));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool RecordingDataMonitor::noticeSoundStreamData(SoundStreamID /*id*/,
|
|
|
|
const SoundFormat &sf, const char *data, size_t size, size_t &/*consumed_size*/,
|
|
|
|
const SoundMetaData &/*md*/
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (!isEnabled())
|
|
|
|
return false;
|
|
|
|
int nSamples = size / sf.frameSize();
|
|
|
|
int sample_size = sf.sampleSize();
|
|
|
|
|
|
|
|
int bias = 0;
|
|
|
|
setChannels(sf.m_Channels);
|
|
|
|
int old_max = m_maxValue;
|
|
|
|
m_maxValue = sf.maxValue();
|
|
|
|
if (!sf.m_IsSigned) {
|
|
|
|
m_maxValue /= 2;
|
|
|
|
bias = -m_maxValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
int c = 0;
|
|
|
|
for (int s = 0; s < nSamples; ++s, ++c, data += sample_size) {
|
|
|
|
if (c >= m_channels) c -= m_channels; // avoid slow c = s % m_channels
|
|
|
|
|
|
|
|
int &m = m_channelsMax[c];
|
|
|
|
int x = abs(sf.convertSampleToInt(data, false) + bias);
|
|
|
|
if (m < x) m = x;
|
|
|
|
m_channelsAvg[c] += x;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < m_channels; ++i)
|
|
|
|
m_channelsAvg[i] /= nSamples;
|
|
|
|
|
|
|
|
TQPainter paint(this);
|
|
|
|
if (m_maxValue != old_max) {
|
|
|
|
repaint(true);
|
|
|
|
} else {
|
|
|
|
internalDrawContents(paint, false);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#include "recording-datamonitor.moc"
|