File indexing completed on 2025-03-09 04:24:04

0001 /****************************************************************************************
0002  * Copyright (c) 2014 Matej Repinc <mrepinc@gmail.com>                                  *
0003  *                                                                                      *
0004  * This program is free software; you can redistribute it and/or modify it under        *
0005  * the terms of the GNU General Public License as published by the Free Software        *
0006  * Foundation; either version 2 of the License, or (at your option) any later           *
0007  * version.                                                                             *
0008  *                                                                                      *
0009  * This program is distributed in the hope that it will be useful, but WITHOUT ANY      *
0010  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A      *
0011  * PARTICULAR PURPOSE. See the GNU General Pulic License for more details.              *
0012  *                                                                                      *
0013  * You should have received a copy of the GNU General Public License along with         *
0014  * this program.  If not, see <http://www.gnu.org/licenses/>.                           *
0015  ****************************************************************************************/
0016 
0017 #include "ASCIIAnalyzer.h"
0018 
0019 #include "PaletteHandler.h"
0020 
0021 #include <cmath>
0022 
0023 #include <QPainter>
0024 #include <QResizeEvent>
0025 
0026 
0027 ASCIIAnalyzer* ASCIIAnalyzer::instance = 0;
0028 
0029 ASCIIAnalyzer::ASCIIAnalyzer( QWidget *parent )
0030     : Analyzer::Base( parent )
0031     , m_columns( 0 )         //int
0032     , m_rows( 0 )            //int
0033 {
0034     instance = this;
0035     setObjectName( "ASCII" );
0036 
0037     setMaximumWidth( MAX_COLUMNS * ( BLOCK_WIDTH + 1 ) - 1 );
0038     setFps( 30 );
0039 }
0040 
0041 void
0042 ASCIIAnalyzer::initializeGL()
0043 {
0044     // Disable depth test (all is drawn on a 2d plane)
0045     glDisable( GL_DEPTH_TEST );
0046 }
0047 
0048 void
0049 ASCIIAnalyzer::resizeGL( int w, int h )
0050 {
0051     glViewport( 0, 0, (GLint)w, (GLint)h );
0052 
0053     // Set up a 2D projection matrix
0054     glMatrixMode( GL_PROJECTION );
0055     glLoadIdentity();
0056     glOrtho( 0.0, (GLdouble)w, (GLdouble)h, 0.0, 0.0, 1.0 );
0057 
0058     const int oldRows = m_rows;
0059 
0060     // Rounded up so that the last column/line is covered if partially visible
0061     m_columns = std::min( std::floor( (double)width() / ( BLOCK_WIDTH + 1 ) ), (double)MAX_COLUMNS );
0062     m_rows    = std::ceil( (double)height() / ( BLOCK_HEIGHT + 1 ) );
0063 
0064     m_scope.resize( m_columns );
0065     m_store.resize( m_columns );
0066 
0067     if( m_rows != oldRows )
0068     {
0069         m_barPixmap = QPixmap( BLOCK_WIDTH, m_rows * ( BLOCK_HEIGHT + 1 ) );
0070 
0071         m_yscale.resize( m_rows + 1 );
0072 
0073         const float PRE = 1, PRO = 1; //PRE and PRO allow us to restrict the range somewhat
0074 
0075         for( int z = 0; z < m_rows; ++z )
0076             m_yscale[z] = 1 - ( log10( PRE + z ) / log10( PRE + m_rows + PRO ) );
0077 
0078         m_yscale[m_rows] = 0;
0079 
0080         determineStep();
0081         paletteChange( palette() );
0082     }
0083 
0084     drawBackground();
0085     analyze( m_scope );
0086 }
0087 
0088 void
0089 ASCIIAnalyzer::determineStep()
0090 {
0091     // Based on Mark Kretschmann's work in BlockAnalyzer
0092     // falltime is dependent on rowcount due to our digital resolution (ie we have boxes/blocks of pixels)
0093     // I calculated the value 50 based on some trial and error
0094 
0095     const double fallTime = 50 * m_rows;
0096     m_step = double( m_rows * 80 ) / fallTime; //80 = ~milliseconds between signals with audio data
0097 }
0098 
0099 void
0100 ASCIIAnalyzer::transform( QVector<float> &s ) //pure virtual
0101 {
0102     // Based on Mark Kretschmann's work in BlockAnalyzer
0103     for( int x = 0; x < s.size(); ++x )
0104         s[x] *= 2;
0105 
0106     float *front = static_cast<float*>( &s.front() );
0107 
0108     m_fht->spectrum( front );
0109     m_fht->scale( front, 1.0 / 20 );
0110 
0111     //the second half is pretty dull, so only show it if the user has a large analyzer
0112     //by setting to m_scope.size() if large we prevent interpolation of large analyzers, this is good!
0113     s.resize( m_scope.size() <= MAX_COLUMNS / 2 ? MAX_COLUMNS / 2 : m_scope.size() );
0114 }
0115 
0116 void
0117 ASCIIAnalyzer::analyze( const QVector<float> &s )
0118 {
0119     interpolate( s, m_scope );
0120 }
0121 
0122 void
0123 ASCIIAnalyzer::paintGL()
0124 {
0125     // Based largely on Mark Kretschmann's work in BlockAnalyzer,
0126     // however a bit simplified since we don't need fancy transitions
0127     // and textures.
0128     // y = 2 3 2 1 0 2
0129     //     . . . . # .
0130     //     . . . # # .
0131     //     # . # # # #
0132     //     # # # # # #
0133     //
0134     // visual aid for how this analyzer works.
0135     // y represents the number of blanks
0136     // y starts from the top and increases in units of blocks
0137 
0138     // m_yscale looks similar to: { 0.7, 0.5, 0.25, 0.15, 0.1, 0 }
0139     // if it contains 6 elements there are 5 rows in the analyzer
0140 
0141     glMatrixMode( GL_MODELVIEW );
0142     glLoadIdentity();
0143 
0144     // Paint the background
0145     drawTexture( m_background.data(), 0, 0, 0, 0 );
0146 
0147     for( uint y, x = 0; x < (uint)m_scope.size(); ++x )
0148     {
0149         // determine y
0150         for( y = 0; m_scope[x] < m_yscale[y]; ++y )
0151             ;
0152 
0153         // the higher the y, the lower the bar physically is.
0154         if( ( float )y > m_store[x] )
0155             y = uint( m_store[x] += m_step );
0156         else
0157             m_store[x] = y;
0158 
0159         // Don't draw top two #'s
0160         y += 2;
0161 
0162         int xpos = x * ( BLOCK_WIDTH + 1 );
0163         // REMEMBER: y is a number from 0 to m_rows, 0 means all blocks are glowing, m_rows means none are
0164         drawTexture( m_barTexture.data(), xpos, y * ( BLOCK_HEIGHT + 1 ), 0, y * ( BLOCK_HEIGHT + 1 ) );
0165 
0166         // Draw second top bar to "ease" transition
0167         int top_ypos = int( m_store[x] ) * ( BLOCK_HEIGHT + 1 );
0168         drawTexture( m_topSecondBarTexture.data(), xpos, top_ypos + BLOCK_HEIGHT + 1, 0, 0 );
0169 
0170         // Draw top bar
0171         drawTexture( m_topBarTexture.data(), xpos, top_ypos, 0, 0 );
0172     }
0173 }
0174 
0175 void
0176 ASCIIAnalyzer::drawTexture( Texture* texture, int x, int y, int sx, int sy )
0177 {
0178     const GLfloat xf = x;
0179     const GLfloat yf = y;
0180     const GLfloat wf = texture->size.width() - sx;
0181     const GLfloat hf = texture->size.height() - sy;
0182     const GLfloat sxf = (GLfloat)sx / texture->size.width();
0183     const GLfloat syf = (GLfloat)sy / texture->size.height();
0184 
0185     glEnable( GL_TEXTURE_2D );
0186     glBindTexture( GL_TEXTURE_2D, texture->id );
0187 
0188     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
0189     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
0190 
0191     // Draw a textured quad
0192     glBegin(GL_QUADS);
0193     glTexCoord2f( sxf, syf ); glVertex2f( xf, yf );
0194     glTexCoord2f( sxf, 1.0 ); glVertex2f( xf, yf + hf );
0195     glTexCoord2f( 1.0, 1.0 ); glVertex2f( xf + wf, yf + hf );
0196     glTexCoord2f( 1.0, syf ); glVertex2f( xf + wf, yf );
0197     glEnd();
0198 
0199     glDisable( GL_TEXTURE_2D );
0200 }
0201 
0202 void
0203 ASCIIAnalyzer::paletteChange( const QPalette& ) //virtual
0204 {
0205     const QColor bg = palette().background().color();
0206     const QFont font ("Cantarell", 10);
0207 
0208     QPixmap topBar( BLOCK_WIDTH, BLOCK_HEIGHT );
0209     topBar.fill( bg );
0210     QPainter tbp ( &topBar );
0211     tbp.setPen(Qt::red);
0212     tbp.setBackground(palette().background().color());
0213     tbp.setFont(font);
0214     tbp.drawText(topBar.rect(), Qt::AlignCenter, ".");
0215     m_topBarTexture = QSharedPointer<Texture>( new Texture( topBar ) );
0216 
0217     QPixmap topSecondBar( BLOCK_WIDTH, BLOCK_HEIGHT );
0218     // red on top, black on bottom
0219     QLinearGradient gradient (BLOCK_WIDTH/2, 0, BLOCK_WIDTH/2, BLOCK_HEIGHT);
0220     gradient.setColorAt(0.3, Qt::red);
0221     gradient.setColorAt(1.0, Qt::darkGreen);
0222     topSecondBar.fill( bg );
0223     QPainter tsbp ( &topSecondBar );
0224     tsbp.setPen( QPen(gradient, BLOCK_WIDTH) );
0225     tsbp.setBrush( gradient );
0226     tsbp.setFont(font);
0227     tsbp.drawText(topSecondBar.rect(), Qt::AlignCenter, "o");
0228     m_topSecondBarTexture = QSharedPointer<Texture>( new Texture( topSecondBar ) );
0229 
0230     m_barPixmap.fill( bg );
0231     QPainter p( &m_barPixmap );
0232     p.setPen(Qt::darkGreen);
0233     p.setFont(font);
0234 
0235     for( int y = 0; y < m_rows; ++y ) {
0236         QRect rect (0, y * ( BLOCK_HEIGHT + 1 ), BLOCK_WIDTH, BLOCK_HEIGHT);
0237         p.drawText(rect, Qt::AlignCenter, "#");
0238     }
0239 
0240     m_barTexture = QSharedPointer<Texture>( new Texture( m_barPixmap ) );
0241     drawBackground();
0242 }
0243 
0244 void
0245 ASCIIAnalyzer::drawBackground()
0246 {
0247     const QColor bg = palette().background().color();
0248     QPixmap background( size() );
0249     background.fill( bg );
0250 
0251     m_background = QSharedPointer<Texture>( new Texture( background ) );
0252 }