1 /***************************************************************************
2     NewSignalDialog.cpp  -  dialog for the "newsignal" plugin
3                              -------------------
4     begin                : Wed Jul 18 2001
5     copyright            : (C) 2001 by Thomas Eschenbacher
6     email                : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "config.h"
19 
20 #include <math.h>
21 #include <stdlib.h>
22 
23 #include <limits>
24 
25 #include <QLabel>
26 #include <QLayout>
27 #include <QPushButton>
28 #include <QRadioButton>
29 #include <QSlider>
30 #include <QSpinBox>
31 #include <QString>
32 #include <QTimer>
33 
34 #include <KComboBox>
35 #include <KHelpClient>
36 #include <KLocalizedString>
37 
38 #include "libkwave/String.h"
39 #include "libkwave/Utils.h"
40 
41 #include "NewSignalDialog.h"
42 
43 //***************************************************************************
NewSignalDialog(QWidget * parent,sample_index_t samples,unsigned int rate,unsigned int bits,unsigned int tracks,bool by_time)44 Kwave::NewSignalDialog::NewSignalDialog(QWidget *parent, sample_index_t samples,
45 	unsigned int rate, unsigned int bits, unsigned int tracks,
46 	bool by_time)
47     :QDialog(parent), Ui::NewSigDlg(), m_timer(this), m_recursive(false)
48 {
49 
50     setupUi(this);
51     setModal(true);
52 
53     edSamples->setRange(0, std::numeric_limits<int>::max());
54     edSamples->setSingleStep(1);
55 
56     // connect the timer for the sample edit
57     connect(&m_timer, SIGNAL(timeout()), this, SLOT(checkNewSampleEdit()));
58     connect(rbTime, SIGNAL(toggled(bool)),
59             this, SLOT(rbTimeToggled(bool)));
60 
61     // connect the file format controls
62     connect(cbSampleRate, SIGNAL(editTextChanged(QString)),
63             this, SLOT(sampleRateChanged(QString)));
64     connect(sbChannels, SIGNAL(valueChanged(int)),
65             this, SLOT(tracksChanged(int)));
66     connect(sbResolution, SIGNAL(valueChanged(int)),
67             this, SLOT(checkTimeAndLengthInfo(int)));
68 
69     // connect the time controls
70     connect(sbSeconds, SIGNAL(valueChanged(int)),
71             this, SLOT(timeChanged(int)));
72     connect(sbMinutes, SIGNAL(valueChanged(int)),
73             this, SLOT(timeChanged(int)));
74     connect(sbHours, SIGNAL(valueChanged(int)),
75             this, SLOT(timeChanged(int)));
76 
77     // selection by number of samples
78     connect(slideLength,SIGNAL(valueChanged(int)),
79             this, SLOT(setLengthPercentage(int)));
80 
81     // selection by percentage of maximum possible length
82     connect(edSamples, SIGNAL(valueChanged(int)),
83             this, SLOT(samplesChanged(int)));
84 
85     // help button
86     connect(buttonBox->button(QDialogButtonBox::Help), SIGNAL(clicked()),
87             this,   SLOT(invokeHelp()));
88 
89     // pre-initialize the size
90     setMaximumHeight(sizeHint().height());
91     setMaximumWidth(sizeHint().width());
92 
93     // initialize the controls
94     cbSampleRate->setEditText(QString::number(rate));
95     sbResolution->setValue(bits);
96     sbChannels->setValue(tracks);
97     if (by_time) {
98 	rbSamples->setChecked(false);
99 	rbTime->setChecked(true);
100 	setHMS(samples);
101 	edSamples->setEnabled(false);
102 	sbHours->setEnabled(true);
103 	sbMinutes->setEnabled(true);
104 	sbSeconds->setEnabled(true);
105     } else {
106 	// by samples
107 	rbTime->setChecked(false);
108 	rbSamples->setChecked(true);
109 	edSamples->setValue(Kwave::toInt(samples));
110 	edSamples->setEnabled(true);
111 	sbHours->setEnabled(false);
112 	sbMinutes->setEnabled(false);
113 	sbSeconds->setEnabled(false);
114     }
115 
116     tracksChanged(0);
117     checkTimeAndLengthInfo(0);
118 
119     // that dialog is big enough, limit it to it's optimal size
120     setFixedHeight(sizeHint().height());
121     setFixedWidth(sizeHint().width());
122 
123     // set the focus onto the "OK" button
124     buttonBox->button(QDialogButtonBox::Ok)->setFocus();
125 }
126 
127 //***************************************************************************
samples()128 sample_index_t Kwave::NewSignalDialog::samples()
129 {
130     return static_cast<sample_index_t>(edSamples->value());
131 }
132 
133 //***************************************************************************
rate()134 double Kwave::NewSignalDialog::rate()
135 {
136     bool ok;
137     double r = cbSampleRate->currentText().toDouble(&ok);
138     if (!ok) r = 0;
139     return r;
140 }
141 
142 //***************************************************************************
checkNewSampleEdit()143 void Kwave::NewSignalDialog::checkNewSampleEdit()
144 {
145     static int last_samples = -1;
146     if (edSamples->value() != last_samples) {
147 	last_samples = edSamples->value();
148 	samplesChanged(last_samples);
149     }
150 }
151 
152 //***************************************************************************
tracks()153 unsigned int Kwave::NewSignalDialog::tracks()
154 {
155     return sbChannels->value();
156 }
157 
158 //***************************************************************************
bitsPerSample()159 unsigned int Kwave::NewSignalDialog::bitsPerSample()
160 {
161     int res = sbResolution->value();
162     if (res < 8) res = 8;
163     return res;
164 }
165 
166 //***************************************************************************
byTime()167 bool Kwave::NewSignalDialog::byTime()
168 {
169     return rbTime->isChecked();
170 }
171 
172 //***************************************************************************
maxSamples()173 sample_index_t Kwave::NewSignalDialog::maxSamples()
174 {
175     unsigned int bytes_per_sample = bitsPerSample() >> 3;
176 
177     /*
178      * NOTE: this limitation to INT_MAX instead of UINT_MAX is
179      *       only needed because some gui elements like
180      *       QSpinBox cannot handle more :-(
181      */
182     const sample_index_t max_file_size = std::numeric_limits<int>::max();
183 
184     return (max_file_size / tracks() / bytes_per_sample);
185 }
186 
187 //***************************************************************************
rbTimeToggled(bool)188 void Kwave::NewSignalDialog::rbTimeToggled(bool)
189 {
190     if (rbTime->isChecked()) {
191 	m_timer.stop();
192     } else {
193 	// activate the sample edit timer
194 	m_timer.setSingleShot(false);
195 	m_timer.start(100);
196     }
197 }
198 
199 //***************************************************************************
checkTimeAndLengthInfo(int)200 void Kwave::NewSignalDialog::checkTimeAndLengthInfo(int)
201 {
202     (rbTime->isChecked()) ? timeChanged(0) : samplesChanged(0);
203 }
204 
205 //***************************************************************************
timeChanged(int)206 void Kwave::NewSignalDialog::timeChanged(int)
207 {
208     if (m_recursive) return; // don't do recursive processing
209     if (!rbTime->isChecked()) return;
210     if ((rate() <= 0) || !tracks() || (bitsPerSample() < 8)) return;
211     m_recursive = true;
212 
213     // get current time and correct wrap-overs
214     int seconds = sbSeconds->value();
215     int minutes = sbMinutes->value();
216     int hours = sbHours->value();
217     if ((seconds < 0) && ((minutes > 0) || (hours > 0)) ) {
218 	sbSeconds->setValue(59);
219 	sbMinutes->stepDown();
220 	minutes--;
221     } else if (seconds < 0) {
222 	sbSeconds->setValue(0);
223     } else if (seconds > 59) {
224 	sbSeconds->setValue(0);
225 	sbMinutes->stepUp();
226 	minutes++;
227     }
228 
229     if ((minutes < 0) && (hours > 0)) {
230 	sbMinutes->setValue(59);
231 	sbHours->stepDown();
232 	hours--;
233     } else if (minutes < 0) {
234 	sbMinutes->setValue(0);
235     } else if (minutes > 59) {
236 	sbMinutes->setValue(0);
237 	sbHours->stepUp();
238 	hours++;
239     }
240     seconds = sbSeconds->value();
241     minutes = sbMinutes->value();
242     hours = sbHours->value();
243     minutes += 60 * hours;
244     seconds += 60 * minutes;
245 
246     // limit the current number of samples
247     sample_index_t max_samples = maxSamples();
248     sample_index_t samples     = static_cast<sample_index_t>(ceil(
249 	static_cast<double>(seconds) * rate()));
250 
251     if (samples > max_samples) {
252 	// wrap down to the maximum allowed number of samples
253 	samples =  max_samples;
254 	setHMS(samples);
255     }
256 
257     // update the other controls
258     edSamples->setValue(Kwave::toInt(samples));
259     slideLength->setValue(Kwave::toInt(100.0 *
260 	static_cast<double>(samples) / static_cast<double>(max_samples)));
261     updateFileSize();
262     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(samples > 0);
263 
264     m_recursive = false;
265 }
266 
267 //***************************************************************************
samplesChanged(int)268 void Kwave::NewSignalDialog::samplesChanged(int)
269 {
270     if (m_recursive) return; // don't do recursive processing
271     if (!rbSamples->isChecked()) return;
272     m_recursive = true;
273 
274     sample_index_t samples = edSamples->value();
275     sample_index_t max_samples = maxSamples();
276 
277     if (samples > max_samples) {
278 	samples = max_samples;
279 	edSamples->setValue(Kwave::toInt(samples));
280     }
281 
282     // update the other controls
283     setHMS(samples);
284     slideLength->setValue(Kwave::toInt(100.0 *
285 	static_cast<double>(samples) / static_cast<double>(max_samples)));
286     updateFileSize();
287     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(samples > 0);
288 
289     m_recursive = false;
290 }
291 
292 //***************************************************************************
sampleRateChanged(const QString &)293 void Kwave::NewSignalDialog::sampleRateChanged(const QString&)
294 {
295     checkTimeAndLengthInfo(0);
296 }
297 
298 //***************************************************************************
tracksChanged(int)299 void Kwave::NewSignalDialog::tracksChanged(int)
300 {
301     switch (tracks()) {
302 	case 1:
303 	    lblTracksVerbose->setText(i18n("(Mono)"));
304 	    break;
305 	case 2:
306 	    lblTracksVerbose->setText(i18n("(Stereo)"));
307 	    break;
308 	case 4:
309 	    lblTracksVerbose->setText(i18n("(Quadro)"));
310 	    break;
311 	default:
312 	    lblTracksVerbose->setText(_(""));
313 	    break;
314     }
315     checkTimeAndLengthInfo(0);
316 }
317 
318 //***************************************************************************
updateFileSize()319 void Kwave::NewSignalDialog::updateFileSize()
320 {
321     double samples = static_cast<double>(edSamples->value());
322     double mbytes = samples * static_cast<double>(tracks()) *
323                     static_cast<double>(bitsPerSample() >> 3);
324     mbytes /= 1024.0; // to kilobytes
325     mbytes /= 1024.0; // to megabytes
326 
327     QString str_bytes;
328     str_bytes.setNum(mbytes, 'f', (mbytes >= 10.0) ? 1 : 3);
329     lblFileSize->setText(i18n("(Resulting file size: %1 MB)", str_bytes));
330 }
331 
332 //***************************************************************************
setLengthPercentage(int percent)333 void Kwave::NewSignalDialog::setLengthPercentage(int percent)
334 {
335     if (m_recursive) return; // don't do recursive processing
336     if (rate() <= 0) return;
337     m_recursive = true;
338 
339     sample_index_t samples = static_cast<sample_index_t>(
340 	static_cast<double>(maxSamples()) *
341 	static_cast<double>(percent) / 100.0);
342 
343     // update the other controls
344     setHMS(samples);
345     edSamples->setValue(Kwave::toInt(samples));
346     updateFileSize();
347     buttonBox->button(QDialogButtonBox::Ok)->setEnabled(samples > 0);
348 
349     m_recursive = false;
350 }
351 
352 //***************************************************************************
setHMS(sample_index_t & samples)353 void Kwave::NewSignalDialog::setHMS(sample_index_t &samples)
354 {
355     double rate = this->rate();
356     if (rate <= 0.0) return;
357 
358     // TODO: support for 64 bit
359     if (samples > maxSamples()) samples = maxSamples();
360 
361     int total_sec = Kwave::toInt(ceil(static_cast<double>(samples) / rate));
362     int hours   = total_sec / (60*60);
363     int minutes = (total_sec / 60) % 60;
364     int seconds = total_sec % 60;
365 
366     sbHours->setValue(hours);
367     sbMinutes->setValue(minutes);
368     sbSeconds->setValue(seconds);
369 }
370 
371 //***************************************************************************
invokeHelp()372 void Kwave::NewSignalDialog::invokeHelp()
373 {
374     KHelpClient::invokeHelp(_("newsignal"));
375 }
376 
377 //***************************************************************************
378 //***************************************************************************
379