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 }