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