Пример #1
0
static void m2_create_surfaces(MF2UI* ui) {
	cairo_t* cr;
	ui->sf_gain = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ANN_W, 40);

#define AMPLABEL(V, O, T, X) \
	{ \
		const float ang = (-.75 * M_PI) + (1.5 * M_PI) * ((V) + (O)) / (T); \
		xlp = X + .5 + sinf (ang) * (10 + 3.0); \
		ylp = 16.5 + .5 - cosf (ang) * (10 + 3.0); \
		cairo_set_line_cap(cr, CAIRO_LINE_CAP_ROUND); \
		CairoSetSouerceRGBA(ui->c_fg); \
		cairo_set_line_width(cr, 1.5); \
		cairo_move_to(cr, rint(xlp)-.5, rint(ylp)-.5); \
		cairo_close_path(cr); \
		cairo_stroke(cr); \
		xlp = X + .5 + sinf (ang) * (10 + 9.5); \
		ylp = 16.5 + .5 - cosf (ang) * (10 + 9.5); \
	}

	ui->sf_dial = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 60, 40);
	cr = cairo_create (ui->sf_dial);
	float xlp, ylp;
	AMPLABEL(-40, 40., 80., 30.5); write_text_full(cr, "-40", ui->font[0], xlp, ylp, 0, 2, ui->c_fg);
	AMPLABEL(-30, 40., 80., 30.5);
	AMPLABEL(-20, 40., 80., 30.5);
	AMPLABEL(-10, 40., 80., 30.5);
	AMPLABEL(  0, 40., 80., 30.5);
	AMPLABEL( 10, 40., 80., 30.5);
	AMPLABEL( 20, 40., 80., 30.5);
	AMPLABEL( 30, 40., 80., 30.5);
	AMPLABEL( 40, 40., 80., 30.5); write_text_full(cr, "+40", ui->font[0], xlp, ylp, 0, 2, ui->c_fg); \
	cairo_destroy (cr);
}
Пример #2
0
static void m1_create_surfaces(MF2UI* ui) {
	cairo_t* cr;
	ui->sf_pc[0] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, PC_WIDTH, 16);
	cr = cairo_create (ui->sf_pc[0]);
	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
	cairo_set_source_rgba(cr, 0, .0, 0, .0);
	cairo_rectangle (cr, 0, 0, PC_WIDTH, 20);
	cairo_fill (cr);
	write_text_full(cr, "+1", ui->font[1], PC_WIDTH / 2, 10, 0, 2, c_ann);
	cairo_destroy (cr);

	ui->sf_pc[1] = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, PC_WIDTH, 16);
	cr = cairo_create (ui->sf_pc[1]);
	cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
	cairo_set_source_rgba(cr, .0, 0, 0, .0);
	cairo_rectangle (cr, 0, 0, PC_WIDTH, 20);
	cairo_fill (cr);
	write_text_full(cr, "-1", ui->font[1], PC_WIDTH / 2, 10, 0, 2, c_ann);
	cairo_destroy (cr);
}
Пример #3
0
static void freq_knob (cairo_t* cr, FilterFreq const * const f)
{
	float xlp, ylp;
	char tfq[8];

	PangoFontDescription* font = pango_font_description_from_string("Mono 9px");

	print_hz(tfq, dial_to_freq(f, 0));
	RESPLABLEL(0.00); write_text_full(cr, tfq, font, xlp, ylp, 0, 1, c_dlf);

	print_hz(tfq, dial_to_freq(f, .25));
	RESPLABLEL(0.25); write_text_full(cr, tfq, font, xlp, ylp, 0, 1, c_dlf);

	print_hz(tfq, dial_to_freq(f, .50));
	RESPLABLEL(0.50); write_text_full(cr, tfq, font, xlp, ylp, 0, 2, c_dlf);

	print_hz(tfq, dial_to_freq(f, .75));
	RESPLABLEL(0.75); write_text_full(cr, tfq, font, xlp-2, ylp, 0, 3, c_dlf);

	print_hz(tfq, dial_to_freq(f, 1.0));
	RESPLABLEL(1.00); write_text_full(cr, tfq, font, xlp-2, ylp, 0, 3, c_dlf);

	pango_font_description_free (font);
}
Пример #4
0
static void gain_knob (cairo_t* cr)
{
	float xlp, ylp;
	PangoFontDescription* font = pango_font_description_from_string("Mono 9px");

	RESPLABLEL(0.00);
	write_text_full(cr, "-18", font, xlp, ylp,      0, 1, c_dlf);
	RESPLABLEL(.16);
	write_text_full(cr, "-12", font, xlp, ylp,      0, 1, c_dlf);
	RESPLABLEL(.33);
	write_text_full(cr,  "-6", font, xlp, ylp,      0, 1, c_dlf);
	RESPLABLEL(0.5);
	write_text_full(cr, "0dB", font, xlp-1, ylp-1,  0, 2, c_dlf);
	RESPLABLEL(.66);
	write_text_full(cr,  "+6", font, xlp-3, ylp,    0, 3, c_dlf);
	RESPLABLEL(.83);
	write_text_full(cr, "+12", font, xlp-3, ylp,    0, 3, c_dlf);
	RESPLABLEL(1.0);
	write_text_full(cr, "+18", font, xlp-3, ylp,    0, 3, c_dlf);
	pango_font_description_free (font);
}
Пример #5
0
static void update_grid(SFSUI* ui) {
	cairo_t *cr = cairo_create (ui->sf_ann);

	cairo_rectangle (cr, 0, 0, ui->width, ui->height);
	CairoSetSouerceRGBA(ui->c_bg);
	cairo_fill (cr);

	cairo_set_line_width (cr, 1.0);

	rounded_rectangle (cr, SS_BORDER, SS_BORDER, SS_SIZE, SS_SIZE, SS_BORDER);
	cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
	cairo_fill(cr);
	rounded_rectangle (cr, SS_BORDER-.5, SS_BORDER-.5, SS_SIZE+1, SS_SIZE+1, SS_BORDER);
	CairoSetSouerceRGBA(c_g90);
	cairo_stroke(cr);

	const double dash1[] = {1.0, 2.0};
	cairo_set_dash(cr, dash1, 2, 0);

	CairoSetSouerceRGBA(c_grd);

#define FREQ_ANN(FRQ, TXT) { \
	const float py = rintf(SS_BORDER + SS_SIZE * (1.0 - fast_log10(1.0 + 2 * FRQ * ui->log_rate / ui->rate) / ui->log_base)) + .5; \
	cairo_move_to(cr, SS_BORDER, py); \
	cairo_line_to(cr, SS_BORDER + SS_SIZE, py); \
	cairo_stroke(cr); \
	write_text_full(cr, TXT, ui->font[0], SS_SIZE, py, 0, -1, c_ahz); \
	}

	float freq = 62.5;
	while (freq < ui->rate / 2) {
		char txt[16];
		if (freq < 1000) {
			snprintf(txt, 16, "%d Hz", (int)ceil(freq));
		} else {
			snprintf(txt, 16, "%d KHz", (int)ceil(freq/1000.f));
		}
		FREQ_ANN(freq, txt);
		freq *= 2.0;
	}

