10 #include <sys/ioctl.h>
11 #include <linux/soundcard.h>
13 #include "pitchdetector.h"
15 #define SAMPLE_RATE 22050
16 #define FFT_LENGTH 4096 /* in samples */
17 #define PAD_FACTOR 2 /* 1/pf of the FFT samples are real samples, the rest are padding */
18 #define OVERLAP 4 /* 1/ol samples will be replaced in the buffer every frame. Should be
19 * a multiple of 2 for the Hamming window (see
20 * http://www-ccrma.stanford.edu/~jos/parshl/Choice_Hop_Size.html).
23 #define EQUAL_TEMPERAMENT 0
24 #define WELL_TEMPERED_GUITAR 1
26 #define TUNING WELL_TEMPERED_GUITAR
29 void read_chunk(int fd, short *in, unsigned num_samples);
30 void print_spectrogram(double freq, double amp);
31 void write_sine(int dsp_fd, double freq, unsigned num_samples);
35 PitchDetector pd(SAMPLE_RATE, FFT_LENGTH, PAD_FACTOR, OVERLAP);
37 int fd = get_dsp_fd();
39 short buf[FFT_LENGTH / PAD_FACTOR / OVERLAP];
41 read_chunk(fd, buf, FFT_LENGTH / PAD_FACTOR / OVERLAP);
42 std::pair<double, double> peak = pd.detect_pitch(buf);
44 if (peak.first < 50.0 || peak.second - log10(FFT_LENGTH) < 0.0) {
45 #if TUNING == WELL_TEMPERED_GUITAR
48 printf("............\n");
51 print_spectrogram(peak.first, peak.second - log10(FFT_LENGTH));
58 int fd = open("/dev/dsp", O_RDWR);
64 ioctl(3, SNDCTL_DSP_RESET, 0);
66 int fmt = AFMT_S16_LE; // FIXME
67 ioctl(fd, SNDCTL_DSP_SETFMT, &fmt);
70 ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &chan);
72 int rate = SAMPLE_RATE;
73 ioctl(fd, SOUND_PCM_WRITE_RATE, &rate);
75 int max_fragments = 2;
76 int frag_shift = ffs(FFT_LENGTH / OVERLAP) - 1;
77 int fragments = (max_fragments << 16) | frag_shift;
78 ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &fragments);
80 ioctl(3, SNDCTL_DSP_SYNC, 0);
86 void read_chunk(int fd, short *in, unsigned num_samples)
90 ret = read(fd, in, num_samples * sizeof(short));
96 if (ret != int(num_samples * sizeof(short))) {
103 // make a pure 440hz sine for testing
104 void read_chunk(int fd, short *in, unsigned num_samples)
106 static double theta = 0.0;
107 for (unsigned i = 0; i < num_samples; ++i) {
108 in[i] = 32768.0 * cos(theta);
109 theta += 2.0 * M_PI * 440.0 / double(SAMPLE_RATE);
114 void write_sine(int dsp_fd, double freq, unsigned num_samples)
116 static double theta = 0.0;
117 short buf[num_samples];
119 for (unsigned i = 0; i < num_samples; ++i) {
120 buf[i] = short(cos(theta) * 16384.0);
121 theta += 2.0 * M_PI * freq / double(SAMPLE_RATE);
124 write(dsp_fd, buf, num_samples * sizeof(short));
127 std::string freq_to_tonename(double freq)
129 std::string notenames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
130 double half_notes_away = 12.0 * log2(freq / 440.0) - 3.0;
131 int hnai = int(floor(half_notes_away + 0.5));
132 int octave = (hnai + 48) / 12;
135 sprintf(buf, "%s%d + %.2f [%d]", notenames[((hnai % 12) + 12) % 12].c_str(), octave, half_notes_away - hnai, hnai);
139 #if TUNING == EQUAL_TEMPERAMENT
140 void print_spectrogram(double freq, double amp)
142 std::string notenames[] = { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" };
143 double half_notes_away = 12.0 * log2(freq / 440.0) - 3.0;
144 int hnai = int(floor(half_notes_away + 0.5));
145 int octave = (hnai + 48) / 12;
147 for (int i = 0; i < 12; ++i)
148 if (i == ((hnai % 12) + 12) % 12)
153 printf(" (%-2s%d %+.2f, %5.2fHz) [%5.2fdB] [", notenames[((hnai % 12) + 12) % 12].c_str(), octave, half_notes_away - hnai,
156 double off = half_notes_away - hnai;
157 for (int i = -10; i <= 10; ++i) {
158 if (off >= (i-0.5) * 0.05 && off < (i+0.5) * 0.05) {
176 static note notes[] = {
177 { "E-3", 110.0 * (3.0/4.0) },
179 { "D-4", 110.0 * (4.0/3.0) },
180 { "G-4", 110.0 * (4.0/3.0)*(4.0/3.0) },
181 { "B-4", 440.0 * (3.0/4.0)*(3.0/4.0) },
182 { "E-5", 440.0 * (3.0/4.0) }
185 void print_spectrogram(double freq, double amp)
187 double best_away = 999999999.9;
188 unsigned best_away_ind = 0;
190 for (unsigned i = 0; i < sizeof(notes)/sizeof(note); ++i) {
191 double half_notes_away = 12.0 * log2(freq / notes[i].freq);
192 if (fabs(half_notes_away) < fabs(best_away)) {
193 best_away = half_notes_away;
198 for (unsigned i = 0; i < sizeof(notes)/sizeof(note); ++i)
199 if (i == best_away_ind)
204 printf(" (%s %+.2f, %5.2fHz) [%5.2fdB] [", notes[best_away_ind].notename, best_away, freq, amp);
207 for (int i = -10; i <= 10; ++i) {
208 if (best_away >= (i-0.5) * 0.05 && best_away < (i+0.5) * 0.05) {
221 for (int i = -10; i <= 10; ++i) {
222 if (best_away >= (i-0.5) * 0.01 && best_away < (i+0.5) * 0.01) {