1 /***************************************************************************
2 * Copyright (C) 2008-2009 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "visualizer.h"
23 #ifdef ENABLE_VISUALIZER
34 using Global::myScreen
;
35 using Global::myPrevScreen
;
36 using Global::MainStartY
;
37 using Global::MainHeight
;
39 Visualizer
*myVisualizer
= new Visualizer
;
41 const unsigned Visualizer::Samples
= 2048;
43 const unsigned Visualizer::FFTResults
= Samples
/2+1;
44 #endif // HAVE_FFTW3_H
46 void Visualizer::Init()
48 w
= new Window(0, MainStartY
, COLS
, MainHeight
, "", Config
.main_color
, brNone
);
52 itsFreqsMagnitude
= new unsigned[FFTResults
];
53 itsInput
= static_cast<double *>(fftw_malloc(sizeof(double)*Samples
));
54 itsOutput
= static_cast<fftw_complex
*>(fftw_malloc(sizeof(fftw_complex
)*FFTResults
));
55 itsPlan
= fftw_plan_dft_r2c_1d(Samples
, itsInput
, itsOutput
, FFTW_ESTIMATE
);
56 # endif // HAVE_FFTW3_H
63 void Visualizer::SwitchTo()
74 if (myScreen
!= this && myScreen
->isTabbable())
75 myPrevScreen
= myScreen
;
85 Global::wFooter
->SetTimeout(1000/25);
86 Global::RedrawHeader
= 1;
89 void Visualizer::Resize()
91 w
->Resize(COLS
, MainHeight
);
92 w
->MoveTo(0, MainStartY
);
96 std::basic_string
<my_char_t
> Visualizer::Title()
98 return U("Music visualizer");
101 void Visualizer::Update()
106 // if mpd is stopped, clear the screen
107 if (!Mpd
.isPlaying())
113 if (itsOutputID
!= -1 && Global::Timer
.tv_sec
> itsTimer
.tv_sec
+120)
115 Mpd
.DisableOutput(itsOutputID
);
116 Mpd
.EnableOutput(itsOutputID
);
117 gettimeofday(&itsTimer
, 0);
120 // it supports only PCM in format 44100:16:1
121 static int16_t buf
[Samples
];
122 ssize_t data
= read(itsFifo
, buf
, sizeof(buf
));
123 if (data
< 0) // no data available in fifo
128 Config
.visualizer_use_wave
? DrawSoundWave(buf
, data
) : DrawFrequencySpectrum(buf
, data
);
130 DrawSoundWave(buf
, data
);
131 # endif // HAVE_FFTW3_H
135 void Visualizer::SpacePressed()
138 Config
.visualizer_use_wave
= !Config
.visualizer_use_wave
;
139 ShowMessage("Visualization type: %s", Config
.visualizer_use_wave
? "Sound wave" : "Frequency spectrum");
140 # endif // HAVE_FFTW3_H
143 void Visualizer::DrawSoundWave(int16_t *buf
, ssize_t data
)
145 const int samples_per_col
= data
/sizeof(int16_t)/COLS
;
146 const int half_height
= MainHeight
/2;
148 double prev_point_pos
= 0;
149 for (int i
= 0; i
< COLS
; ++i
)
151 double point_pos
= 0;
152 for (int j
= 0; j
< samples_per_col
; ++j
)
153 point_pos
+= buf
[i
*samples_per_col
+j
];
154 point_pos
/= samples_per_col
;
155 point_pos
/= std::numeric_limits
<int16_t>::max();
156 point_pos
*= half_height
;
157 *w
<< XY(i
, half_height
+point_pos
) << '`';
158 if (i
&& abs(prev_point_pos
-point_pos
) > 2)
160 // if gap is too big. intermediate values are needed
161 // since without them all we see are blinking points
162 const int breakpoint
= std::max(prev_point_pos
, point_pos
);
163 const int half
= (prev_point_pos
+point_pos
)/2;
164 for (int k
= std::min(prev_point_pos
, point_pos
)+1; k
< breakpoint
; k
+= 2)
165 *w
<< XY(i
-(k
< half
), half_height
+k
) << '`';
167 prev_point_pos
= point_pos
;
169 *w
<< fmtAltCharsetEnd
;
173 void Visualizer::DrawFrequencySpectrum(int16_t *buf
, ssize_t data
)
176 std::fill(buf
+data
/sizeof(int16_t), buf
+Samples
, 0);
177 for (unsigned i
= 0; i
< Samples
; ++i
)
178 itsInput
[i
] = buf
[i
];
180 fftw_execute(itsPlan
);
182 // count magnitude of each frequency and scale it to fit the screen
183 for (unsigned i
= 0; i
< FFTResults
; ++i
)
184 itsFreqsMagnitude
[i
] = sqrt(itsOutput
[i
][0]*itsOutput
[i
][0] + itsOutput
[i
][1]*itsOutput
[i
][1])/1e5
*LINES
/5;
186 const int freqs_per_col
= FFTResults
/COLS
/* cut bandwidth a little to achieve better look */ * 4/5;
187 for (int i
= 0; i
< COLS
; ++i
)
189 size_t bar_height
= 0;
190 for (int j
= 0; j
< freqs_per_col
; ++j
)
191 bar_height
+= itsFreqsMagnitude
[i
*freqs_per_col
+j
];
192 bar_height
= std::min(bar_height
/freqs_per_col
, MainHeight
);
193 mvwvline(w
->Raw(), MainHeight
-bar_height
, i
, 0, bar_height
);
196 #endif // HAVE_FFTW3_H
198 void Visualizer::SetFD()
200 if (itsFifo
< 0 && (itsFifo
= open(Config
.visualizer_fifo_path
.c_str(), O_RDONLY
| O_NONBLOCK
)) < 0)
201 ShowMessage("Couldn't open \"%s\" for reading PCM data: %s", Config
.visualizer_fifo_path
.c_str(), strerror(errno
));
204 void Visualizer::ResetFD()
209 void Visualizer::FindOutputID()
212 if (!Config
.visualizer_output_name
.empty())
214 MPD::OutputList outputs
;
215 Mpd
.GetOutputs(outputs
);
216 for (unsigned i
= 0; i
< outputs
.size(); ++i
)
217 if (outputs
[i
].first
== Config
.visualizer_output_name
)
219 if (itsOutputID
== -1)
220 ShowMessage("There is no output named \"%s\"!", Config
.visualizer_output_name
.c_str());
224 #endif // ENABLE_VISUALIZER