1 // guitune.cc
2 //
3 // guitune - program for tuning instruments (actually an oscilloscope)
4 // Copyright (C) 1999 Florian Berger
5 // Email: florian.berger@jk.uni-linz.ac.at
6 //
7 // This program is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License Version 2 as
9 // published by the Free Software Foundation;
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 //
20 //
21
22 //#include <iostream.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <sys/ioctl.h>
27 #include <sys/soundcard.h>
28
29 #include <gtkmm/main.h>
30 #include <gtkmm/table.h>
31 #include <gtkmm/spinbutton.h>
32 #include <gtkmm/style.h>
33 #include <gtkmm/frame.h>
34
35 #include <unistd.h>
36 #include <stdio.h>
37 #include <time.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <errno.h>
41
42 #include <math.h>
43
44 #include "guitune.h"
45
46 #include "resources.h"
47
48
49
50 //globally
51 double KAMMERTON, KAMMERTON_LOG;
52
setTuningNorm()53 void MainWidget::setTuningNorm()
54 {
55 KAMMERTON=KAMMERTON_NORM;
56 KAMMERTON_LOG=KAMMERTON_LOG_NORM;
57 logview->invalidate();
58 }
59
setTuningWien()60 void MainWidget::setTuningWien()
61 {
62 KAMMERTON=KAMMERTON_WIEN;
63 KAMMERTON_LOG=KAMMERTON_LOG_WIEN;
64 logview->invalidate();
65 }
66
setTuningPhys()67 void MainWidget::setTuningPhys()
68 {
69 KAMMERTON=KAMMERTON_PHYS;
70 KAMMERTON_LOG=KAMMERTON_LOG_PHYS;
71 logview->invalidate();
72 }
73
74
setTuningEqui()75 void MainWidget::setTuningEqui()
76 {
77 logview->set_nat_tuning(false);
78 }
79
80
setTuningNat()81 void MainWidget::setTuningNat()
82 {
83 logview->set_nat_tuning(true);
84 }
85
86
setScaleUS()87 void MainWidget::setScaleUS()
88 {
89 logview->setScale(LogView::us_scale);
90 }
91
setScaleUSAlt()92 void MainWidget::setScaleUSAlt()
93 {
94 logview->setScale(LogView::us_scale_alt);
95 }
96
setScaleGE()97 void MainWidget::setScaleGE()
98 {
99 logview->setScale(LogView::german_scale);
100 }
101
setScaleGEAlt()102 void MainWidget::setScaleGEAlt()
103 {
104 logview->setScale(LogView::german_scale_alt);
105 }
106
107
108
showLogView()109 void MainWidget::showLogView()
110 {
111 logview->show();
112 }
113
hideLogView()114 void MainWidget::hideLogView()
115 {
116 logview->hide();
117 }
118
showOszi()119 void MainWidget::showOszi()
120 {
121 oszi->show();
122 }
123
hideOszi()124 void MainWidget::hideOszi()
125 {
126 oszi->hide();
127 }
128
getTrigger()129 double MainWidget::getTrigger()
130 {
131 return(oszi->getTrigFact());
132 }
133
setSampFreqVal(int sampfreq)134 void MainWidget::setSampFreqVal( int sampfreq )
135 {
136 i_pAdjSF->set_value(sampfreq);
137 }
138
139
setSampFreq()140 void MainWidget::setSampFreq()
141 {
142 sampfreq = (int) i_pAdjSF->get_value();
143 // timer->stop();
144 init_audio();
145 // timer->start(0);
146 oszi->setSampleFreq(sampfreq_exact);
147 // emit signalSampFreqChanged();
148 }
149
150
setSampNrVal(int sampnr)151 void MainWidget::setSampNrVal( int sampnr )
152 {
153 i_pAdjSN->set_value(sampnr);
154 }
155
156
setSampNr()157 void MainWidget::setSampNr()
158 {
159 sampnr = (int) i_pAdjSN->get_value();
160 // emit signalSampNrChanged();
161 }
162
163
setTrigger()164 void MainWidget::setTrigger()
165 {
166 oszi->setTrigFact(i_pAdjTR->get_value());
167 // emit signalTriggerChanged();
168 }
169
170
setTriggerVal(double value)171 void MainWidget::setTriggerVal(double value)
172 {
173 // oszi->setTrigFact(value);
174 i_pAdjTR->set_value(value);
175 // i_pAdjTR->changed();
176 // emit signalTriggerChanged();
177 }
178
179
setAdaptive()180 void MainWidget::setAdaptive()
181 {
182 bool active;
183 active = i_pCBAdapt->get_active();
184 oszi->setAdaptive((int)active);
185 }
186
187
setAdaptiveVal(int adaptive)188 void MainWidget::setAdaptiveVal( int adaptive )
189 {
190 i_pCBAdapt->set_active((bool)adaptive);
191 }
192
193
194
setDSPName(const char * name)195 void MainWidget::setDSPName(const char *name)
196 {
197 // timer->stop();
198 strcpy(dsp_devicename,name);
199 init_audio();
200 // timer->start(0);
201 }
202
init_audio()203 void MainWidget::init_audio()
204 {
205 printf("initializing audio at %s\n",dsp_devicename);
206
207 uninit_audio();
208
209 audio_fd = open(dsp_devicename, O_RDONLY);
210 if (audio_fd == -1) {
211 perror(dsp_devicename);
212 exit(1);
213 }
214 fcntl(audio_fd,F_SETFD,FD_CLOEXEC);
215
216 // ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
217 if( strcmp(dsp_devicename,"/dev/stdin")==0 ){
218
219 printf("reading data from stdin\n");
220 blksize=32;
221 printf(" blocksize=%d\n",blksize);
222 printf(" sampfreq=%d\n",sampfreq);
223 sampfreq_exact=sampfreq;
224
225 }else{
226
227 ioctl(audio_fd, SNDCTL_DSP_SETDUPLEX, 0);
228
229 {int caps;
230 ioctl( audio_fd, SNDCTL_DSP_GETCAPS, &caps );
231 printf("OSS-Version %d\n", caps & DSP_CAP_REVISION );
232 printf(" DUPLEX = %X\n",caps & DSP_CAP_DUPLEX );
233 printf(" REALTIME = %X\n",caps & DSP_CAP_REALTIME );
234 printf(" BATCH = %X\n",caps & DSP_CAP_BATCH );
235 printf(" COPROC = %X\n",caps & DSP_CAP_COPROC );
236 printf(" TRIGGER = %X\n",caps & DSP_CAP_TRIGGER );
237 printf(" MMAP = %X\n",caps & DSP_CAP_MMAP );
238 }
239
240 blksize=8; // 2^8 = 256
241 ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &blksize);
242 // ioctl(audio_fd, SNDCTL_DSP_SETBLKSIZE, &blksize);
243
244 ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &blksize);
245 printf("blocksize=%d\n",blksize);
246 //blksize=4096;
247
248 ioctl(audio_fd, SNDCTL_DSP_SYNC, NULL);
249 // int a_sampsize = 8;
250 // int a_sampsize = AFMT_U8;
251 i_sampfmt = AFMT_S16_LE;
252 ioctl(audio_fd, SNDCTL_DSP_SAMPLESIZE, &i_sampfmt);
253 int a_stereo = 0;
254 ioctl(audio_fd, SNDCTL_DSP_STEREO, &a_stereo);
255
256 int a_speed = sampfreq;
257 printf("sampfreq=%d\n",sampfreq);
258 ioctl(audio_fd, SNDCTL_DSP_SPEED, &a_speed);
259 ioctl(audio_fd, SOUND_PCM_READ_RATE, &sampfreq);
260 printf("sampfreq=%d\n",sampfreq);
261 sampfreq_exact=sampfreq;
262
263 }
264
265 //int mixer = open("/dev/mixer", O_RDONLY, 0);
266 //int vol=0xFFFF;
267 //ioctl(mixer, SOUND_MIXER_WRITE_MIC, &vol);
268 //printf("MIC-Volume=%d\n",vol);
269 //close(mixer);
270
271 // printf("close(audio_fd)=%d\n",close(audio_fd));
272 // printf("close(audio_fd)=%d\n",close(audio_fd));
273
274 trig = false;
275 got_bytes = 0;
276
277 audio_connection = Glib::signal_io()
278 .connect(sigc::mem_fun(this, &MainWidget::proc_audio),
279 audio_fd, Glib::IO_IN | Glib::IO_HUP);
280 }
281
uninit_audio()282 void MainWidget::uninit_audio()
283 {
284 audio_connection.disconnect();
285
286 if (audio_fd != -1)
287 {
288 ::close(audio_fd);
289 audio_fd = -1;
290 }
291 }
292
MainWidget()293 MainWidget::MainWidget()
294 : Gtk::Table(7, 4)
295 {
296 int i;
297 //InitAudio();
298 // setMinimumSize(490,240);
299
300 set_row_spacings( 12 );
301 set_col_spacings( 12 );
302
303 KAMMERTON=KAMMERTON_NORM;
304 KAMMERTON_LOG=KAMMERTON_LOG_NORM;
305
306 strcpy(dsp_devicename,"/dev/dsp");
307
308 sampnr=1024;
309 sampfreq=11048;
310 audio_fd = -1;
311 init_audio();
312 printf("Audiodriver initialized\n");
313
314 freqs[0]=KAMMERTON; lfreqs[0]=KAMMERTON_LOG;
315 for(i=1;i<12;i++){
316 freqs [i] = freqs [i-1] * D_NOTE;
317 lfreqs[i] = lfreqs[i-1] + D_NOTE_LOG;
318 }
319
320 oszi = new OsziView;
321 oszi->setSampleFreq(sampfreq_exact);
322 logview = new LogView;
323
324 // Gtk::Style style = new Gtk::Style( new void );
325 // Gdk_Font font("-*-helvetica-bold-r-*-*-15-*-*-*-*-*-*-*");
326 // get_style()->set_font( font );
327
328 // freqview = new Gtk::Label("freq");
329 // nfreqview = new Gtk::Label("nfreq");
330 freqview = new LCDView("");
331 freqview->set_size_request(100,20);
332 nfreqview = new LCDView("");
333 nfreqview->set_size_request(100,20);
334
335 // nfreqview->set_style( style );
336
337
338 i_pAdjSF = new Gtk::Adjustment(
339 11024.0, // value
340 5000.0, // lower
341 48000.0, // upper
342 1.0, // step inc
343 100.0, // page inc
344 0 // page size
345 );
346
347 i_pAdjSN = new Gtk::Adjustment(
348 1024.0, // value
349 100.0, // lower
350 10000.0, // upper
351 1.0, // step inc
352 100.0, // page inc
353 0 // page size
354 );
355
356 i_pAdjTR = new Gtk::Adjustment(
357 0.6, // value
358 0.0, // lower
359 1.0, // upper
360 0.05, // step inc
361 0.1, // page inc
362 0 // page size
363 );
364
365 attach( *oszi, 0, 1, 0, 5 );
366
367 attach( *manage( new Gtk::Label("Freq:") ),
368 1, 2, 1, 2,
369 Gtk::AttachOptions(), Gtk::AttachOptions() );
370 attach( *freqview, 2, 3, 1, 2,
371 Gtk::FILL, Gtk::AttachOptions() );
372
373 attach( *manage( new Gtk::Label("Tune:") ),
374 1, 2, 2, 3,
375 Gtk::AttachOptions(), Gtk::AttachOptions() );
376 attach( *nfreqview, 2, 3, 2, 3,
377 Gtk::FILL, Gtk::AttachOptions() );
378
379 Gtk::Frame *adj_frame = new Gtk::Frame("Adjust:");
380
381 Gtk::Table *adj_table = new Gtk::Table(6, 4);
382
383 adj_table->set_row_spacings( 12 );
384 adj_table->set_col_spacings( 12 );
385
386 adj_frame->add( *manage( adj_table ) );
387
388 adj_table->attach( *manage( new Gtk::Label("SF:") ),
389 1, 2, 1, 2,
390 Gtk::FILL, Gtk::AttachOptions() );
391
392 Gtk::SpinButton *spin_sf = new Gtk::SpinButton( *i_pAdjSF );
393
394 adj_table->attach( *manage( spin_sf ),
395 2, 3, 1, 2 );
396
397 adj_table->attach( *manage( new Gtk::Label("SN:") ),
398 1, 2, 2, 3,
399 Gtk::AttachOptions(), Gtk::AttachOptions() );
400
401 Gtk::SpinButton *spin_sn = new Gtk::SpinButton( *i_pAdjSN );
402
403 adj_table->attach( *manage( spin_sn ),
404 2, 3, 2, 3 );
405
406 adj_table->attach( *manage( new Gtk::Label("Trig:") ),
407 1, 2, 3, 4,
408 Gtk::AttachOptions(), Gtk::AttachOptions() );
409
410 Gtk::SpinButton *spin_tr = new Gtk::SpinButton( *i_pAdjTR );
411
412 adj_table->attach( *manage( spin_tr ),
413 2, 3, 3, 4 );
414
415 i_pCBAdapt = new Gtk::CheckButton( "Adapt" );
416
417 adj_table->attach( *manage( i_pCBAdapt ),
418 2, 3, 4, 5 );
419
420 attach( *manage( adj_frame ),
421 1, 3, 3, 4,
422 Gtk::FILL, Gtk::FILL );
423
424 attach( *manage( new Gtk::Label( "" ) ),
425 1, 3, 4, 5, Gtk::AttachOptions() );
426
427 attach( *logview,
428 0, 3, 5, 6,
429 Gtk::EXPAND | Gtk::FILL, Gtk::AttachOptions() );
430
431 logview->set_size_request(0,86);
432
433 spin_tr->set_digits(2);
434
435 i_pAdjSF->signal_value_changed().connect( sigc::mem_fun( this, &MainWidget::setSampFreq ) );
436 i_pAdjSN->signal_value_changed().connect( sigc::mem_fun( this, &MainWidget::setSampNr ) );
437 i_pAdjTR->signal_value_changed().connect( sigc::mem_fun( this, &MainWidget::setTrigger ) );
438
439 i_pAdjSF->signal_value_changed().connect( sigc::mem_fun( oszi, &OsziView::invalidate ) );
440 i_pAdjSN->signal_value_changed().connect( sigc::mem_fun( oszi, &OsziView::invalidate ) );
441
442 i_pCBAdapt->signal_toggled().connect( sigc::mem_fun( this, &MainWidget::setAdaptive ) );
443 }
444
445
proc_audio(Glib::IOCondition cond)446 bool MainWidget::proc_audio(Glib::IOCondition cond)
447 {int i,j,r,got_samples;
448 unsigned char *c;
449 short int *s;
450 unsigned char * sample_uc;
451 short int * sample_s16le;
452 double ldf,mldf;
453 char str[50];
454
455 sample_uc = (unsigned char *)sample;
456 sample_s16le = (short int *)sample;
457 c = sample_uc;
458 s = sample_s16le;
459
460 r = read(audio_fd, c + got_bytes, blksize);
461
462 if (r == -1)
463 {
464 g_critical("error reading audio: %s", strerror(errno));
465 uninit_audio();
466 return false;
467 }
468 if (r == 0)
469 {
470 g_critical("EOF on audio");
471 uninit_audio();
472 return false;
473 }
474
475 got_bytes += r;
476
477 got_samples = got_bytes;
478 if (i_sampfmt == AFMT_S16_LE)
479 got_samples /= 2;
480
481 if (!trig)
482 {
483 // Look for a trigger
484 trigpos=0;
485
486 if(i_sampfmt==AFMT_U8){
487 for( i=0; i<got_samples && Abs(c[i]-128)<2; i++ );
488 } else if(i_sampfmt==AFMT_S16_LE) {
489 for( i=0; i<got_samples && Abs(s[i])<256; i++ );
490 }
491 //i=-1;
492 j=0; trig=0;
493 if (i<got_samples) {
494 if(i_sampfmt==AFMT_U8){
495 for( ; i<got_samples-1; i++ ) /* n-1 because of POSTRIG uses i+1 */
496 if ( POSTRIG(c,i) ) { trig = true; trigpos=i; }
497 }else if(i_sampfmt==AFMT_S16_LE){
498 for( ; i<got_samples-1; i++ ) /* n-1 because of POSTRIG uses i+1 */
499 if ( POSTRIG_S16(s,i) ) { trig = true; trigpos=i; }
500 }
501 }
502 // If we didn't find a trigger then discard the bytes we read
503 if (!trig)
504 got_bytes = 0;
505 }
506 else if (got_samples - trigpos >= sampnr)
507 {
508 if(i_sampfmt==AFMT_U8)
509 oszi->setSampleData( sample_uc + trigpos, sampnr );
510 else if(i_sampfmt==AFMT_S16_LE)
511 oszi->setSampleData( sample_s16le + trigpos, sampnr );
512
513 freq_0t = (double)sampfreq*oszi->get_freq();
514 lfreq_0t = log(freq_0t);
515 while ( lfreq_0t < lfreqs[0]-D_NOTE_LOG/2.0 ) lfreq_0t+=LOG_2;
516 while ( lfreq_0t >= lfreqs[0]+LOG_2-D_NOTE_LOG/2.0 ) lfreq_0t-=LOG_2;
517 mldf=D_NOTE_LOG; note_0t=0;
518 for( i=0; i<12; i++ ){
519 ldf = fabs(lfreq_0t-lfreqs[i]);
520 if (ldf<mldf) { mldf=ldf; note_0t=i; }
521 }
522 logview->change_lfreq(lfreq_0t);
523 sprintf(str,"%0.3f",freq_0t);
524 freqview->set(str);
525 double nfreq_0t=freqs[note_0t];
526 while( nfreq_0t/freq_0t > D_NOTE_SQRT ) nfreq_0t/=2.0;
527 while( freq_0t/nfreq_0t > D_NOTE_SQRT ) nfreq_0t*=2.0;
528 sprintf(str,"%0.3f",nfreq_0t);
529 nfreqview->set(str);
530
531 // printf("Note: %s (%lfHz) Freq=%lf\n",
532 // note[note_0t],freqs[note_0t],freq_0t);
533
534 trig = false;
535 got_bytes = 0;
536 }
537
538 return true;
539 }
540
~MainWidget()541 MainWidget::~MainWidget()
542 {
543 uninit_audio();
544 }
545