#define LEVEL_ANN(LVL, TXT) { \
	const float dx =  .5 * SS_SIZE * (1 - powf(10, .05 * (LVL))); \
	const float p0 = .5 + rintf(SS_BORDER + .5 * SS_SIZE + dx); \
	const float p1 = .5 + rintf(SS_BORDER + .5 * SS_SIZE - dx); \
	cairo_move_to(cr, p0, SS_BORDER); \
	cairo_line_to(cr, p0, SS_BORDER + SS_SIZE); \
	cairo_stroke(cr); \
	cairo_move_to(cr, p1, SS_BORDER); \
	cairo_line_to(cr, p1, SS_BORDER + SS_SIZE); \
	cairo_stroke(cr); \
	write_text_full(cr, TXT, ui->font[0], p0, SS_BORDER + 3, -.5 * M_PI, -1, c_ahz); \
	write_text_full(cr, TXT, ui->font[0], p1, SS_BORDER + 3, -.5 * M_PI, -1, c_ahz); \
	}

	LEVEL_ANN(-1, "1dB");
	LEVEL_ANN(-3, "3dB");
	LEVEL_ANN(-6, "6dB");
	LEVEL_ANN(-10, "10dB");
	LEVEL_ANN(-20, "20dB");


	const double dash2[] = {1.0, 3.0};
	cairo_set_line_width(cr, 3.5);
	cairo_set_dash(cr, dash2, 2, 2);

	const float xmid = rintf(SS_BORDER + SS_SIZE *.5) + .5;
	cairo_move_to(cr, xmid, SS_BORDER);
	cairo_line_to(cr, xmid, SS_BORDER + SS_SIZE);
	cairo_stroke(cr);

	write_text_full(cr, "L",  ui->font[1], SS_BORDER + 6,           SS_BORDER + 12, 0, -2, c_ann);
	write_text_full(cr, "R",  ui->font[1], SS_BORDER + SS_SIZE - 6, SS_BORDER + 12, 0, -2, c_ann);

	cairo_destroy (cr);
}
Пример #6
0
static bool expose_event (RobWidget* rw, cairo_t* cr, cairo_rectangle_t *ev) {
	BITui* ui = (BITui*)GET_HANDLE(rw);
	cairo_rectangle (cr, ev->x, ev->y, ev->width, ev->height);
	cairo_clip (cr);

	const int ww = rw->area.width;
	const int hh = rw->area.height;

	CairoSetSouerceRGBA(c_g30);
	cairo_rectangle (cr, 0, 0, ww, hh);
	cairo_fill (cr);

	if (!ui->m0_faceplate) {
		gen_faceplate (ui, ww, hh);
	}

	const int spc = (int) floorf ((ww - 28) / 28.) & ~1;
	const int rad = ceilf (spc * .75);
	const int x0r = rint (ww * .5 + 12 * spc);
	const int xpr = rint (ww * .5 - 13 * spc);

	const int spc_s = (int) floorf (ww / 45.) & ~1;
	const int rad_s = ceilf (spc_s * .75);
	const int x0r_s  = ww * .5 + 20 * spc_s;

	const int y0   = hh - 60 - rad_s - spc;
	const int y0_s = hh - 20 - rad_s;

	const int y0_g = 10;
	const int y1_g = y0 - 4;
	const int yh_g = y1_g - y0_g;

	// draw distribution
	if ((int)ui->integration_spl == ui->f_zero) { // all blank
		draw_bit_dist (cr, xpr, y0_g, rad, yh_g, -1);
		for (int k = 0; k < 23; ++k) {
			const float xp = x0r - rintf (spc * (.5 * (k / 8) + k));
			draw_bit_dist (cr, xp, y0_g, rad, yh_g, -1);
		}
	} else {
		const float scnt = ui->integration_spl;
		draw_bit_dist (cr, xpr, y0_g, rad, yh_g, ui->f_pos / scnt);
		for (int k = 0; k < 23; ++k) {
			const float xp = x0r - rintf (spc * (.5 * (k / 8) + k));
			const float v = ui->flt[k + BIM_DSET] / scnt;
			draw_bit_dist (cr, xp, y0_g, rad, yh_g, v);
		}
	}

	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
	cairo_set_source_surface(cr, ui->m0_faceplate, 0, 0);
	cairo_paint (cr);

	/* quick glance boxes */
	// sign - bit
	draw_bit_box (ui, cr, xpr, y0, rad, -1, ui->f_pos);

	// "+" sign
	const float mid = .5 + rintf (spc * .75 * .5);
	const float span = ceilf (spc * .75 * .2);
	CairoSetSouerceRGBA(c_wht);
	cairo_set_line_width (cr, 1);
	cairo_move_to (cr, xpr + mid, y0 + mid - span);
	cairo_line_to (cr, xpr + mid, y0 + mid + span);
	cairo_stroke (cr);
	cairo_move_to (cr, xpr + mid - span, y0 + mid);
	cairo_line_to (cr, xpr + mid + span, y0 + mid);
	cairo_stroke (cr);

	// mantissa
	for (int k = 0; k < 23; ++k) {
		const float xp = x0r - rintf (spc * (.5 * (k / 8) + k));
		draw_bit_box (ui, cr, xp, y0, rad, -1, ui->flt[k + BIM_DSET]);
	}

	// magnitude
	for (int k = 0; k < 40; ++k) {
		const int o = k + 118;
		const float xp = .5 * (k / 8) + k;
		draw_bit_box (ui, cr, x0r_s - rintf (xp * spc_s), y0_s, rad_s, ui->flt[o + BIM_DHIT], ui->flt[o + BIM_DONE]);
	}

	if (ui->integration_spl == 0) {
		cairo_set_source_rgba (cr, 0, 0, 0, .6);
		cairo_rectangle (cr, 0, 0, ww, hh);
		cairo_fill (cr);
		write_text_full (cr, "<markup><b>No data available.</b></markup>",
				FONT(FONT_S), rintf(ww * .5f), rintf(hh * .5f), 0, 2, c_wht);
	}
	else if (ui->integration_spl >= 2147483647) {
		cairo_set_source_rgba (cr, .9, .9, .9, .5);
		cairo_rectangle (cr, 0, 0, ww, hh);
		cairo_fill (cr);
		write_text_full (cr, "<markup>Reached <b>2<sup><small>31</small></sup> sample limit.\nData acquisition suspended.</b></markup>",
				FONT(FONT_S), rintf(ww * .5f), rintf(hh * .5f), 0, 2, c_blk);
	} else if ((int)ui->integration_spl == ui->f_zero) { // all blank
		write_text_full (cr, "<markup><b>All samples are zero.</b></markup>",
				FONT(FONT_S), rintf(ww * .5f), rintf(y0_g + yh_g * .5f), 0, 2, c_wht);
	}
	return TRUE;
}
Пример #7
0
static void gen_faceplate (BITui* ui, const int ww, const int hh) {
	assert(!ui->m0_faceplate);
	ui->m0_faceplate = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ww, hh);
	cairo_t* cr = cairo_create (ui->m0_faceplate);

	cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
	cairo_paint (cr);
	cairo_set_operator (cr, CAIRO_OPERATOR_OVER);

	const int spc = (int) floorf ((ww - 28) / 28.) & ~1;
	const int rad = ceilf (spc * .75);
	const int mid = rint (spc * .75 * .5);
	const int x0r = rint (ww * .5 + 12 * spc);
	const int xpr = rint (ww * .5 - 13 * spc);

	const int spc_s = (int) floorf (ww / 45.) & ~1;
	const int rad_s = ceilf (spc_s * .75);
	const int x0r_s  = ww * .5 + 20 * spc_s;

	const int y0   = hh - 60 - rad_s - spc;
	const int y0_s = hh - 20 - rad_s;

	const int y0_g = 10;
	const int y1_g = y0 - 4;
	const int yh_g = y1_g - y0_g;

	// grid & annotations -- TODO statically allocate surface
	const float x0_g = xpr - 2;
	const float x1_b = x0r + rad + 2;
	const float x1_g = x1_b + mid + 2;
	const float yc_g = rintf (y0_g + .5 * yh_g);
	const float y6_g = rintf (y0_g +  yh_g * 2. / 3.);
	const float y3_g = rintf (y0_g +  yh_g / 3.);

	cairo_rectangle (cr, x1_b, y0_g, mid, y3_g);
	cairo_set_source_rgba (cr, .8, .5, .1, 1.0);
	cairo_fill (cr);
	cairo_rectangle (cr, x1_b, y3_g, mid, y6_g - y3_g);
	cairo_set_source_rgba (cr, .1, .9, .1, 1.0);
	cairo_fill (cr);
	cairo_rectangle (cr, x1_b, y6_g, mid, y1_g - y6_g);
	cairo_set_source_rgba (cr, .1, .6, .9, 1.0);
	cairo_fill (cr);

	cairo_set_line_width (cr, 2);
	cairo_move_to (cr, x1_b, y0_g);
	cairo_line_to (cr, x1_b + mid, y0_g);
	cairo_set_source_rgba (cr, .9, .0, .0, 1.0);
	cairo_stroke (cr);
	cairo_move_to (cr, x1_b, y0_g + yh_g);
	cairo_line_to (cr, x1_b + mid, y0_g + yh_g);
	cairo_set_source_rgba (cr, .0, .0, .9, 1.0);
	cairo_stroke (cr);

	CairoSetSouerceRGBA(c_g80);
	cairo_set_line_width (cr, 1);

	cairo_save (cr);
	double dash = 1;
	cairo_set_line_cap(cr, CAIRO_LINE_CAP_BUTT);
	cairo_set_dash (cr, &dash, 1, 0);

	cairo_move_to (cr, x0_g, y0_g - .5);
	cairo_line_to (cr, x1_b, y0_g - .5);
	cairo_stroke (cr);

	cairo_move_to (cr, x0_g, .5 + yc_g);
	cairo_line_to (cr, x0_g + spc + 4, .5 + yc_g);
	cairo_stroke (cr);

	cairo_move_to (cr, x0_g, .5 + y6_g);
	cairo_line_to (cr, x1_b, .5 + y6_g);
	cairo_stroke (cr);

	cairo_move_to (cr, x0_g, .5 + y3_g);
	cairo_line_to (cr, x1_b, .5 + y3_g);
	cairo_stroke (cr);

	cairo_move_to (cr, x0_g, y1_g + .5);
	cairo_line_to (cr, x1_b, y1_g + .5);
	cairo_stroke (cr);

	cairo_restore (cr);

	cairo_move_to (cr, 1.5 + rintf (x0r_s - 33 * spc_s), y0_s - 1.5);
	cairo_line_to (cr, 1.5 + rintf (x0r_s - 33 * spc_s), y0_s + rad_s + 3.5);
	cairo_line_to (cr, .5 + rintf (x0r_s - 35.5 * spc_s), y0_s + rad_s + 3.5);
	cairo_stroke (cr);


	write_text_full (cr, ">1.0",
				FONT(FONT_M), x0r_s - 33.0 * spc_s, hh - 2, 0, 4, c_wht);
	write_text_full (cr, "<markup>2<small><sup>-32</sup></small></markup>",
				FONT(FONT_M), x0r_s + 0.5 * spc_s, hh - 2, 0, 5, c_wht);
	write_text_full (cr, "<markup>2<small><sup>-24</sup></small></markup>",
				FONT(FONT_M), x0r_s - 8.0 * spc_s, hh - 2, 0, 5, c_wht);
	write_text_full (cr, "<markup>2<small><sup>-16</sup></small></markup>",
				FONT(FONT_M), x0r_s - 16.5 * spc_s, hh - 2, 0, 5, c_wht);
	write_text_full (cr, "<markup>2<small><sup>-8</sup></small></markup>",
				FONT(FONT_M), x0r_s - 25.0 * spc_s, hh - 2, 0, 5, c_wht);
	write_text_full (cr, "<markup>2<small><sup>7</sup></small></markup>",
				FONT(FONT_M), x0r_s - 40.5 * spc_s, hh - 2, 0, 5, c_wht);

	write_text_full (cr, "% time bit is set",
				FONT(FONT_M), x1_g, yc_g, -.5 * M_PI, 8, c_wht);

	write_text_full (cr, "100%", FONT(FONT_M), x0_g - 2, y0_g, 0, 1, c_wht);
	write_text_full (cr, "50%", FONT(FONT_M), x0_g - 2, yc_g, 0, 1, c_wht);
	write_text_full (cr, "0%", FONT(FONT_M), x0_g - 2, y1_g, 0, 1, c_wht);

	// sep
	int ysep = .5 * (y0 + rad + y0_s);
	CairoSetSouerceRGBA(c_g60);
	cairo_move_to (cr, 15, ysep + .5);
	cairo_line_to (cr, ww - 30 , ysep + .5);
	cairo_stroke (cr);
	write_text_full (cr, "Sign & Mantissa (23bit significand)", FONT(FONT_S), ww * .5, ysep - 2, 0, 5, c_wht);
	write_text_full (cr, "Full Scale", FONT(FONT_S), ww * .5, ysep + 3, 0, 8, c_wht);

	write_text_full (cr, ui->nfo, FONT(FONT_M), 2, hh -2, 1.5 * M_PI, 9, c_gry);

	cairo_destroy (cr);
}
Пример #8
0
/** draw level-range display
 * depends on gain (dial) and cutoff
 */
static void update_annotations(MF2UI* ui) {
	cairo_t* cr = cairo_create (ui->sf_gain);

	cairo_rectangle (cr, 0, 0, ANN_W, 40);
	CairoSetSouerceRGBA(ui->c_bg);
	cairo_fill (cr);

	rounded_rectangle (cr, 3, 3 , ANN_W - 6, ANN_H - 6, 6);
	if (ui->drag_cutoff_x >= 0 || ui->prelight_cutoff) {
		cairo_set_source_rgba(cr, .15, .15, .15, 1.0);
	} else {
		cairo_set_source_rgba(cr, .0, .0, .0, 1.0);
	}
	cairo_fill (cr);

	cairo_set_line_width (cr, 1.0);
	const uint32_t mxw = ANN_W - XOFF * 2 - 36;
	const uint32_t mxo = XOFF + 18;
	const float cutoff = ui->db_cutoff;
	const uint32_t   cutoff_m = floor(mxw * (MIN_CUTOFF - cutoff) / MIN_CUTOFF);
	assert(cutoff_m < mxw);
	const uint32_t   cutoff_w = mxw - cutoff_m;

	for (uint32_t i=0; i < mxw; ++i) {
		float clr[3];
		if (i < cutoff_m) {
			clr[0] = clr[1] = clr[2] = .1;
		} else {
			const float pk = (i-cutoff_m) / (float)cutoff_w;
			hsl2rgb(clr, .68 - .72 * pk, .9, .2 + pk * .4);
		}

		cairo_set_source_rgba(cr, clr[0], clr[1], clr[2], 1.0);

		cairo_move_to(cr, mxo + i + .5, ANN_B - 5);
		cairo_line_to(cr, mxo + i + .5, ANN_B);
		cairo_stroke(cr);
	}

	cairo_set_source_rgba(cr, .8, .8, .8, .8);

	const float gain = robtk_dial_get_value(ui->gain);
	for (int32_t db = MIN_CUTOFF; db <=0 ; db+= 10) {
		char dbt[16];
		if (db == 0) {
			snprintf(dbt, 16, "\u2265%+.0fdB", (db - gain));
		} else {
			snprintf(dbt, 16, "%+.0fdB", (db - gain));
		}
		write_text_full(cr, dbt, ui->font[0], mxo + rint(mxw * (-MIN_CUTOFF + db) / -MIN_CUTOFF), ANN_B - 14 , 0, 2, c_wht);
		cairo_move_to(cr, mxo + rint(mxw * (-MIN_CUTOFF + db) / -MIN_CUTOFF) + .5, ANN_B - 7);
		cairo_line_to(cr, mxo + rint(mxw * (-MIN_CUTOFF + db) / -MIN_CUTOFF) + .5, ANN_B);
		cairo_stroke(cr);
	}

	/* black overlay above low-end cutoff */
	if (ui->db_cutoff > MIN_CUTOFF && (ui->drag_cutoff_x >= 0 || ui->prelight_cutoff)) {
		const float cox = rint(mxw * (ui->db_cutoff - MIN_CUTOFF)/ -MIN_CUTOFF);
		cairo_rectangle(cr, mxo, 6, cox, ANN_B - 6);
		cairo_set_source_rgba(cr, .0, .0, .0, .7);
		cairo_fill(cr);

		cairo_set_line_width (cr, 1.0);
		cairo_set_source_rgba(cr, .9, .5, .5, .6);
		cairo_move_to(cr, mxo + cox + .5, ANN_B - 6);
		cairo_line_to(cr, mxo + cox + .5, ANN_B + 1);
		cairo_stroke(cr);
	}

	cairo_destroy (cr);
}
Пример #9
0
/** draw frequency calibration circles
 * and on screen annotations - sample-rate dependent
 */
static void update_grid(MF2UI* ui) {
	const double ccc = ui->width / 2.0 + .5;
	const double rad = (ui->width - XOFF) * .5;
	cairo_t *cr = cairo_create (ui->sf_ann);

	cairo_rectangle (cr, 0, 0, ui->width, ui->height);
	CairoSetSouerceRGBA(ui->c_bg);
	cairo_fill (cr);

	cairo_set_line_width (cr, 1.0);

	cairo_arc (cr, ccc, ccc, rad, 0, 2.0 * M_PI);
	cairo_set_source_rgba(cr, 0, 0, 0, 1.0);
	cairo_fill_preserve(cr);
	CairoSetSouerceRGBA(c_g90);
	cairo_stroke(cr);

	const double dash1[] = {1.0, 2.0};
	cairo_set_dash(cr, dash1, 2, 0);

	CairoSetSouerceRGBA(c_grd);

	float freq = 62.5;
	while (freq < ui->rate / 2) {
		char txt[16];
		if (freq < 1000) {
			snprintf(txt, 16, "%d Hz", (int)ceil(freq));
		} else {
			snprintf(txt, 16, "%d KHz", (int)ceil(freq/1000.f));
		}

		{
			const float dr = ui->scale * PH_RAD * fast_log10(1.0 + 2 * freq * ui->log_rate / ui->rate) / ui->log_base;
			cairo_arc (cr, ccc, ccc, dr, 0, 2.0 * M_PI);
			cairo_stroke(cr);
			const float px = ccc + dr * sinf(M_PI * -.75);
			const float py = ccc - dr * cosf(M_PI * -.75);
			write_text_full(cr, txt, ui->font[0], px, py, M_PI * -.75, -2, c_ahz);
		}

		freq *= 2.0;
	}

	const double dash2[] = {1.0, 3.0};
	cairo_set_line_width(cr, 3.5);
	cairo_set_dash(cr, dash2, 2, 2);

	cairo_set_line_width(cr, 1.5);
	cairo_move_to(cr, ccc - rad, ccc);
	cairo_line_to(cr, ccc + rad, ccc);
	cairo_stroke(cr);

	cairo_set_line_width(cr, 3.5);
	cairo_move_to(cr, ccc, ccc - rad);
	cairo_line_to(cr, ccc, ccc + rad);
	cairo_stroke(cr);
	cairo_set_dash(cr, NULL, 0, 0);

	write_text_full(cr, "+L",  ui->font[0], ccc, ccc - rad * .92, 0, -2, c_ann);
	write_text_full(cr, "-L",  ui->font[0], ccc, ccc + rad * .92, 0, -2, c_ann);
	write_text_full(cr, "0\u00B0",  ui->font[0], ccc, ccc - rad * .80, 0, -2, c_ann);
	write_text_full(cr, "180\u00B0",  ui->font[0], ccc, ccc + rad * .80, 0, -2, c_ann);

	write_text_full(cr, "-R",  ui->font[0], ccc - rad * .92, ccc, 0, -2, c_ann);
	write_text_full(cr, "+R",  ui->font[0], ccc + rad * .92, ccc, 0, -2, c_ann);
	write_text_full(cr, "-90\u00B0",  ui->font[0], ccc - rad * .80, ccc, 0, -2, c_ann);
	write_text_full(cr, "+90\u00B0",  ui->font[0], ccc + rad * .80, ccc, 0, -2, c_ann);
	cairo_destroy (cr);
}