[xiph-commits] r18812 - in trunk: . Xiph-episode-II Xiph-episode-II/bounce Xiph-episode-II/cairo
xiphmont at svn.xiph.org
xiphmont at svn.xiph.org
Sun Feb 24 14:04:10 PST 2013
Author: xiphmont
Date: 2013-02-24 14:04:10 -0800 (Sun, 24 Feb 2013)
New Revision: 18812
Added:
trunk/Xiph-episode-II/
trunk/Xiph-episode-II/bounce/
trunk/Xiph-episode-II/bounce/Makefile
trunk/Xiph-episode-II/bounce/gtk-bounce-audio.c
trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.c
trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.h
trunk/Xiph-episode-II/bounce/gtk-bounce-dither.c
trunk/Xiph-episode-II/bounce/gtk-bounce-dither.h
trunk/Xiph-episode-II/bounce/gtk-bounce-filter.c
trunk/Xiph-episode-II/bounce/gtk-bounce-filter.h
trunk/Xiph-episode-II/bounce/gtk-bounce-panel.c
trunk/Xiph-episode-II/bounce/gtk-bounce-wavheader.c
trunk/Xiph-episode-II/bounce/gtk-bounce-widget.c
trunk/Xiph-episode-II/bounce/gtk-bounce-widget.h
trunk/Xiph-episode-II/bounce/gtk-bounce.c
trunk/Xiph-episode-II/bounce/gtk-bounce.c.old
trunk/Xiph-episode-II/bounce/gtk-bounce.h
trunk/Xiph-episode-II/bounce/twopole.c
trunk/Xiph-episode-II/bounce/twopole.h
trunk/Xiph-episode-II/cairo/
trunk/Xiph-episode-II/cairo/README
trunk/Xiph-episode-II/cairo/ch3_convenient.c
trunk/Xiph-episode-II/cairo/ch3_minutely.c
trunk/Xiph-episode-II/cairo/ch3_samples.c
trunk/Xiph-episode-II/cairo/ch3_stairsteps.c
trunk/Xiph-episode-II/cairo/ch3_zerohold.c
trunk/Xiph-episode-II/cairo/ch4-samplequant.c
trunk/Xiph-episode-II/cairo/ch5-quantize.c
trunk/Xiph-episode-II/cairo/ch6-overlay.c
trunk/Xiph-episode-II/cairo/ch6-right.c
trunk/Xiph-episode-II/cairo/ch6-squareband.c
trunk/Xiph-episode-II/cairo/ch6-squarefreq.c
trunk/Xiph-episode-II/cairo/ch6-squaretime.c
trunk/Xiph-episode-II/cairo/ch6-wrong.c
Log:
Initial import of source exactly as used in video
Added: trunk/Xiph-episode-II/bounce/Makefile
===================================================================
--- trunk/Xiph-episode-II/bounce/Makefile (rev 0)
+++ trunk/Xiph-episode-II/bounce/Makefile 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,41 @@
+CC=gcc
+LD=gcc
+INSTALL=install
+PREFIX=/usr/local
+BINDIR=$(PREFIX)/bin
+MANDIR=$(PREFIX)/man
+
+SRC = gtk-bounce-panel.c gtk-bounce-audio.c gtk-bounce-widget.c twopole.c \
+ gtk-bounce-dither.c gtk-bounce-filter.c gtk-bounce-wavheader.c
+OBJ = gtk-bounce-panel.o gtk-bounce-audio.o gtk-bounce-widget.o twopole.o \
+ gtk-bounce-dither.o gtk-bounce-filter.o gtk-bounce-wavheader.o
+
+GCF = `pkg-config --cflags gtk+-2.0` -DETCDIR=$(ETCDIR)
+
+CFLAGS := ${CFLAGS} $(GCF) $(ADD_DEF)
+LIBS := -lfftw3 -lfftw3f -lasound -lm -lpthread
+
+all:
+ $(MAKE) target CFLAGS="${CFLAGS} -g -O2 -ffast-math -fomit-frame-pointer"
+
+debug:
+ $(MAKE) target CFLAGS="${CFLAGS} -g -Wall -W -Wno-unused-parameter -D__NO_MATH_INLINES"
+
+profile:
+ $(MAKE) target CFLAGS="${CFLAGS} -pg -g -O2 -ffast-math"
+
+clean:
+ rm -f $(OBJ) *.d *.d.* gmon.out gtk-bounce
+
+%.d: %.c
+ $(CC) -M $(CFLAGS) $< > $@.$$$$; sed 's,\($*\)\.o[ :]*,\1.o $@ : ,g' < $@.$$$$ > $@; rm -f $@.$$$$
+
+ifeq ($(MAKECMDGOALS),target)
+include $(SRC:.c=.d)
+endif
+
+gtk-bounce: $(OBJ)
+ $(LD) $(OBJ) -o gtk-bounce $(LIBS) $(CFLAGS) `pkg-config --libs gtk+-2.0`
+
+target: gtk-bounce
+
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-audio.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-audio.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-audio.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,1149 @@
+#include "gtk-bounce.h"
+#include "gtk-bounce-filter.h"
+#include "gtk-bounce-dither.h"
+#include <time.h>
+#include <alsa/asoundlib.h>
+
+extern int header_out(FILE *out, int rate, int bits, int channels);
+
+static snd_pcm_uframes_t pframes = 128;
+static snd_pcm_uframes_t pframes_mark = 128*8;
+static snd_pcm_uframes_t bframes = 4096;
+
+/* only apply to ch0 and ch1 */
+
+static shapestate shapestate_ch0;
+static shapestate shapestate_ch1;
+
+static notchfilter *notch[2]={0,0};
+static convofilter *lowpass;
+static convostate *lowpass_state[2]={0,0};
+static int active_1kHz_notch;
+static int active_lp_filter;
+static int state_1kHz_modulation = 0;
+static float amplitude_1kHz_modmix = 0;
+
+static int param_setup_i(snd_pcm_t *devhandle, int dir, int rate,
+ int format, int channels){
+ int ret;
+ snd_pcm_hw_params_t *hw;
+ snd_pcm_sw_params_t *sw;
+ char *prompt = (!dir ? "capture" : "playback");
+
+ /* allocate the parameter structures */
+ snd_pcm_hw_params_alloca (&hw);
+ snd_pcm_sw_params_alloca (&sw);
+
+ /* fetch all possible hardware parameters */
+ if ((ret = snd_pcm_hw_params_any (devhandle, hw)) < 0) {
+ fprintf (stderr, "%s cannot fetch hardware parameters (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* use file-like syscall interface as opposed to mmap */
+ if ((ret = snd_pcm_hw_params_set_access (devhandle, hw, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) {
+ fprintf (stderr, "%s cannot set access type (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* set the sample bitformat */
+ if ((ret = snd_pcm_hw_params_set_format (devhandle, hw, format)) < 0) {
+ fprintf (stderr, "%s cannot set sample format (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* set the sample rate */
+ if ((ret = snd_pcm_hw_params_set_rate (devhandle, hw, rate, 0)) < 0) {
+ fprintf (stderr, "%s cannot set sample rate (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* set the number of channels */
+ if ((ret = snd_pcm_hw_params_set_channels (devhandle, hw, channels)) < 0) {
+ fprintf (stderr, "%s cannot set channel count (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* set the time per hardware sample transfer */
+ if ((ret = snd_pcm_hw_params_set_period_size_near(devhandle,
+ hw, &pframes, 0)) < 0) {
+
+ fprintf (stderr, "%s cannot set period size (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* set the length of the hardware sample buffer */
+ if ((ret = snd_pcm_hw_params_set_buffer_size_near(devhandle,
+ hw, &bframes)) < 0) {
+
+ fprintf (stderr, "%s cannot set buffer size (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ if ((ret = snd_pcm_hw_params (devhandle, hw)) < 0) {
+ fprintf (stderr, "%s cannot set hard parameters (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ if((ret = snd_pcm_hw_params_get_period_size(hw, &pframes, 0)) < 0){
+ fprintf (stderr, "%s cannot query period size (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ if((ret = snd_pcm_hw_params_get_buffer_size(hw, &bframes)) < 0){
+ fprintf (stderr, "%s cannot query buffer size (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* fetch software parameter settings */
+ if ((ret = snd_pcm_sw_params_current (devhandle, sw)) < 0) {
+ fprintf (stderr, "%s cannot initialize hardware parameter structure (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* start transfers at lowest possible buffer watermark */
+ if(dir){
+ if ((ret = snd_pcm_sw_params_set_start_threshold (devhandle, sw, pframes_mark)) < 0) {
+ fprintf (stderr, "%s cannot set start threshold (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+ }else{
+ if ((ret = snd_pcm_sw_params_set_start_threshold (devhandle, sw, pframes)) < 0) {
+ fprintf (stderr, "%s cannot set start threshold (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+ }
+ if ((ret = snd_pcm_sw_params_set_avail_min (devhandle, sw, pframes)) < 0) {
+ fprintf (stderr, "%s cannot set minimum activity threshold (%s)\n",
+ prompt,snd_strerror (ret));
+ return ret;
+ }
+
+ if ((ret = snd_pcm_sw_params (devhandle, sw)) < 0) {
+ fprintf (stderr, "%s cannot set soft parameters (%s)\n",
+ prompt, snd_strerror (ret));
+ return ret;
+ }
+
+ /* go time */
+ if ((ret = snd_pcm_prepare (devhandle)) < 0) {
+ fprintf (stderr, "%s cannot prepare audio interface for use (%s)\n",
+ prompt,snd_strerror (ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int param_setup(snd_pcm_t *devhandle, int dir, int rate,
+ int bits, int *channels){
+
+ /* The eMagic 2|6 only does 24 bit playback if also using 6
+ channels. The Focusrite Scarlett 2i2 can only do 32 bit
+ output */
+ int format_list[3] = {SND_PCM_FORMAT_S16_LE,
+ SND_PCM_FORMAT_S24_3LE,
+ SND_PCM_FORMAT_S32_LE};
+ int channel_list[2] = {2,6};
+
+ int fi = (bits==16?0:1);
+ int ci;
+
+ for(;fi<3;fi++){
+ for(ci=0;ci<2;ci++){
+ if(!param_setup_i(devhandle,dir,rate,format_list[fi],channel_list[ci])){
+ *channels = channel_list[ci];
+ return format_list[fi];
+ }
+ }
+ }
+ return -1;
+}
+
+static void alsa_to_dev_null(const char *file,
+ int line,
+ const char *function,
+ int err,
+ const char *fmt, ...){
+}
+
+/* capture open blocks as we can't do anything much without a capture
+ clock */
+static snd_pcm_t *open_capture(char *dev, int rate, int bits, int *channels, int *format){
+ snd_pcm_t *handle;
+ int ret,fail=0;
+ struct timespec t = {0,250000000};
+ while((ret=snd_pcm_open(&handle, dev, SND_PCM_STREAM_CAPTURE, 0)) < 0) {
+ if(ret!=-ENOENT){
+ /* A device that's in the middle of initializing may throw a
+ random error */
+ fail++;
+ if(fail==3)return NULL;
+ }
+ nanosleep(&t,NULL);
+ }
+
+ *format = param_setup(handle,0,rate,bits,channels);
+ if(*format<0){
+ snd_pcm_close(handle);
+ return NULL;
+ }
+
+ memset(&shapestate_ch0,0,sizeof(shapestate_ch0));
+ memset(&shapestate_ch1,0,sizeof(shapestate_ch1));
+
+ return handle;
+}
+
+/* don't block; try it and if we fail, fail quickly */
+static snd_pcm_t *open_playback(char *dev, int rate, int bits,
+ int *channels, int *format){
+ snd_pcm_t *handle;
+ int ret;
+
+ if ((ret = snd_pcm_open (&handle, dev, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
+ return NULL;
+ }
+
+ *format = param_setup(handle,1,rate,bits,channels);
+ if(*format<0){
+ snd_pcm_close(handle);
+ return NULL;
+ }
+
+ return handle;
+}
+
+static float *square_coeffs;
+static float *square_phases;
+static int square_coeffs_n;
+static double square_coeffs_w;
+
+/* brute-force the expansion of a synthetic aliased 'squarewave';
+ choose a fundamental that evenly divides the rate, doesn't land a
+ harmonic at nyquist, and has an integer period, so that each folded
+ component lands directly on another. */
+/* we don't adjust for phase through the complete aliased expansion
+ during later generation because this isn't a real squarewave. The
+ additional mirrored components exist only to fill in the illusory
+ appearance of it being a squarewave when drawn with a zero-hold at
+ phase=0 */
+
+void generate_squarewave_expansion(int rate){
+ int period = rint(rate/4000.)*4;
+ //double freq = rate/period;
+ double w = 2./period*M_PI;
+ int coeffs = period/4;
+ int i,j;
+ float *work = fftwf_malloc((period+2)*sizeof(*work));
+ fftwf_plan plan = fftwf_plan_dft_r2c_1d(period,work,
+ (fftwf_complex *)work,
+ FFTW_ESTIMATE);
+
+ for(i=0;i<period/2;i++)
+ work[i]=1.;
+ for(;i<period;i++)
+ work[i]=-1.;
+ fftwf_execute(plan);
+
+ square_coeffs_n = coeffs;
+ square_coeffs_w = w;
+ if(square_coeffs)free(square_coeffs);
+ if(square_phases)free(square_phases);
+ square_coeffs = calloc(coeffs,sizeof(*square_coeffs));
+ square_phases = calloc(coeffs,sizeof(*square_phases));
+
+ for(i=1,j=0;j<square_coeffs_n;i+=2,j++){
+ square_coeffs[j] = hypotf(work[i<<1],work[(i<<1)+1]) / period;
+ square_phases[j] = atan2f(work[(i<<1)+1],work[i<<1]);
+ }
+
+}
+
+void *io_thread(void *dummy){
+ snd_pcm_t *in_devhandle=NULL;
+ snd_pcm_t *out_devhandle=NULL;
+ FILE *outpipe0=NULL;
+ FILE *outpipe1=NULL;
+ pole2 filter_1kHz_amplitude;
+ pole2 filter_5kHz_amplitude;
+ pole2 filter_1kHz_mix;
+ pole2 filter_output_noise;
+ pole2 filter_output_sweep;
+ pole2 filter_modulation1;
+ pole2 filter_modulation2;
+
+ int channels = 2; //request_ch;
+ int in_channels = 2;
+ int out_channels = 2;
+ int rate = request_rate;
+ int bits = request_bits;
+ int informat=-1;
+ int outformat=-1;
+
+ int i,j;
+
+ unsigned char *iobuffer=NULL;
+ float *crossbuffer;
+ float *fbuffer[32];
+
+ filter_make_critical(.0001,1,&filter_1kHz_amplitude);
+ filter_make_critical(.0001,1,&filter_5kHz_amplitude);
+ filter_make_critical(.0001,1,&filter_1kHz_mix);
+ filter_make_critical(.0001,1,&filter_output_noise);
+ filter_make_critical(.0001,1,&filter_output_sweep);
+ filter_make_critical(.00005,2,&filter_modulation1);
+ filter_make_critical(.00005,2,&filter_modulation2);
+
+ /* libasound prints debugging output directly to stderr; tell it to
+ shut up */
+ snd_lib_error_set_handler(alsa_to_dev_null);
+
+ while(!exiting){
+
+ if(request_bits != bits || request_rate!=rate){
+ /* the panel has requested a differnet bitdepth than we're running.
+ Close any already-open devices, reopen below */
+ bits=request_bits;
+ //channels=request_ch;
+ rate=request_rate;
+
+ if(notch[0])filter_notch_destroy(notch[0]);
+ notch[0]=NULL;
+ if(notch[1])filter_notch_destroy(notch[1]);
+ notch[1]=NULL;
+ if(lowpass)convofilter_destroy(lowpass);
+ lowpass=NULL;
+
+ if(lowpass_state[0])convostate_destroy(lowpass_state[0]);
+ lowpass_state[0]=NULL;
+ if(lowpass_state[1])convostate_destroy(lowpass_state[1]);
+ lowpass_state[1]=NULL;
+
+ if(in_devhandle){
+ snd_pcm_close(in_devhandle);
+ in_devhandle=NULL;
+ write(eventpipe[1],"\002",1);
+ }
+ if(out_devhandle){
+ snd_pcm_close(out_devhandle);
+ out_devhandle=NULL;
+ write(eventpipe[1],"\002",1);
+ }
+ if(outpipe0) fclose(outpipe0);
+ if(outpipe1) fclose(outpipe1);
+
+ if(outpipe0 || outpipe1){
+ /* solve a race with a sleep; we want the readers to realize
+ this closed */
+ struct timespec t = {0,250000000};
+ nanosleep(&t,NULL);
+ outpipe0=NULL;
+ outpipe1=NULL;
+
+ if(in_devhandle)
+ snd_pcm_drop(in_devhandle);
+ if(out_devhandle)
+ snd_pcm_drop(out_devhandle);
+ if(in_devhandle)
+ snd_pcm_prepare(in_devhandle);
+ if(out_devhandle)
+ snd_pcm_prepare(out_devhandle);
+
+ }
+ }
+
+ if(!outpipe0){
+ int fd = open("pipe0", O_WRONLY|O_NONBLOCK);
+ if(fd>=0){
+ outpipe0 = fdopen(fd,"wb");
+ if(outpipe0==NULL){
+ close(fd);
+ write(eventpipe[1],"\000",1);
+ }
+ header_out(outpipe0,rate,bits,channels);
+ fprintf(stderr,"Opened outpipe0\n");
+ }
+ }
+
+ if(!outpipe1){
+ int fd = open("pipe1", O_WRONLY|O_NONBLOCK);
+ if(fd>=0){
+ outpipe1 = fdopen(fd,"wb");
+ if(outpipe1==NULL){
+ close(fd);
+ write(eventpipe[1],"\000",1);
+ }
+ header_out(outpipe1,rate,bits,channels);
+ fprintf(stderr,"Opened outpipe1\n");
+ }
+ }
+
+ if(!in_devhandle || !out_devhandle){
+
+
+ /* assume that hw:1 is always our capture handle, and if we don't
+ have capture, we can't do anything */
+ if(!in_devhandle){
+ in_channels = channels;
+ in_devhandle = open_capture("hw:1",rate,bits,&in_channels,&informat);
+
+ if(in_devhandle){
+ /* configure/reconfigure buffering for i/o */
+ int maxchannels=8;
+ if(iobuffer){
+ iobuffer=realloc(iobuffer,pframes*maxchannels*4);
+ for(i=0;i<maxchannels;i++)
+ fbuffer[i]=realloc(fbuffer[i],pframes*sizeof(**fbuffer));
+ crossbuffer=realloc(crossbuffer,pframes*sizeof(**fbuffer));
+ }else{
+ iobuffer=malloc(pframes*maxchannels*4);
+ for(i=0;i<maxchannels;i++)
+ fbuffer[i]=malloc(pframes*sizeof(**fbuffer));
+ crossbuffer=malloc(pframes*sizeof(**fbuffer));
+ }
+
+ /* squarewave */
+ generate_squarewave_expansion(rate);
+
+ /* set-up our on-demand filtering that has
+ expensive-to-construct persistent state */
+
+ notch[0]=filter_notch_new(1000.,request_rate);
+ lowpass=filter_lowpass_new(20500,request_rate,512);
+
+ lowpass_state[0] = convostate_new(lowpass);
+ if(channels>1){
+ notch[1]=filter_notch_new(1000.,request_rate);
+ lowpass_state[1] = convostate_new(lowpass);
+ }
+ }
+ }
+
+ if(!out_devhandle){
+ out_channels = channels;
+ out_devhandle = open_playback("hw:1",rate,bits,
+ &out_channels,&outformat);
+ }
+
+ if(in_devhandle && out_devhandle){
+ char *name;
+ snd_card_get_name(1,&name);
+ unsigned char len = strlen(name)+1;
+ write(eventpipe[1],"\001",1);
+ write(eventpipe[1],&len,1);
+ write(eventpipe[1],name,(int)len);
+ }
+ }
+
+ /* capture a block of input */
+ if(in_devhandle){
+ int frames = snd_pcm_readi (in_devhandle, iobuffer, pframes);
+ if(frames<0){
+ switch(frames){
+ case -EAGAIN:
+ break;
+ case -EPIPE: case -ESTRPIPE:
+ // underrun; set starve and reset soft device
+ snd_pcm_drop(in_devhandle);
+ if(out_devhandle)
+ snd_pcm_drop(out_devhandle);
+ snd_pcm_prepare(in_devhandle);
+ if(out_devhandle)
+ snd_pcm_prepare(out_devhandle);
+ frames=0;
+ fprintf(stderr,"\n CAPTURE XRUN @ (%ld)\n ", (long)time(NULL));
+ break;
+ case -EBADFD:
+ default:
+ /* shut down device */
+ snd_pcm_close(in_devhandle);
+ in_devhandle=NULL;
+ write(eventpipe[1],"\002",1);
+ frames=0;
+
+ if(outpipe0) fclose(outpipe0);
+ if(outpipe1) fclose(outpipe1);
+
+ if(outpipe0 || outpipe1){
+ /* solve a race with a sleep; we want the readers to realize
+ this closed */
+ struct timespec t = {0,250000000};
+ nanosleep(&t,NULL);
+ outpipe0=NULL;
+ outpipe1=NULL;
+ }
+
+ break;
+ }
+ }else{
+
+ /* convert our frames to 32-bit float intermediary */
+ unsigned char *bufp = iobuffer;
+ switch(informat){
+ case SND_PCM_FORMAT_S32_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<in_channels;j++){
+ int32_t in = bufp[0]|(bufp[1]<<8)|(bufp[2]<<16)|(bufp[3]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=4;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S32_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = bufp[3]|(bufp[2]<<8)|(bufp[1]<<16)|(bufp[0]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=4;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S24_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[1]<<8)|(bufp[2]<<16)|(bufp[3]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=4;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S24_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[2]<<8)|(bufp[1]<<16)|(bufp[0]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=4;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S24_3LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[0]<<8)|(bufp[1]<<16)|(bufp[2]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=3;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S24_3BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[2]<<8)|(bufp[1]<<16)|(bufp[0]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=3;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S16_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[0]<<16)|(bufp[1]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=2;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ case SND_PCM_FORMAT_S16_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int32_t in = (bufp[1]<<16)|(bufp[0]<<24);
+ fbuffer[j][i]= in * (1./128./256./256./256.);
+ bufp+=2;
+ }
+ for(;j<channels;j++) fbuffer[j][i]=0;
+ }
+ break;
+ }
+
+ //if(!(request_output_silence ||
+ // request_output_tone ||
+ // request_output_noise ||
+ // request_output_sweep ||
+ // request_output_logsweep)){
+ ///* not in panel 10; mirror input 0 into input 1 */
+ //memcpy(fbuffer[1],fbuffer[0],sizeof(**fbuffer)*frames);
+ //}
+
+ /* save the ch0 input in two-input mode */
+ if(request_output_duallisten && channels>1){
+ float *temp = crossbuffer;
+ crossbuffer=fbuffer[0];
+ fbuffer[0]=temp;
+ }
+
+ if(request_output_silence){
+ for(i=0;i<frames;i++)
+ fbuffer[0][i]=0;
+ }
+
+ /* input lowpass */
+ if((request_lp_filter || active_lp_filter) && !request_1kHz_square){
+ active_lp_filter =
+ run_convolution_filter(lowpass,lowpass_state[0],
+ fbuffer[0],
+ frames,0,request_lp_filter,1.);
+
+ if(channels>1)
+ active_lp_filter |=
+ run_convolution_filter(lowpass,lowpass_state[1],
+ fbuffer[1],
+ frames,0,request_lp_filter,1.);
+ }
+
+ /* panel 10 white noise burst */
+ static int count_output_noise;
+ static float mix_output_noise;
+ if(request_output_noise || mix_output_noise>0.){
+ float target = request_output_noise ? 1. : 0.;
+ if(request_output_noise && count_output_noise==0)
+ count_output_noise = 10 * rate;
+
+ if(fabsf(mix_output_noise - target) > .0000001){
+ float m;
+ for(i=0;i<frames;i++){
+ float v = .99*(drand48()-drand48());
+ m = filter_filter(target,&filter_output_noise);
+ fbuffer[0][i] = v*m;
+ }
+ mix_output_noise = m;
+ }else{
+ mix_output_noise = target;
+ if(request_output_noise){
+ for(i=0;i<frames;i++){
+ float v = .99*(drand48()-drand48());
+ fbuffer[0][i] = v;
+ }
+ }else
+ count_output_noise=0;
+ }
+ count_output_noise-=frames;
+ if(count_output_noise<=0){
+ count_output_noise=0;
+ request_output_noise=0;
+ write(eventpipe[1],"\006",1);
+ }
+ }
+
+ /* panel 10 linear/log sweep */
+ static float mix_output_sweep=0.;
+ if(request_output_sweep ||
+ request_output_logsweep ||
+ mix_output_sweep){
+ float target =
+ (request_output_sweep||request_output_logsweep) ? 1. : 0.;
+
+ double totalsamples = rate * 20.;
+ float m;
+
+ static int logp;
+ static double theta = 0.;
+ static double samples = 0.;
+ static double d;
+ double l0 = log(5./rate*2*M_PI);
+ double l1 = log(M_PI);
+ double dd = logp ? (l1-l0)/totalsamples : M_PI/totalsamples;
+
+ if(request_output_logsweep)logp=1;
+ if(request_output_sweep)logp=0;
+ if(samples==0.){
+ //filter_set(&filter_output_sweep,1.);
+ d = logp ? l0 : 0;
+ }
+
+ for(i=0;i<frames;i++){
+ double instf = logp ? exp(d) : d;
+ float v = .99*sin(theta);
+ m = filter_filter(target,&filter_output_sweep);
+
+ d+=dd;
+ theta+=instf;
+ samples++;
+
+ if(theta>2*M_PI)theta-=2.*M_PI;
+ if(totalsamples-samples<2000){
+ target=0;
+ request_output_sweep=0;
+ request_output_logsweep=0;
+ }
+ fbuffer[0][i] = v*m;
+ }
+
+ mix_output_sweep=m;
+ if(!request_output_sweep &&
+ !request_output_logsweep &&
+ m<.00000001){
+ mix_output_sweep=0;
+ samples=0;
+ theta=0;
+ write(eventpipe[1],"\006",1);
+ }
+ }
+
+ /* synthetic ~ 1kHz 'squarewave' */
+ /* generate it via previously computed Fourier expansion */
+ if(request_1kHz_square){
+ static double phase=0.;
+ double offset = request_1kHz_square_offset/65535.*square_coeffs_w;
+ double spread = offset + request_1kHz_square_spread/65535.*square_coeffs_w;
+
+ for(i=0;i<frames;i++){
+ float acc=0.;
+ int k;
+ for(j=0,k=1;j<square_coeffs_n;j++,k+=2)
+ acc += square_coeffs[j] *
+ cos( square_phases[j] + k*(phase+offset));
+ fbuffer[0][i] = acc;
+
+ if(channels>1 && spread!=offset){
+ acc=0;
+ for(j=0,k=1;j<square_coeffs_n;j++,k+=2)
+ acc += square_coeffs[j] *
+ cos( square_phases[j] + k*(phase+spread));
+ fbuffer[1][i] = acc;
+ }else
+ fbuffer[1][i] = fbuffer[0][i];
+
+ phase+=square_coeffs_w;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+ }
+
+ /* synthetic ~ 5kHz generation */
+ static double amplitude_5kHz_sine=0;
+ if(request_5kHz_sine || amplitude_5kHz_sine>0.){
+ static double phase=0.;
+ double w = 1./rate*5000.*2*M_PI;
+
+ /* avoid clipping when dithering at 8 bits */
+ float amp_target = request_5kHz_sine ?
+ (1.-1.5f/pow(2,7)) *
+ (pow(2,request_1kHz_amplitude/65536.f-1.f)/32768.):
+ 0.;
+
+ if(fabsf(amplitude_5kHz_sine - amp_target) > .0000001){
+
+ for(i=0;i<frames;i++){
+
+ amplitude_5kHz_sine =
+ filter_filter(amp_target, &filter_5kHz_amplitude);
+
+ float v = sinf(phase)*amplitude_5kHz_sine;
+ fbuffer[0][i] = v;
+ phase+=w;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+ }else{
+ amplitude_5kHz_sine = amp_target;
+ if(request_5kHz_sine){
+ for(i=0;i<frames;i++){
+ float v = sinf(phase)*amplitude_5kHz_sine;
+ fbuffer[0][i] = v;
+ phase+=w;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+ }
+ }
+ }
+
+ /* synthetic ~ 1kHz generation; we choose a value that's an
+ integer factor of the sample rate */
+ static double amplitude_1kHz_sine=0;
+ static double mix_1kHz_sine=0;
+ if(request_1kHz_sine || mix_1kHz_sine>0.){
+ static double phase=0.;
+ int quadrant=rate/2;
+ int period = ceil(rate/1000);
+ double w = 1./period*2*M_PI;
+
+ /* avoid clipping when dithering at 8 bits */
+ float amp_target = (1.-1.5f/pow(2,7)) *
+ (pow(2,request_1kHz_amplitude/65536.f-1.f)/32768.);
+ float mix_target = request_1kHz_sine ? 1. : 0.;
+ float modmix_target = request_1kHz_modulate ? 1. : 0.;
+
+ if(amplitude_1kHz_modmix > 0. ||
+ modmix_target > 0. ||
+ fabsf(amplitude_1kHz_sine - amp_target) > .0000001 ||
+ fabsf(mix_1kHz_sine - mix_target) > .0000001){
+
+ for(i=0;i<frames;i++){
+
+ float first =
+ filter_filter(modmix_target, &filter_modulation1);
+ amplitude_1kHz_modmix =
+ filter_filter(first, &filter_modulation2);
+
+ float mod = sinf(.5*M_PI*state_1kHz_modulation/quadrant)*
+ amplitude_1kHz_modmix;
+
+ state_1kHz_modulation++;
+ if(state_1kHz_modulation>=2*quadrant)
+ state_1kHz_modulation=0;
+ mod *= mod;
+ mod = fromdB(mod*-110);
+
+ amplitude_1kHz_sine =
+ filter_filter(amp_target, &filter_1kHz_amplitude)*mod;
+
+ mix_1kHz_sine =
+ filter_filter(mix_target, &filter_1kHz_mix);
+
+ float v = sinf(phase)*amplitude_1kHz_sine;
+ fbuffer[0][i] *= (1.-mix_1kHz_sine);
+ fbuffer[0][i] += v*mix_1kHz_sine;
+ if(channels>1 && request_1kHz_sine2){
+ fbuffer[1][i] *= (1.-mix_1kHz_sine);
+ fbuffer[1][i] += v*mix_1kHz_sine;
+ }
+ phase+=w;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+ }else{
+ amplitude_1kHz_modmix = 0;
+ amplitude_1kHz_sine = amp_target;
+ mix_1kHz_sine = mix_target;
+ state_1kHz_modulation = 0;
+
+ if(request_1kHz_sine){
+ for(i=0;i<frames;i++){
+ float v = sinf(phase)*amplitude_1kHz_sine;
+ fbuffer[0][i] = v;
+ if(channels>1 && request_1kHz_sine2)
+ fbuffer[1][i] = v;
+ phase+=w;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+ }
+ }
+ }
+
+ /* conditionally subquantize and dither output */
+ subquant_dither_to_X(&shapestate_ch0,fbuffer[0],frames,
+ request_ch1_quant);
+ if(channels>1)
+ subquant_dither_to_X(&shapestate_ch1,fbuffer[1],frames,
+ request_ch2_quant);
+
+ /* notch filter has to come after dither as it's sort of the
+ point... The notch filter would normally break the dither
+ (it would be ~equivalent to adding an out-of-phase since
+ wave that is not itself dithered, and thus upon final quant
+ it adds distortion), but because we add gain with the
+ notch, it's OK */
+ if(request_1kHz_notch ||
+ active_1kHz_notch ||
+ prime_1kHz_notch){
+ float notch_gain_target = fromdB( 6*request_ch2_quant-30);
+
+ active_1kHz_notch =
+ run_notch_filter(notch[0],
+ fbuffer[0],
+ frames,
+ prime_1kHz_notch,
+ request_1kHz_notch,
+ notch_gain_target);
+ if(channels>1)
+ active_1kHz_notch |=
+ run_notch_filter(notch[1],
+ fbuffer[1],
+ frames,
+ prime_1kHz_notch,
+ request_1kHz_notch,
+ notch_gain_target);
+ }
+
+ /* in dual listen mode, pipe sees original ch0 and ch1 */
+ if(request_output_duallisten && channels>1){
+ float *temp = crossbuffer;
+ crossbuffer=fbuffer[0];
+ fbuffer[0]=temp;
+ }
+
+ /* float buffer -> out pipe */
+ {
+ unsigned char *buf = iobuffer;
+ switch(bits){
+ case 16:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*32768.f);
+ if(v>32767)v=32767;
+ if(v<-32768)v=-32768.;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ }
+ }
+ break;
+ case 24:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*8388608.f);
+ if(v>8388607)v=8388607;
+ if(v<-8388608)v=-8388608;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=(v>>16)&0xff;
+ }
+ }
+ break;
+ }
+
+ if(outpipe0 || outpipe1){
+ if(outpipe0){
+ fwrite(iobuffer,1,buf-iobuffer,outpipe0);
+ fflush(outpipe0);
+ }
+ if(outpipe1){
+ fwrite(iobuffer,1,buf-iobuffer,outpipe1);
+ fflush(outpipe1);
+ }
+ }
+ }
+
+ /* output filtering for 'perfect squarewave' */
+ if((request_lp_filter || active_lp_filter) && request_1kHz_square){
+ active_lp_filter =
+ run_convolution_filter(lowpass,lowpass_state[0],
+ fbuffer[0],
+ frames,0,request_lp_filter,1.);
+
+ if(channels>1)
+ active_lp_filter |=
+ run_convolution_filter(lowpass,lowpass_state[1],
+ fbuffer[1],
+ frames,0,request_lp_filter,1.);
+
+ }
+
+ /* in dual listen mode, audio device sees silence on ch1,
+ generated output on ch0 */
+ if(request_output_duallisten && channels>1){
+ float *temp=crossbuffer;
+ crossbuffer=fbuffer[0];
+ fbuffer[0]=temp;
+ }
+
+ /* panel 10 modes all require silence on ch1 */
+ if(request_output_silence ||
+ request_output_tone ||
+ request_output_noise ||
+ request_output_sweep ||
+ request_output_logsweep){
+ for(i=0;i<frames;i++)
+ fbuffer[1][i]=0;
+ }
+
+ if(out_devhandle){
+ /* float buffer -> PCM stream */
+
+ /* the output pipe and the hardware device format may not
+ match */
+ unsigned char *buf = iobuffer;
+ switch(outformat){
+ case SND_PCM_FORMAT_S16_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*32768.f);
+ if(v>32767)v=32767;
+ if(v<-32768)v=-32768.;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S24_3LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*8388608.f);
+ if(v>8388607)v=8388607;
+ if(v<-8388608)v=-8388608;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=(v>>16)&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S24_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*8388608.f);
+ if(v>8388607)v=8388607;
+ if(v<-8388608)v=-8388608;
+ *buf++=0;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=(v>>16)&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S32_LE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ double vv = rint(fbuffer[j][i]*2147483648.f);
+
+ if(vv>2147483647)vv=2147483647;
+ if(vv<-2147483648)vv=-2147483648;
+ int v = vv;
+ *buf++=v&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=(v>>16)&0xff;
+ *buf++=(v>>24)&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S16_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*32768.f);
+ if(v>32767)v=32767;
+ if(v<-32768)v=-32768.;
+ *buf++=(v>>8)&0xff;
+ *buf++=v&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S24_3BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*8388608.f);
+ if(v>8388607)v=8388607;
+ if(v<-8388608)v=-8388608;
+ *buf++=(v>>16)&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=v&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S24_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ int v = rint(fbuffer[j][i]*8388608.f);
+ if(v>8388607)v=8388607;
+ if(v<-8388608)v=-8388608;
+ *buf++=(v>>16)&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=v&0xff;
+ *buf++=0;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ case SND_PCM_FORMAT_S32_BE:
+ for(i=0;i<frames;i++){
+ for(j=0;j<channels;j++){
+ double vv = rint(fbuffer[j][i]*2147483648.f);
+ if(vv>2147483647)vv=2147483647;
+ if(vv<-2147483648)vv=-2147483648;
+ int v=vv;
+ *buf++=(v>>24)&0xff;
+ *buf++=(v>>16)&0xff;
+ *buf++=(v>>8)&0xff;
+ *buf++=v&0xff;
+ }
+ for(;j<out_channels;j++){
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ *buf++=0;
+ }
+ }
+ break;
+ }
+
+ int togo = frames;
+ unsigned char *outbuf=iobuffer;
+ while(togo){
+ int wret = snd_pcm_writei (out_devhandle,outbuf,togo);
+ if(wret<0){
+ switch(wret){
+ case -EAGAIN:
+ wret = 0;
+ break;
+ case -EPIPE: case -ESTRPIPE:
+ // underrun; set starve and reset soft device
+ snd_pcm_drop(in_devhandle);
+ snd_pcm_drop(out_devhandle);
+ snd_pcm_prepare(in_devhandle);
+ snd_pcm_prepare(out_devhandle);
+ wret=0;
+ togo=0;
+ fprintf(stderr,"\n PLAYBACK XRUN @ (%ld)\n ",
+ (long)time(NULL));
+ break;
+ case -EBADFD:
+ default:
+ /* shut down device */
+ snd_pcm_close(out_devhandle);
+ out_devhandle=NULL;
+ wret=0;
+ togo=0;
+ break;
+ }
+ }else{
+ outbuf += wret*channels*((bits+7)>>3);
+ togo -= wret;
+ }
+ }
+ }
+ }
+ }else
+ sleep(1);
+ }
+ return NULL;
+}
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,332 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gtk-bounce-convolve.h"
+
+static inline float todB(float x){
+ return log((double)(x)*(x)+1e-50)*4.34294480;
+}
+
+#define A0 .35875f
+#define A1 .48829f
+#define A2 .14128f
+#define A3 .01168f
+
+static float beta(int n, float alpha){
+ return cosh(acosh(pow(10,alpha))/(n-1));
+}
+
+static double T(double n, double x){
+ if(fabs(x)<=1){
+ return cos(n*acos(x));
+ }else{
+ return cosh(n*acosh(x));
+ }
+}
+
+/* 'n' is equivalent to complex blocksize; the returned array is a
+ real-only array of frequency-domain amplitudes of size n/2+1 */
+convofilter *filter_bandstop_new(float w0,
+ float w1,
+ int rate,
+ int n){
+ int i;
+ convofilter *ret = calloc(1,sizeof(*ret));
+ float *f = malloc((n/2+1)*sizeof(*f));
+
+ double *freqbuffer = fftw_malloc((n/2+1)*sizeof(*freqbuffer));
+ double *winbuffer = fftw_malloc((n/4+1)*sizeof(*winbuffer));
+
+ fftw_plan freqplan_i =
+ fftw_plan_r2r_1d(n/4+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan winplan_i =
+ fftw_plan_r2r_1d(n/4+1,winbuffer,winbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan freqplan_f =
+ fftw_plan_r2r_1d(n/2+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+
+ /* construct box response */
+ int a = rint(w0/rate*n*.5);
+ int b = rint(w1/rate*n*.5);
+ for(i=0;i<a;i++)
+ freqbuffer[i]=1.;
+ for(;i<=b;i++)
+ freqbuffer[i]=0.;
+ for(;i<n/4+1;i++)
+ freqbuffer[i]=1.;
+
+ /* to time domain for windowing/padding */
+ fftw_execute(freqplan_i);
+
+ /* ~160dB deep Dolph-Tschebyshev window; exceeds precision of fftwf */
+ /* be aware that the 'Net is rife with incorrect definitions of this
+ window (and many that don't even make sense). This version is
+ adapted from Dolph's 1946 paper. */
+ int N=n/2;
+ float alpha = 8.;
+ double B = beta(N,alpha);
+
+ for(i=0;i<n/4+1;i++)
+ winbuffer[i] = T(N,B*cos( M_PI*i/N ));
+
+ fftw_execute(winplan_i);
+
+ double D = 2./(winbuffer[0]*n*n);
+
+ for(i=0;i<n/4+1;i++)
+ freqbuffer[i]*=winbuffer[i];
+
+ /* pad */
+ for(;i<n/2+1;i++)
+ freqbuffer[i]=0;
+
+ /* back to frequency */
+ fftw_execute(freqplan_f);
+
+ for(i=0;i<n/2+1;i++)
+ f[i]=freqbuffer[i]*D;
+
+#if 0
+ {
+ FILE *file=fopen("notch.m","w");
+ for(i=0;i<n/2+1;i++)
+ fprintf(file,"%d %lf\n",(int)(rate/2.*i/(n/2)),todB(f[i]));
+ fclose(file);
+ }
+#endif
+
+ fftw_destroy_plan(winplan_i);
+ fftw_destroy_plan(freqplan_i);
+ fftw_destroy_plan(freqplan_f);
+ fftw_free(winbuffer);
+ fftw_free(freqbuffer);
+
+ ret->blocksize=n;
+ ret->filter=f;
+
+ return ret;
+}
+
+convofilter *filter_lowpass_new(float w,
+ int rate,
+ int n){
+ int i;
+ convofilter *ret=calloc(1,sizeof(*ret));
+
+ float *f = malloc((n/2+1)*sizeof(*f));
+ double *freqbuffer = fftw_malloc((n/2+1)*sizeof(*freqbuffer));
+
+ fftw_plan freqplan_i =
+ fftw_plan_r2r_1d(n/4+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan freqplan_f =
+ fftw_plan_r2r_1d(n/2+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+
+ /* construct box response */
+ int a = rint(w/rate*n*.5);
+ for(i=0;i<a;i++)
+ freqbuffer[i]=1.;
+ for(;i<n/4+1;i++)
+ freqbuffer[i]=0.;
+
+ /* to time domain for windowing/padding */
+ fftw_execute(freqplan_i);
+
+ /* Blackmann-Harris */
+ float scale = 4*M_PI/n;
+ for(i=0;i<n/4+1;i++){
+ float w = A0 + A1*cos(scale*i) + A2*cos(scale*i*2) + A3*cos(scale*i*3);
+ freqbuffer[i] *= w;
+ }
+
+ /* pad */
+ for(;i<n/2+1;i++)
+ freqbuffer[i]=0;
+
+ /* back to frequency */
+ fftw_execute(freqplan_f);
+
+ for(i=0;i<n/2+1;i++)
+ f[i]=2.*freqbuffer[i]/(n*n); /* normalize */
+
+#if 0
+ {
+ FILE *file=fopen("low.m","w");
+ for(i=0;i<n/2+1;i++)
+ fprintf(file,"%d %f\n",(int)(rate/2.*i/(n/2)),todB(f[i]));
+ fclose(file);
+ }
+#endif
+
+ fftw_destroy_plan(freqplan_i);
+ fftw_destroy_plan(freqplan_f);
+ fftw_free(freqbuffer);
+
+ ret->blocksize=n;
+ ret->filter=f;
+ return ret;
+}
+
+convostate *convostate_new(convofilter *f){
+ convostate *ret = calloc(1,sizeof(*ret));
+ int i;
+
+ for(i=0;i<3;i++){
+ ret->fbuffer[i]=fftwf_malloc((f->blocksize+2)*sizeof(*ret->fbuffer));
+ ret->forward[i]=fftwf_plan_dft_r2c_1d(f->blocksize,ret->fbuffer[i],
+ (fftwf_complex *)ret->fbuffer[i],
+ FFTW_ESTIMATE);
+ ret->inverse[i]=fftwf_plan_dft_c2r_1d(f->blocksize,
+ (fftwf_complex *)ret->fbuffer[i],
+ ret->fbuffer[i],
+ FFTW_ESTIMATE);
+ }
+ filter_make_critical(.0001,1,&ret->mix_filter);
+ ret->mix_now=0;
+ return ret;
+}
+
+void convofilter_destroy(convofilter *f){
+ if(f){
+ if(f->filter)free(f->filter);
+ free(f);
+ }
+}
+
+void convostate_destroy(convostate *f){
+ if(f){
+ int i;
+ for(i=0;i<3;i++){
+ if(f->fbuffer[i])fftwf_free(f->fbuffer[i]);
+ if(f->forward[i])fftwf_destroy_plan(f->forward[i]);
+ if(f->inverse[i])fftwf_destroy_plan(f->inverse[i]);
+ }
+ free(f);
+ }
+}
+
+/* modifies buf in-place */
+int run_convolution_filter(convofilter *f,
+ convostate *s,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain){
+ int i;
+ int bs = f->blocksize;
+ int bs2 = f->blocksize/2;
+ int bs4 = f->blocksize/4;
+
+ while(n){
+ float *head = s->fbuffer[s->head];
+ float *head4 = head+bs4;
+ float *head2 = head+bs2;
+ int copysamples = n;
+ if(copysamples+s->headfill>bs2) copysamples = bs2-s->headfill;
+
+ /* accumulate new samples into the head */
+ memcpy(head4+s->headfill,buf,sizeof(*head)*copysamples);
+
+ /* overlap-add samples out of the lap buffer if it's deep enough */
+ if(s->storefill==2){
+ float *tail = s->fbuffer[(s->head+1)%3]+bs2+s->headfill;
+ float *mid = s->fbuffer[(s->head+2)%3]+s->headfill;
+ float mix_target = active ? 1. : 0.;
+
+ if(fabs(s->mix_now-mix_target)>.0000001){
+ float mix;
+ for(i=0;i<copysamples;i++){
+ float val = tail[i]+mid[i];
+ mix = filter_filter(mix_target, &s->mix_filter);
+
+ buf[i] *= (1.-mix);
+ buf[i] += val*mix*gain;
+ }
+ s->mix_now = mix;
+ }else{
+ if(!active && !run){
+ /* reset state and return; leave rest of buf untouched */
+ for(i=0;i<3;i++)
+ memset(s->fbuffer[i],0,(f->blocksize+2)*sizeof(**s->fbuffer));
+ s->head=0;
+ s->headfill=0;
+ s->storefill=0;
+ return 0;
+ }else{
+ s->mix_now = mix_target;
+ if(mix_target>.0000001){
+ for(i=0;i<copysamples;i++){
+ float val = tail[i]+mid[i];
+ buf[i] = val*gain;
+ }
+ }
+ }
+ }
+ } //else nothing; leave buf untouched
+
+ s->headfill+=copysamples;
+ buf+=copysamples;
+ n-=copysamples;
+
+ /* full accumulation? */
+ if(s->headfill==bs2){
+
+ /* pad fftw3f buffer */
+ memset(head,0,bs4*sizeof(*head2));
+ memset(head+bs2+bs4,0,bs4*sizeof(*head2));
+
+ /* transform */
+ fftwf_execute(s->forward[s->head]);
+
+ /* filter */
+ float *mag = f->filter;
+ for(i=0;i<bs+2;i+=2){
+ head[i] *= mag[i>>1];
+ head[i+1] *= mag[i>>1];
+ }
+
+ /* inverse transform */
+ fftwf_execute(s->inverse[s->head]);
+
+ /* cycle */
+ s->head = (s->head+1)%3;
+ s->headfill=0;
+ s->storefill++;
+ if(s->storefill>2)s->storefill=2;
+ }
+ }
+ return 1;
+}
+
+#if 0
+int main(int argc, char **argv){
+ int i;
+ float data[131072];
+
+ for(i=0;i<131072;i++){
+ data[i]=copysignf(1,sin(1000*2.*M_PI/44100*i));
+
+ }
+ convofilter *notch=filter_bandstop_new(850,1150,44100,4096);
+ convofilter *lowpass=filter_lowpass_new(20500,44100,512);
+
+ convostate *nt = convostate_new(notch);
+ convostate *lt = convostate_new(lowpass);
+
+ for(i=0;i<131072;i+=64){
+ run_convolution_filter(lowpass,lt,data+i,64,1,256.);
+ }
+
+ FILE *f=fopen("test.m","w");
+ for(i=0;i<131072;i++)
+ fprintf(f,"%d %f\n",i,data[i]);
+ fclose(f);
+
+ return 0;
+}
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.h
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-convolve.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,49 @@
+
+#ifndef _BOUNCE_CONVO_H_
+#define _BOUNCE_CONVO_H_
+
+#include "twopole.h"
+#include <fftw3.h>
+
+typedef struct {
+ int blocksize;
+ float *filter;
+} convofilter;
+
+typedef struct {
+ fftwf_plan forward[3];
+ fftwf_plan inverse[3];
+ float *fbuffer[3];
+ int head;
+
+ int headfill;
+ int storefill;
+
+ pole2 mix_filter;
+ float mix_now;
+} convostate;
+
+extern convofilter *filter_bandstop_new(float w0,
+ float w1,
+ int rate,
+ int n);
+
+extern convofilter *filter_lowpass_new(float w,
+ int rate,
+ int n);
+
+extern void convofilter_destroy(convofilter *f);
+
+extern convostate *convostate_new(convofilter *f);
+
+extern void convostate_destroy(convostate *f);
+
+extern int run_convolution_filter(convofilter *f,
+ convostate *s,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain);
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-dither.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-dither.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-dither.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,100 @@
+#include "gtk-bounce.h"
+#include "gtk-bounce-dither.h"
+
+/* flat and shaped dither, adapted from gmaxwell's code in opusdec */
+
+static unsigned int rngseed = 22222;
+static inline unsigned int fast_rand() {
+ /* Greg, this is one of the craziest things I've ever seen. Bravo. */
+ rngseed = (rngseed * 96314165) + 907633515;
+ return rngseed;
+}
+
+const float fcoef[3][8] ={
+ /* 44.1 kHz noise shaping filter sd=2.51*/
+ {2.2061f, -.4706f, -.2534f, -.6214f, 1.0587f, .0676f, -.6054f, -.2738f},
+ /* 48.0kHz noise shaping filter sd=2.34*/
+ {2.2374f, -.7339f, -.1251f, -.6033f, 0.9030f, .0116f, -.5853f, -.2571f},
+ /* lowpass noise shaping filter sd=0.65*/
+ {1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f,0.0000f, 0.0000f, 0.0000f},
+};
+
+/* This implements a n-bit quantization with full triangular dither
+ and IIR noise shaping. The noise shaping filters were designed by
+ Sebastian Gesemann based on the LAME ATH curves with flattening
+ to limit their peak gain to 20dB.
+
+ The 48kHz version of this filter is just a warped version of the
+ 44.1kHz filter and probably could be improved by shifting the
+ HF shelf up in frequency a little bit since 48k has a bit more
+ room and being more conservative against bat-ears is probably
+ more important than more noise suppression.
+ This process can increase the peak level of the signal (in theory
+ by the peak error of 1.5 +20dB though this much is unobservably rare).
+
+ We're not bothering to guard against clipping here because this is
+ in a demo and the condiitons are chosen so the we know we won't run
+ into it. It also saves a little noise in the non-dithered case as
+ we won't be slightly shifting the quantzation scale */
+
+void subquant_dither_to_X(shapestate *ss, float *data, int n,
+ float quant_bits){
+ int i;
+ if(quant_bits==0)quant_bits=24;
+ int filter_choice = request_rate==44100?0:(request_rate==48000?1:2);
+ const float *dither_fcoef = fcoef[filter_choice];
+ float quant_gain = pow(2,quant_bits-1);
+ float quant_invgain = 1./quant_gain;
+ float quant_clamp = quant_gain;
+ float dither_amp = request_dither_amplitude/65536.;
+
+ /* don't shape if we don't have a filter */
+ if(request_rate != 48000 && request_rate != 44100){
+ /* pop the request button back out */
+ request_dither_shaped=0;
+ write(eventpipe[1],"\005",1);
+ }
+
+ float *b_buf=ss->b_buf;
+ float *a_buf=ss->a_buf;
+ int mute=ss->mute;
+
+ if(mute>64)
+ memset(a_buf,0,sizeof(float)*4);
+
+ for(i=0;i<n;i++){
+ int silent=data[i]==0;
+ int j;
+ float si,r=0,err=0;
+ float s = data[i] * quant_gain;
+
+ if(request_dither && request_dither_shaped)
+ for(j=0;j<4;j++)
+ err += dither_fcoef[j]*b_buf[j] - dither_fcoef[j+4]*a_buf[j];
+
+ memmove(&a_buf[1],&a_buf[0],sizeof(float)*3);
+ memmove(&b_buf[1],&b_buf[0],sizeof(float)*3);
+ a_buf[0]=err;
+ s = s - err;
+
+ if(request_dither && mute<16)
+ r=((float)fast_rand()*(1/(float)UINT_MAX) -
+ (float)fast_rand()*(1/(float)UINT_MAX)) * dither_amp;
+
+ /* no need to clamp; this is a demo, and we won't clip */
+ si = rintf(s + r);
+
+ /* output width may be higher than the subquant */
+ data[i] = si*quant_invgain;
+
+ if(request_dither_shaped)
+ b_buf[0] = (mute>16)?0:si-s;
+ else
+ b_buf[0] = 0;
+
+ mute++;
+ if(!silent)mute=0;
+ }
+ ss->mute = (mute>960?960:mute);
+}
+
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-dither.h
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-dither.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-dither.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,16 @@
+#ifndef _BOUNCE_DITHER_H_
+#define _BOUNCE_DITHER_H_
+
+#include <math.h>
+#include <stdlib.h>
+
+typedef struct shapestate {
+ float b_buf[8];
+ float a_buf[8];
+ int mute;
+} shapestate;
+
+extern void subquant_dither_to_X(shapestate *ss, float *data, int n,
+ float quant_bits);
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-filter.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-filter.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-filter.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,497 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "gtk-bounce-filter.h"
+
+static inline float todB(float x){
+ return log((double)(x)*(x)+1e-50)*4.34294480;
+}
+
+#define A0 .35875f
+#define A1 .48829f
+#define A2 .14128f
+#define A3 .01168f
+
+static float beta(int n, float alpha){
+ return cosh(acosh(pow(10,alpha))/(n-1));
+}
+
+static double T(double n, double x){
+ if(fabs(x)<=1){
+ return cos(n*acos(x));
+ }else{
+ return cosh(n*acosh(x));
+ }
+}
+
+void filter_notch_reset(notchfilter *f){
+ f->preroll = 0;
+ f->mix_now = 0.;
+ f->z1p = f->delayz1;
+ f->z2p = f->delayz2;
+ memset(f->delayz1,0,sizeof(f->delayz1));
+ memset(f->delayz2,0,sizeof(f->delayz2));
+}
+
+void filter_notch_destroy(notchfilter *f){
+ if(f)free(f);
+}
+
+/* Hardwired / specialized notch filter implementation **************/
+
+/* Intended for very deep notches; we want greater than single
+ precision mantissa depth (~200db). Although it is possible to make
+ that go in single precision, there's no reason to be clever. Just
+ run the filter in double precision and be done with it. */
+
+/* Normally, we'd probably want to use a fast convolution FIR filter
+ for this, but the additional latency of a sharp enough FIR filter
+ will impact the responsiveness of the demo. The IIR notch filter
+ is inferior in several ways, but its reaction outside the stop band
+ will be much faster than a big convolution filter and we really
+ don't care about phase behavior or precision requirements. */
+
+int run_notch_filter(notchfilter *f,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain){
+ int i,j;
+ float mix_target = active ? 1. : 0.;
+ float mix = f->mix_now;
+ double *z1 = f->z1p;
+ double *z2 = f->z2p;
+ double *a1 = f->a1;
+ double *a2 = f->a2;
+ double *b1 = f->b1;
+
+ for(i=0;i<n;i++){
+
+ double x = buf[i];
+ for(j=0;j<f->stages;j++){
+ double z0 = x - a1[j]*z1[j] - a2[j]*z2[j];
+ x = z0 + b1[j]*z1[j] + z2[j];
+ z2[j] = z0;
+ }
+
+ double *temp = z1;
+ z1 = z2;
+ z2 = temp;
+
+ if(f->preroll > f->rate){
+ if(fabs(mix-mix_target)>.0000001){
+ mix = filter_filter(mix_target, &f->mix_filter);
+ buf[i] *= (1.-mix);
+ buf[i] += x*mix*gain;
+ }else{
+ if(!active && !run){
+ /* reset state and return; leave rest of buf untouched */
+ filter_notch_reset(f);
+ return 0;
+ }else{
+ f->mix_now = mix_target;
+ if(mix_target>.0000001){
+ buf[i] = x*gain;
+ }
+ }
+ }
+ }else{
+ f->preroll++;
+ }
+ }
+
+ f->mix_now = mix;
+ f->z1p = z1;
+ f->z2p = z2;
+ return 1;
+}
+
+/* Analog filter gurus, please don't laugh too hard */
+/* Hand drawn in the z-plane, hardwired to our specific use. */
+/* For a more general-purpose filter, see the convolution based
+ bandstop with a deep Dolph-Tschebyshev window later below. */
+#define DUMPH 0
+notchfilter *filter_notch_new(float f, int rate){
+ int i,j;
+
+#if DUMPH
+ float Hmag[rate*8];
+ float Hang[rate*8];
+ for(i=0;i<rate*8;i++)Hmag[i]=1.;
+ for(i=0;i<rate*8;i++)Hang[i]=0.;
+#endif
+
+ notchfilter *ret = calloc(1,sizeof(*ret));
+ int stages = ret->stages = 30;
+ float side = (stages-1.)/2;
+ float tw = 30;
+ float fscale = tw/2./side;
+
+ for(j=0;j<stages;j++){
+ float bw = 15 + 6.2*cos(M_PI*(j-side)/side);
+ double r = 1. - M_PI*(bw)/rate;
+
+ float jfz = sin(M_PI*(j-side)/(side+1)/2)*side;
+ float jfp = sin(M_PI*(j-side)/(side)/2)*side;
+ float wz = 2*M_PI*(f+jfz*fscale)/rate;
+ float wp = 2*M_PI*(f+jfp*fscale*1.206)/rate;
+
+ double b1 = ret->b1[j] = -2. * cos(wz);
+ double a1 = ret->a1[j] = r * -2 * cos(wp);
+ double a2 = ret->a2[j] = r*r;
+
+#if DUMPH
+ for(i=0;i<rate*8;i++){
+ float w = 2*M_PI*i/rate/16;
+ double a = 1 + b1*cos(w) + cos(2*w);
+ float b = -b1*sin(w) - sin(2*w);
+ double c = 1. + a1*cos(w) + a2*cos(2*w);
+ double d = -a1*sin(w) - a2*sin(2*w);
+ double re = (a*c + b*d)/(c*c+d*d);
+ double im = (b*c - a*d)/(c*c+d*d);
+ Hmag[i] *= hypot(im,re);
+ Hang[i] += atan2(im,re);
+ }
+#endif
+ }
+
+#if DUMPH
+ {
+ FILE *file=fopen("iirnotchM.m","w");
+ for(i=0;i<rate*8;i++){
+ fprintf(file,"%f %lf\n",(i/16.), todB(Hmag[i])-90.6);
+ }
+ fclose(file);
+ }
+ {
+ FILE *file=fopen("iirnotchA.m","w");
+ for(i=0;i<rate*8;i++){
+ float a = Hang[i]*180/M_PI;
+ fprintf(file,"%f %lf\n",(i/16.), a);
+ }
+ fclose(file);
+ }
+#endif
+
+ ret->rate = rate;
+ filter_make_critical(.0001,1,&ret->mix_filter);
+ filter_notch_reset(ret);
+ return (ret);
+}
+
+/* FIR bandstop filter constructed out of a frequency domain box
+ response convolved with a deep Dolph-Tschebyshev window */
+
+/* 'n' is equivalent to complex blocksize; the returned array is a
+ real-only array of frequency-domain amplitudes of size n/2+1 */
+convofilter *filter_bandstop_new(float w0,
+ float w1,
+ int rate,
+ int n){
+ int i;
+ convofilter *ret = calloc(1,sizeof(*ret));
+ float *f = malloc((n/2+1)*sizeof(*f));
+
+ double *freqbuffer = fftw_malloc((n*2+1)*sizeof(*freqbuffer));
+ double *winbuffer = fftw_malloc((n/4+1)*sizeof(*winbuffer));
+
+ fftw_plan freqplan_i =
+ fftw_plan_r2r_1d(n*2+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan winplan_i =
+ fftw_plan_r2r_1d(n/4+1,winbuffer,winbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan freqplan_f =
+ fftw_plan_r2r_1d(n/2+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+
+ /* construct box response */
+ int a = rint(w0/rate*n*4);
+ int b = rint(w1/rate*n*4);
+ for(i=0;i<a;i++)
+ freqbuffer[i]=1.;
+ for(;i<=b;i++)
+ freqbuffer[i]=0.;
+ for(;i<n*2+1;i++)
+ freqbuffer[i]=1.;
+
+ /* to time domain for windowing/padding */
+ fftw_execute(freqplan_i);
+
+ /* ~160dB deep Dolph-Tschebyshev window; exceeds precision of fftwf */
+ /* be aware that the 'Net is rife with incorrect definitions of this
+ window (and many that don't even make sense). This version is
+ adapted from Dolph's 1946 paper. */
+ int N=n/2;
+ float alpha = 8.;
+ double B = beta(N,alpha);
+
+ for(i=0;i<n/4+1;i++)
+ winbuffer[i] = T(N,B*cos( M_PI*i/N ));
+
+ fftw_execute(winplan_i);
+
+ double D = 2./(winbuffer[0]*n*n*8);
+
+ for(i=0;i<n/4+1;i++)
+ freqbuffer[i]*=winbuffer[i];
+
+ /* pad */
+ for(;i<n/2+1;i++)
+ freqbuffer[i]=0;
+
+ /* back to frequency */
+ fftw_execute(freqplan_f);
+
+ for(i=0;i<n/2+1;i++)
+ f[i]=freqbuffer[i]*D;
+
+#if 0
+ {
+ FILE *file=fopen("notch.m","w");
+ for(i=0;i<n/2+1;i++)
+ fprintf(file,"%d %lf\n",(int)(rate/2.*i/(n/2)),todB(f[i]));
+ fclose(file);
+ }
+#endif
+
+ fftw_destroy_plan(winplan_i);
+ fftw_destroy_plan(freqplan_i);
+ fftw_destroy_plan(freqplan_f);
+ fftw_free(winbuffer);
+ fftw_free(freqbuffer);
+
+ ret->blocksize=n;
+ ret->filter=f;
+
+ return ret;
+}
+
+convofilter *filter_lowpass_new(float w,
+ int rate,
+ int n){
+ int i;
+ convofilter *ret=calloc(1,sizeof(*ret));
+
+ float *f = malloc((n/2+1)*sizeof(*f));
+ double *freqbuffer = fftw_malloc((n/2+1)*sizeof(*freqbuffer));
+
+ fftw_plan freqplan_i =
+ fftw_plan_r2r_1d(n/4+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+ fftw_plan freqplan_f =
+ fftw_plan_r2r_1d(n/2+1,freqbuffer,freqbuffer,
+ FFTW_REDFT00,FFTW_ESTIMATE);
+
+ /* construct box response */
+ int a = rint(w/rate*n*.5);
+ for(i=0;i<a;i++)
+ freqbuffer[i]=1.;
+ for(;i<n/4+1;i++)
+ freqbuffer[i]=0.;
+
+ /* to time domain for windowing/padding */
+ fftw_execute(freqplan_i);
+
+ /* Blackmann-Harris */
+ float scale = 4*M_PI/n;
+ for(i=0;i<n/4+1;i++){
+ float w = A0 + A1*cos(scale*i) + A2*cos(scale*i*2) + A3*cos(scale*i*3);
+ freqbuffer[i] *= w;
+ }
+
+ /* pad */
+ for(;i<n/2+1;i++)
+ freqbuffer[i]=0;
+
+ /* back to frequency */
+ fftw_execute(freqplan_f);
+
+ for(i=0;i<n/2+1;i++)
+ f[i]=2.*freqbuffer[i]/(n*n); /* normalize */
+
+#if 0
+ {
+ FILE *file=fopen("low.m","w");
+ for(i=0;i<n/2+1;i++)
+ fprintf(file,"%d %f\n",(int)(rate/2.*i/(n/2)),todB(f[i]));
+ fclose(file);
+ }
+#endif
+
+ fftw_destroy_plan(freqplan_i);
+ fftw_destroy_plan(freqplan_f);
+ fftw_free(freqbuffer);
+
+ ret->blocksize=n;
+ ret->filter=f;
+ return ret;
+}
+
+convostate *convostate_new(convofilter *f){
+ convostate *ret = calloc(1,sizeof(*ret));
+ int i;
+
+ for(i=0;i<3;i++){
+ ret->fbuffer[i]=fftwf_malloc((f->blocksize+2)*sizeof(*ret->fbuffer));
+ ret->forward[i]=fftwf_plan_dft_r2c_1d(f->blocksize,ret->fbuffer[i],
+ (fftwf_complex *)ret->fbuffer[i],
+ FFTW_ESTIMATE);
+ ret->inverse[i]=fftwf_plan_dft_c2r_1d(f->blocksize,
+ (fftwf_complex *)ret->fbuffer[i],
+ ret->fbuffer[i],
+ FFTW_ESTIMATE);
+ }
+ filter_make_critical(.0001,1,&ret->mix_filter);
+ ret->mix_now=0;
+ return ret;
+}
+
+void convofilter_destroy(convofilter *f){
+ if(f){
+ if(f->filter)free(f->filter);
+ free(f);
+ }
+}
+
+void convostate_destroy(convostate *f){
+ if(f){
+ int i;
+ for(i=0;i<3;i++){
+ if(f->fbuffer[i])fftwf_free(f->fbuffer[i]);
+ if(f->forward[i])fftwf_destroy_plan(f->forward[i]);
+ if(f->inverse[i])fftwf_destroy_plan(f->inverse[i]);
+ }
+ free(f);
+ }
+}
+
+/* modifies buf in-place */
+int run_convolution_filter(convofilter *f,
+ convostate *s,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain){
+ int i;
+ int bs = f->blocksize;
+ int bs2 = f->blocksize/2;
+ int bs4 = f->blocksize/4;
+
+ while(n){
+ float *head = s->fbuffer[s->head];
+ float *head4 = head+bs4;
+ float *head2 = head+bs2;
+ int copysamples = n;
+ if(copysamples+s->headfill>bs2) copysamples = bs2-s->headfill;
+
+ /* accumulate new samples into the head */
+ memcpy(head4+s->headfill,buf,sizeof(*head)*copysamples);
+
+ /* overlap-add samples out of the lap buffer if it's deep enough */
+ if(s->storefill==2){
+ float *tail = s->fbuffer[(s->head+1)%3]+bs2+s->headfill;
+ float *mid = s->fbuffer[(s->head+2)%3]+s->headfill;
+ float mix_target = active ? 1. : 0.;
+
+ if(fabs(s->mix_now-mix_target)>.0000001){
+ float mix;
+ for(i=0;i<copysamples;i++){
+ float val = tail[i]+mid[i];
+ mix = filter_filter(mix_target, &s->mix_filter);
+
+ buf[i] *= (1.-mix);
+ buf[i] += val*mix*gain;
+ }
+ s->mix_now = mix;
+ }else{
+ if(!active && !run){
+ /* reset state and return; leave rest of buf untouched */
+ for(i=0;i<3;i++)
+ memset(s->fbuffer[i],0,(f->blocksize+2)*sizeof(**s->fbuffer));
+ s->head=0;
+ s->headfill=0;
+ s->storefill=0;
+ return 0;
+ }else{
+ s->mix_now = mix_target;
+ if(mix_target>.0000001){
+ for(i=0;i<copysamples;i++){
+ float val = tail[i]+mid[i];
+ buf[i] = val*gain;
+ }
+ }
+ }
+ }
+ } //else nothing; leave buf untouched
+
+ s->headfill+=copysamples;
+ buf+=copysamples;
+ n-=copysamples;
+
+ /* full accumulation? */
+ if(s->headfill==bs2){
+
+ /* pad fftw3f buffer */
+ memset(head,0,bs4*sizeof(*head2));
+ memset(head+bs2+bs4,0,bs4*sizeof(*head2));
+
+ /* transform */
+ fftwf_execute(s->forward[s->head]);
+
+ /* filter */
+ float *mag = f->filter;
+ for(i=0;i<bs+2;i+=2){
+ head[i] *= mag[i>>1];
+ head[i+1] *= mag[i>>1];
+ }
+
+ /* inverse transform */
+ fftwf_execute(s->inverse[s->head]);
+
+ /* cycle */
+ s->head = (s->head+1)%3;
+ s->headfill=0;
+ s->storefill++;
+ if(s->storefill>2)s->storefill=2;
+ }
+ }
+ return 1;
+}
+
+#if 0
+#define fromdB(x) (exp((x)*.11512925f))
+int main(int argc, char **argv){
+ int i;
+ float data[131072];
+
+ for(i=0;i<131072;i++){
+ data[i]=sin(1000*2.*M_PI/44100*i)*.95;
+ data[i]+=(drand48()-drand48())*fromdB(-93.);
+ }
+ notchfilter *notch=filter_notch_new(1000,44100);
+ //convofilter *notch=filter_bandstop_new(970,1030,44100,32768);
+ //convofilter *lowpass=filter_lowpass_new(20500,44100,512);
+
+ //convostate *nt = convostate_new(notch);
+ //convostate *lt = convostate_new(lowpass);
+
+ for(i=0;i<131072;i+=4096){
+ //run_convolution_filter(notch,nt,data+i,4096,1,1,256.);
+ run_notch_filter(notch,data+i,4096,1,1,6309);
+ }
+
+ FILE *f=fopen("test.m","w");
+ for(i=0;i<131072;i++){
+ fputc(((int)rint(data[i]*32768))&0xff,stdout);
+ fputc((((int)rint(data[i]*32768))>>8)&0xff,stdout);
+ fprintf(f,"%d %f\n",i,data[i]);
+ }
+ fclose(f);
+
+ return 0;
+}
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-filter.h
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-filter.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-filter.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,75 @@
+
+#ifndef _BOUNCE_CONVO_H_
+#define _BOUNCE_CONVO_H_
+
+#include "twopole.h"
+#include <fftw3.h>
+
+typedef struct {
+ int blocksize;
+ float *filter;
+} convofilter;
+
+typedef struct {
+ fftwf_plan forward[3];
+ fftwf_plan inverse[3];
+ float *fbuffer[3];
+ int head;
+
+ int headfill;
+ int storefill;
+
+ pole2 mix_filter;
+ float mix_now;
+} convostate;
+
+#define MAXSTAGES 41
+
+typedef struct {
+ int stages;
+ int rate;
+ double a1[MAXSTAGES];
+ double a2[MAXSTAGES];
+ double b1[MAXSTAGES];
+ double *z1p;
+ double *z2p;
+ double delayz1[MAXSTAGES];
+ double delayz2[MAXSTAGES];
+ pole2 mix_filter;
+ float mix_now;
+ int preroll;
+} notchfilter;
+
+extern convofilter *filter_bandstop_new(float w0,
+ float w1,
+ int rate,
+ int n);
+
+extern convofilter *filter_lowpass_new(float w,
+ int rate,
+ int n);
+
+extern void convofilter_destroy(convofilter *f);
+
+extern convostate *convostate_new(convofilter *f);
+
+extern void convostate_destroy(convostate *f);
+
+extern int run_convolution_filter(convofilter *f,
+ convostate *s,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain);
+
+extern notchfilter *filter_notch_new(float f,int rate);
+extern void filter_notch_destroy(notchfilter *f);
+extern int run_notch_filter(notchfilter *f,
+ float *buf,
+ int n,
+ int run,
+ int active,
+ float gain);
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-panel.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-panel.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-panel.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,935 @@
+#include "gtk-bounce.h"
+#include "gtk-bounce-widget.h"
+#include <pthread.h>
+
+#define PANEL_WIDTH 1024
+#define PANEL_HEIGHT 90
+
+/* parameters that set the uncondiitonal behavior of the io thread */
+sig_atomic_t exiting=0;
+
+sig_atomic_t request_bits=16;
+//sig_atomic_t request_ch=2;
+sig_atomic_t request_rate=44100;
+
+sig_atomic_t request_ch1_quant=0;
+sig_atomic_t request_ch2_quant=0;
+sig_atomic_t prime_1kHz_notch=0;
+sig_atomic_t request_1kHz_notch=0;
+sig_atomic_t request_1kHz_sine=0;
+sig_atomic_t request_1kHz_sine2=0;
+sig_atomic_t request_5kHz_sine=0;
+sig_atomic_t request_1kHz_amplitude=0;
+sig_atomic_t request_1kHz_modulate=0;
+sig_atomic_t request_dither=0;
+sig_atomic_t request_dither_shaped=0;
+sig_atomic_t request_dither_amplitude=0;
+
+sig_atomic_t request_lp_filter=0;
+sig_atomic_t request_1kHz_square=0;
+sig_atomic_t request_1kHz_square_offset=0;
+sig_atomic_t request_1kHz_square_spread=0;
+
+sig_atomic_t request_output_silence=0;
+sig_atomic_t request_output_noise=0;
+sig_atomic_t request_output_sweep=0;
+sig_atomic_t request_output_logsweep=0;
+sig_atomic_t request_output_tone=0;
+sig_atomic_t request_output_duallisten=0;
+
+/* these are panel settings remembered between panels but not
+ necessarily active on a given panel. They're used to update the io
+ settings when switching between panels */
+
+int current_panel=0;
+
+int panel_bits;
+int panel_ch1_quant;
+int panel_ch2_quant;
+int panel_1kHz_notch;
+int panel_1kHz_sine;
+int panel_5kHz_sine;
+int panel_1kHz_amplitude;
+int panel_dither;
+int panel_dither_shaped;
+int panel_dither_amplitude;
+
+int panel_lp_filter;
+int panel_1kHz_square;
+int panel_1kHz_square_offset;
+int panel_1kHz_square_spread;
+
+
+pthread_t io_thread_id;
+int eventpipe[2];
+
+/* panel 0 */
+rowwidget *panel0_readout_hw;
+rowwidget *panel0_button_16bit;
+rowwidget *panel0_button_24bit;
+rowwidget *panel0_button_44;
+rowwidget *panel0_button_48;
+rowwidget *panel0_button_96;
+rowwidget *panel0_button_192;
+
+/* panel 1 */
+rowwidget *panel1_button_c1_8;
+rowwidget *panel1_button_c1_16;
+rowwidget *panel1_button_c2_8;
+rowwidget *panel1_button_c2_16;
+
+/* panel 2 */
+rowslider *panel2_slider_bits;
+rowwidget *panel2_button_notch;
+
+/* panel 3 */
+rowwidget *panel3_button_1k;
+rowwidget *panel3_button_8bit;
+rowwidget *panel3_button_16bit;
+rowwidget *panel3_button_dither;
+
+/* panel 4 */
+rowslider *panel4_slider_amp;
+rowwidget *panel4_button_dither;
+
+/* panel 5 */
+rowwidget *panel5_button_dither;
+rowwidget *panel5_button_flat;
+rowwidget *panel5_button_shaped;
+rowwidget *panel5_button_notch;
+
+/* panel 6 */
+rowslider *panel6_slider_amp;
+rowwidget *panel6_button_notch;
+rowwidget *panel6_button_modulate;
+rowwidget *panel6_button_flat;
+rowwidget *panel6_button_shaped;
+
+/* panel 7 */
+
+/* panel 8 */
+rowwidget *panel8_button_square;
+rowslider *panel8_slider_offset;
+
+/* panel 9 */
+rowslider *panel9_slider_spread;
+
+/* panel 10 */
+rowwidget *panel10_readout_hw;
+
+rowwidget *panel10_button_silence;
+rowwidget *panel10_button_noise;
+rowwidget *panel10_button_sweep;
+rowwidget *panel10_button_logsweep;
+rowwidget *panel10_button_tone1;
+rowwidget *panel10_button_tone5;
+rowwidget *panel10_button_duallisten;
+
+static int ready=0;
+void panelchange(rowpanel *p){
+ if(!ready)return;
+ current_panel = p->current_panel;
+
+ if(current_panel == 10){
+ /* we use 24 bit for the response testing */
+ request_bits=24;
+ }else{
+ request_bits=panel_bits;
+ }
+
+ switch(current_panel){
+ case 1:
+ panel_ch1_quant=16;
+ panel_ch2_quant=16;
+ /* fall through */
+ case 2:
+ case 3:
+ if(current_panel!=2){
+ if(panel_ch1_quant<16)
+ panel_ch1_quant=8;
+ if(panel_ch2_quant<16)
+ panel_ch2_quant=8;
+ }
+
+ request_ch1_quant=panel_ch1_quant;
+ request_ch2_quant=panel_ch2_quant;
+
+ rowtoggle_set_active(panel1_button_c1_8,panel_ch1_quant==8);
+ rowtoggle_set_active(panel1_button_c2_8,panel_ch2_quant==8);
+ rowtoggle_set_active(panel1_button_c1_16,panel_ch1_quant==16);
+ rowtoggle_set_active(panel1_button_c2_16,panel_ch2_quant==16);
+
+ rowslider_set(panel2_slider_bits,panel_ch2_quant);
+
+ rowtoggle_set_active(panel3_button_8bit,panel_ch2_quant==8);
+ rowtoggle_set_active(panel3_button_16bit,panel_ch2_quant==16);
+
+ break;
+ case 4:
+ case 5:
+ case 6:
+ request_ch1_quant=16;
+ request_ch2_quant=16;
+ break;
+ default:
+ request_ch1_quant=0;
+ request_ch2_quant=0;
+ break;
+ }
+
+ if(current_panel==2 || current_panel==5 || current_panel==6){
+ request_1kHz_notch=panel_1kHz_notch=0;
+ prime_1kHz_notch=1;
+ rowtoggle_set_active(panel2_button_notch,panel_1kHz_notch);
+ rowtoggle_set_active(panel5_button_notch,panel_1kHz_notch);
+ rowtoggle_set_active(panel6_button_notch,panel_1kHz_notch);
+ }else{
+ request_1kHz_notch=0;
+ prime_1kHz_notch=0;
+ }
+
+ switch(current_panel){
+ case 3:
+ request_1kHz_sine = request_1kHz_sine2 = panel_1kHz_sine = 0;
+ rowtoggle_set_active(panel3_button_1k,panel_1kHz_sine);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ request_1kHz_sine = 1;
+ request_1kHz_sine2 = 1;
+ break;
+ default:
+ request_1kHz_sine = 0;
+ request_1kHz_sine2 = 0;
+ }
+
+ if(current_panel==4){
+ request_1kHz_amplitude=panel_1kHz_amplitude;
+ }else{
+ request_1kHz_amplitude=65536*16.;
+ }
+
+ switch(current_panel){
+ case 1:
+ case 2:
+ case 5:
+ case 6:
+ //case 8:
+ //case 9:
+ panel_dither=request_dither=1;
+ rowtoggle_set_active(panel3_button_dither,panel_dither);
+ rowtoggle_set_active(panel4_button_dither,panel_dither);
+ rowtoggle_set_active(panel5_button_dither,panel_dither);
+
+ break;
+ case 3:
+ case 4:
+ request_dither=panel_dither;
+ rowtoggle_set_active(panel3_button_dither,panel_dither);
+ rowtoggle_set_active(panel4_button_dither,panel_dither);
+ rowtoggle_set_active(panel5_button_dither,panel_dither);
+
+ break;
+ default:
+ request_dither=0;
+ break;
+ }
+
+ //if(current_panel==5){
+ request_dither_shaped=panel_dither_shaped=0;
+ request_1kHz_modulate=0;
+ // }else{
+ //if(current_panel != 6)request_dither_shaped=0;
+ //}
+ rowtoggle_set_active(panel5_button_flat,!panel_dither_shaped);
+ rowtoggle_set_active(panel5_button_shaped,panel_dither_shaped);
+ rowtoggle_set_active(panel6_button_flat,!panel_dither_shaped);
+ rowtoggle_set_active(panel6_button_shaped,panel_dither_shaped);
+ rowtoggle_set_active(panel6_button_modulate,request_1kHz_modulate);
+
+ if(current_panel==6){
+ request_dither_amplitude=panel_dither_amplitude;
+ }else{
+ request_dither_amplitude=65536;
+ }
+
+ if(current_panel>=7 && current_panel<=9){
+ request_lp_filter=1;
+ }else{
+ request_lp_filter=0;
+ }
+
+ switch(current_panel){
+ case 8:
+ request_1kHz_square=panel_1kHz_square;
+ request_1kHz_square_offset=panel_1kHz_square_offset;
+ panel_1kHz_square_spread=request_1kHz_square_spread=0;
+ break;
+ case 9:
+ request_1kHz_square=1;
+ panel_1kHz_square_offset=request_1kHz_square_offset=0;
+ request_1kHz_square_spread=panel_1kHz_square_spread;
+ break;
+ default:
+ panel_1kHz_square=request_1kHz_square=0;
+ panel_1kHz_square_offset=request_1kHz_square_offset=0;
+ panel_1kHz_square_spread=request_1kHz_square_spread=0;
+ break;
+ }
+ rowtoggle_set_active(panel8_button_square,panel_1kHz_square);
+ rowslider_set(panel8_slider_offset,panel_1kHz_square_offset);
+ rowslider_set(panel9_slider_spread,panel_1kHz_square_spread);
+
+ if(current_panel==10){
+ request_output_duallisten=0;
+ request_output_silence=1;
+ rowtoggle_set_active(panel10_button_duallisten,0);
+ rowtoggle_set_active(panel10_button_silence,1);
+ }else{
+ request_output_silence=0;
+ }
+}
+
+/* action handlers for panel buttons */
+static int clicked_enter=0;
+void panel0_clicked_bits(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel0_button_16bit)
+ panel_bits=request_bits=16;
+ else
+ rowtoggle_set_active(panel0_button_16bit,0);
+
+ if(t==panel0_button_24bit)
+ panel_bits=request_bits=24;
+ else
+ rowtoggle_set_active(panel0_button_24bit,0);
+
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel0_clicked_rate(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel0_button_44)
+ request_rate=44100;
+ else
+ rowtoggle_set_active(panel0_button_44,0);
+
+ if(t==panel0_button_48)
+ request_rate=48000;
+ else
+ rowtoggle_set_active(panel0_button_48,0);
+
+ if(t==panel0_button_96)
+ request_rate=96000;
+ else
+ rowtoggle_set_active(panel0_button_96,0);
+
+ if(t==panel0_button_192)
+ request_rate=192000;
+ else
+ rowtoggle_set_active(panel0_button_192,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel1_clicked_c1(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel1_button_c1_8)
+ panel_ch1_quant=request_ch1_quant=8;
+ else
+ rowtoggle_set_active(panel1_button_c1_8,0);
+
+ if(t==panel1_button_c1_16)
+ panel_ch1_quant=request_ch1_quant=16;
+ else
+ rowtoggle_set_active(panel1_button_c1_16,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel1_clicked_c2(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel1_button_c2_8)
+ panel_ch2_quant=request_ch2_quant=8;
+ else
+ rowtoggle_set_active(panel1_button_c2_8,0);
+
+ if(t==panel1_button_c2_16)
+ panel_ch2_quant=request_ch2_quant=16;
+ else
+ rowtoggle_set_active(panel1_button_c2_16,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel2_clicked_bits(rowslider *t){
+ panel_ch1_quant = request_ch1_quant = t->value;
+ panel_ch2_quant = request_ch2_quant = t->value;
+}
+
+void panel2_clicked_notch(rowwidget *t){
+ panel_1kHz_notch = request_1kHz_notch = rowtoggle_get_active(t);
+}
+
+void panel3_clicked_1kHz(rowwidget *t){
+ panel_1kHz_sine = request_1kHz_sine = request_1kHz_sine2 =
+ rowtoggle_get_active(t);
+}
+
+void panel3_clicked_dither(rowwidget *t){
+ panel_dither = request_dither = rowtoggle_get_active(t);
+}
+
+void panel3_clicked_bits(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel3_button_8bit){
+ panel_ch1_quant=request_ch1_quant=8;
+ panel_ch2_quant=request_ch2_quant=8;
+ }else
+ rowtoggle_set_active(panel3_button_8bit,0);
+
+ if(t==panel3_button_16bit){
+ panel_ch1_quant=request_ch1_quant=16;
+ panel_ch2_quant=request_ch2_quant=16;
+ }else
+ rowtoggle_set_active(panel3_button_16bit,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel4_clicked_amp(rowslider *t){
+ panel_1kHz_amplitude = request_1kHz_amplitude = rint(t->value*65536);
+}
+
+void panel4_clicked_dither(rowwidget *t){
+ panel_dither = request_dither = rowtoggle_get_active(t);
+}
+
+void panel5_clicked_dither(rowwidget *t){
+ panel_dither = request_dither = rowtoggle_get_active(t);
+}
+
+void panel5_clicked_notch(rowwidget *t){
+ panel_1kHz_notch = request_1kHz_notch = rowtoggle_get_active(t);
+}
+
+void panel5_clicked_shape(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel5_button_flat){
+ panel_dither_shaped=request_dither_shaped=0;
+ }else
+ rowtoggle_set_active(panel5_button_flat,0);
+
+ if(t==panel5_button_shaped){
+ panel_dither_shaped=request_dither_shaped=1;
+ }else
+ rowtoggle_set_active(panel5_button_shaped,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel6_clicked_notch(rowwidget *t){
+ panel_1kHz_notch = request_1kHz_notch = rowtoggle_get_active(t);
+}
+
+void panel6_clicked_modulate(rowwidget *t){
+ request_1kHz_modulate = rowtoggle_get_active(t);
+}
+
+void panel6_clicked_dithamp(rowslider *t){
+ panel_dither_amplitude = request_dither_amplitude = rint(t->value*65536);
+}
+
+void panel6_clicked_shape(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel6_button_flat){
+ panel_dither_shaped=request_dither_shaped=0;
+ }else
+ rowtoggle_set_active(panel6_button_flat,0);
+
+ if(t==panel6_button_shaped){
+ panel_dither_shaped=request_dither_shaped=1;
+ }else
+ rowtoggle_set_active(panel6_button_shaped,0);
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel8_clicked_square(rowwidget *t){
+ panel_1kHz_square = request_1kHz_square = rowtoggle_get_active(t);
+}
+
+void panel8_clicked_offset(rowslider *t){
+ panel_1kHz_square_offset = request_1kHz_square_offset = rint(t->value*65536.);
+}
+
+void panel9_clicked_spread(rowslider *t){
+ panel_1kHz_square_spread = request_1kHz_square_spread = rint(t->value*65536.);
+}
+
+void panel10_clicked_output(rowwidget *t){
+ if(!clicked_enter){
+ clicked_enter=1;
+ if(rowtoggle_get_active(t)){
+
+ if(t==panel10_button_silence){
+ request_output_silence=1;
+ }else{
+ rowtoggle_set_active(panel10_button_silence,0);
+ request_output_silence=0;
+ }
+
+ if(t==panel10_button_tone1){
+ request_1kHz_sine=1;
+ }else{
+ rowtoggle_set_active(panel10_button_tone1,0);
+ request_1kHz_sine=0;
+ }
+
+ if(t==panel10_button_tone5){
+ request_5kHz_sine=1;
+ }else{
+ rowtoggle_set_active(panel10_button_tone5,0);
+ request_5kHz_sine=0;
+ }
+
+ if(t!=panel10_button_tone1 &&
+ t!=panel10_button_tone5)
+ request_output_tone=0;
+ else
+ request_output_tone=1;
+
+ if(t==panel10_button_noise){
+ request_output_noise=1;
+ }else{
+ rowtoggle_set_active(panel10_button_noise,0);
+ request_output_noise=0;
+ }
+
+ if(t==panel10_button_sweep){
+ request_output_sweep=1;
+ }else{
+ rowtoggle_set_active(panel10_button_sweep,0);
+ request_output_sweep=0;
+ }
+
+ if(t==panel10_button_logsweep){
+ request_output_logsweep=1;
+ }else{
+ rowtoggle_set_active(panel10_button_logsweep,0);
+ request_output_logsweep=0;
+ }
+ }else
+ rowtoggle_set_active(t,1);
+ clicked_enter=0;
+ }
+}
+
+void panel10_clicked_duallisten(rowwidget *t){
+ request_output_duallisten = rowtoggle_get_active(t);
+}
+
+/* panel communication */
+
+static void blocking_read(int fd,char *buffer, int len){
+ while(len){
+ int bytes = read(fd,buffer,len);
+ if(bytes>0){
+ len-=bytes;
+ buffer+=bytes;
+ }
+ }
+}
+
+static gboolean async_event_handle(GIOChannel *channel,
+ GIOCondition condition,
+ gpointer data){
+ unsigned char buf[1];
+
+ while(read(eventpipe[0],buf,1)>0){
+ switch((int)buf[0]){
+ case 0: /* exit */
+ gtk_main_quit();
+ break;
+ case 1: /* hw1 opened */
+ {
+ char name[255];
+ char len;
+ blocking_read(eventpipe[0],&len,1);
+ blocking_read(eventpipe[0],name,(int)len);
+
+ rowreadout_light(panel0_readout_hw,1);
+ rowwidget_add_label(panel0_readout_hw,name,1);
+ rowreadout_light(panel10_readout_hw,1);
+ rowwidget_add_label(panel10_readout_hw,name,1);
+ }
+ break;
+ case 2: /* hw1 closed */
+ rowreadout_light(panel0_readout_hw,0);
+ rowwidget_add_label(panel0_readout_hw,NULL,1);
+ rowreadout_light(panel10_readout_hw,0);
+ rowwidget_add_label(panel10_readout_hw,NULL,1);
+ break;
+ case 5: /* update shaped dither setting */
+ panel_dither_shaped = request_dither_shaped;
+ rowtoggle_set_active(panel5_button_shaped,panel_dither_shaped);
+ break;
+ case 6: /* done with an output burst; back to silence */
+ rowtoggle_set_active(panel10_button_silence,1);
+ }
+ }
+ return TRUE;
+}
+
+/* toplevel panel itself */
+
+static void make_panel(void){
+ rowpanel *p=rowpanel_new(PANEL_WIDTH,PANEL_HEIGHT,panelchange);
+ GtkBox *b;
+
+ /* panel 0 */
+ b = rowpanel_new_row(p,0);
+ panel0_readout_hw = rowreadout_new
+ (b, "<span foreground=\"#c0c0c0\">hw:1</span>");
+
+ rowspacer_new(b,10);
+
+ panel0_button_16bit = rowtoggle_new(b,"16 bit",panel0_clicked_bits);
+ panel0_button_24bit = rowtoggle_new(b,"24 bit",panel0_clicked_bits);
+
+ rowspacer_new(b,10);
+
+ panel0_button_44 = rowtoggle_new(b,"44.1kHz",panel0_clicked_rate);
+ panel0_button_48 = rowtoggle_new(b,"48kHz",panel0_clicked_rate);
+ panel0_button_96 = rowtoggle_new(b,"96kHz",panel0_clicked_rate);
+ panel0_button_192 = rowtoggle_new(b,"192kHz",panel0_clicked_rate);
+
+ rowtoggle_set_active(panel0_button_16bit,1);
+ rowtoggle_set_active(panel0_button_44,1);
+
+ /* panel 1 */
+ b = rowpanel_new_row(p,0);
+ rowlabel_new(b, "channel 1:");
+ panel1_button_c1_8 = rowtoggle_new(b,"8 bit",panel1_clicked_c1);
+ panel1_button_c1_16 = rowtoggle_new(b,"16 bit",panel1_clicked_c1);
+
+ rowspacer_new(b,10);
+ rowspacer_new(b,10);
+
+ rowlabel_new(b, "channel 2:");
+ panel1_button_c2_8 = rowtoggle_new(b,"8 bit",panel1_clicked_c2);
+ panel1_button_c2_16 = rowtoggle_new(b,"16 bit",panel1_clicked_c2);
+
+ rowtoggle_set_active(panel1_button_c1_16,1);
+ rowtoggle_set_active(panel1_button_c2_16,1);
+
+ /* panel 2 */
+ b = rowpanel_new_row(p,1);
+ rowspacer_new(b,10);
+ rowslider *t = panel2_slider_bits =
+ rowslider_new(b,"bits",panel2_clicked_bits);
+ rowslider_add_stop(t,"8",8,1);
+ rowslider_add_stop(t,"9",9,1);
+ rowslider_add_stop(t,"10",10,1);
+ rowslider_add_stop(t,"11",11,1);
+ rowslider_add_stop(t,"12",12,1);
+ rowslider_add_stop(t,"13",13,1);
+ rowslider_add_stop(t,"14",14,1);
+ rowslider_add_stop(t,"15",15,1);
+ rowslider_add_stop(t,"16",16,1);
+ rowspacer_new(b,20);
+ panel2_button_notch = rowtoggle_new(b,"notch and",panel2_clicked_notch);
+ rowwidget_add_label(panel2_button_notch,"gain",1);
+ rowspacer_new(b,10);
+
+ /* panel 3 */
+ b = rowpanel_new_row(p,0);
+ panel3_button_1k = rowtoggle_new(b,"generate",panel3_clicked_1kHz);
+ rowwidget_add_label(panel3_button_1k,"sine wave",1);
+ rowspacer_new(b,40);
+ panel3_button_8bit = rowtoggle_new(b,"8 bit",panel3_clicked_bits);
+ panel3_button_16bit = rowtoggle_new(b,"16 bit",panel3_clicked_bits);
+ rowspacer_new(b,40);
+ panel3_button_dither = rowtoggle_new(b,"dither",panel3_clicked_dither);
+
+ rowtoggle_set_active(panel3_button_1k,1);
+ rowtoggle_set_active(panel3_button_8bit,1);
+ rowtoggle_set_active(panel3_button_dither,1);
+
+ /* panel 4 */
+ b = rowpanel_new_row(p,1);
+ rowspacer_new(b,20);
+ t = panel4_slider_amp =
+ rowslider_new(b,"amplitude (bits)",panel4_clicked_amp);
+ rowslider_add_stop(t,"1/4",-1,0);
+ rowslider_add_stop(t,"1/2",0,0);
+ rowslider_add_stop(t,"1",1,0);
+ rowslider_add_stop(t,"2",2,0);
+ rowslider_add_stop(t,"4",4,0);
+ rowslider_add_stop(t,"8",8,0);
+ rowslider_add_stop(t,"16",16,0);
+ rowspacer_new(b,20);
+
+ panel4_button_dither = rowtoggle_new(b,"dither",panel4_clicked_dither);
+ rowspacer_new(b,10);
+ rowslider_set(t,16);
+
+ /* panel 5 */
+ b = rowpanel_new_row(p,0);
+
+ panel5_button_notch = rowtoggle_new(b,"notch and",panel5_clicked_notch);
+ rowwidget_add_label(panel5_button_notch,"gain",1);
+
+ rowspacer_new(b,40);
+
+ panel5_button_flat = rowtoggle_new(b,"flat",panel5_clicked_shape);
+ panel5_button_shaped = rowtoggle_new(b,"shaped",panel5_clicked_shape);
+ rowtoggle_set_active(panel5_button_flat,1);
+
+ rowspacer_new(b,40);
+
+ panel5_button_dither = rowtoggle_new(b,"dither",panel5_clicked_dither);
+
+ /* panel 6 */
+ b = rowpanel_new_row(p,1);
+
+ rowspacer_new(b,0);
+ panel6_button_notch = rowtoggle_new(b,"notch and",panel6_clicked_notch);
+ rowwidget_add_label(panel6_button_notch,"gain",1);
+ panel6_button_modulate = rowtoggle_new(b,"modulate",panel6_clicked_modulate);
+ rowwidget_add_label(panel6_button_modulate,"input",1);
+
+ rowspacer_new(b,10);
+ t = panel6_slider_amp = rowslider_new(b,"dither ",panel6_clicked_dithamp);
+ rowslider_add_stop(t,"none",0,0);
+ rowslider_add_stop(t,"",.1,0);
+ rowslider_add_stop(t,"",.2,0);
+ rowslider_add_stop(t,"",.3,0);
+ rowslider_add_stop(t,"",.4,0);
+ rowslider_add_stop(t,"",.5,0);
+ rowslider_add_stop(t,"",.6,0);
+ rowslider_add_stop(t,"",.7,0);
+ rowslider_add_stop(t,"",.8,0);
+ rowslider_add_stop(t,"",.9,0);
+ rowslider_add_stop(t,"full",1,0);
+
+ rowspacer_new(b,10);
+
+ panel6_button_flat = rowtoggle_new(b,"flat",panel6_clicked_shape);
+ panel6_button_shaped = rowtoggle_new(b,"shaped",panel6_clicked_shape);
+
+ rowspacer_new(b,0);
+ rowslider_set(t,1.0);
+
+ /* panel 7 */
+ b = rowpanel_new_row(p,0);
+ rowlabel_new(b, "filter: "
+ "<span color=\"#a0c0ff\">"
+ "cutoff=</span>"
+ "20.5kHz "
+ "<span color=\"#a0c0ff\">"
+ "rolloff="
+ "</span>"
+ "-100dB @ 21kHz");
+
+ /* panel 8 */
+ b = rowpanel_new_row(p,1);
+
+ rowspacer_new(b,25);
+
+ panel8_button_square = rowtoggle_new(b,"generate",panel8_clicked_square);
+ rowwidget_add_label(panel8_button_square,"square wave",1);
+
+ rowspacer_new(b,30);
+
+ t = panel8_slider_offset =
+ rowslider_new(b,"sample offset",panel8_clicked_offset);
+ rowslider_add_stop(t,"-1.0",-1,0);
+ rowslider_add_stop(t,"",-.8,0);
+ rowslider_add_stop(t,"",-.6,0);
+ rowslider_add_stop(t,"",-.4,0);
+ rowslider_add_stop(t,"",-.2,0);
+ rowslider_add_stop(t,"0",0,0);
+ rowslider_add_stop(t,"",.2,0);
+ rowslider_add_stop(t,"",.4,0);
+ rowslider_add_stop(t,"",.6,0);
+ rowslider_add_stop(t,"",.8,0);
+ rowslider_add_stop(t,"1.0",1,0);
+ rowspacer_new(b,20);
+ rowslider_set(t,0);
+
+ /* panel 9 */
+ b = rowpanel_new_row(p,1);
+ rowspacer_new(b,60);
+ t = panel9_slider_spread =
+ rowslider_new(b,"sample spread",panel9_clicked_spread);
+ rowslider_add_stop(t,"-1.0",-1,0);
+ rowslider_add_stop(t,"",-.8,0);
+ rowslider_add_stop(t,"",-.6,0);
+ rowslider_add_stop(t,"",-.4,0);
+ rowslider_add_stop(t,"",-.2,0);
+ rowslider_add_stop(t,"0",0,0);
+ rowslider_add_stop(t,"",.2,0);
+ rowslider_add_stop(t,"",.4,0);
+ rowslider_add_stop(t,"",.6,0);
+ rowslider_add_stop(t,"",.8,0);
+ rowslider_add_stop(t,"1.0",1,0);
+ rowspacer_new(b,50);
+ rowslider_set(t,0);
+
+ /* panel 10 */
+ b = rowpanel_new_row(p,0);
+ panel10_readout_hw = rowreadout_new
+ (b, "<span foreground=\"#c0c0c0\">hw:1</span>");
+
+ rowspacer_new(b,20);
+
+ panel10_button_silence =
+ rowtoggle_new(b,"silence",panel10_clicked_output);
+ panel10_button_tone1 =
+ rowtoggle_new(b,"1kHz tone",panel10_clicked_output);
+ panel10_button_tone5 =
+ rowtoggle_new(b,"5kHz tone",panel10_clicked_output);
+ panel10_button_noise =
+ rowtoggle_new(b,"white noise",panel10_clicked_output);
+ panel10_button_sweep =
+ rowtoggle_new(b,"linear sweep",panel10_clicked_output);
+ panel10_button_logsweep =
+ rowtoggle_new(b,"log sweep",panel10_clicked_output);
+
+ rowspacer_new(b,20);
+
+ panel10_button_duallisten =
+ rowtoggle_new(b,"two-input",panel10_clicked_duallisten);
+ rowwidget_add_label(panel10_button_duallisten,"mode",1);
+
+ rowtoggle_set_active(panel10_button_silence,1);
+ if(current_panel!=10)request_output_silence=0;
+ ready=1;
+ panelchange(p); /* force setup to be consistent with current panel */
+}
+
+int main(int argc, char **argv){
+ gtk_init (&argc, &argv);
+
+ gtk_rc_parse_string
+ ("style \"panel\" {"
+ " fg[NORMAL]=\"#ffffff\""
+ "}"
+ "style \"topframe\" {"
+ " font_name = \"sans 10 bold\""
+ " fg[NORMAL]=\"#cccccc\""
+ "}"
+ "style \"rowlabel\" {"
+ " font_name = \"sans 13 \""
+ " fg[NORMAL]=\"#cccccc\""
+ "}"
+ "class \"*\" style \"panel\""
+ "widget \"*.topframe.GtkLabel\" style \"topframe\""
+ "widget \"*.topframe*.rowlabel*\" style \"rowlabel\""
+ );
+
+ /* easiest way to inform gtk of changes and not deal with locking
+ issues around the UI */
+ if(pipe(eventpipe)){
+ fprintf(stderr,"Unable to open event pipe:\n"
+ " %s\n",strerror(errno));
+ return 1;
+ }
+
+ /* Allows event compression on the read side */
+ if(fcntl(eventpipe[0], F_SETFL, O_NONBLOCK)){
+ fprintf(stderr,"Unable to set O_NONBLOCK on event pipe:\n"
+ " %s\n",strerror(errno));
+ return 1;
+ }
+
+ /* Tell glib to watch the notificaiton pipe in gtk_main() */
+ GIOChannel *channel = g_io_channel_unix_new (eventpipe[0]);
+ g_io_channel_set_encoding (channel, NULL, NULL);
+ g_io_channel_set_buffered (channel, FALSE);
+ g_io_channel_set_close_on_unref (channel, TRUE);
+ g_io_add_watch (channel, G_IO_IN, async_event_handle, NULL);
+ g_io_channel_unref (channel);
+
+ /* go */
+ make_panel();
+
+ struct sched_param sched;
+ int policy,s;
+ sched.sched_priority = 99;
+ pthread_create(&io_thread_id,NULL,&io_thread,NULL);
+
+ if(pthread_setschedparam(io_thread_id, SCHED_FIFO, &sched)){
+ fprintf(stderr,"Unable to set realtime scheduling on io thread\n");
+ }
+
+ if(pthread_getschedparam(io_thread_id, &policy, &sched)){
+ fprintf(stderr,"Unable to check realtime scheduling on io thread\n");
+ }
+
+ printf(" io thread policy=%s, priority=%d\n",
+ (policy == SCHED_FIFO) ? "SCHED_FIFO" :
+ (policy == SCHED_RR) ? "SCHED_RR" :
+ (policy == SCHED_OTHER) ? "SCHED_OTHER" :
+ "???",
+ sched.sched_priority);
+
+
+ sched.sched_priority = 90;
+ if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sched)){
+ fprintf(stderr,"Unable to set realtime scheduling on main thread\n");
+ }
+
+ if(pthread_getschedparam(pthread_self(), &policy, &sched)){
+ fprintf(stderr,"Unable to check realtime scheduling on main thread\n");
+ }
+
+ printf(" main thread policy=%s, priority=%d\n",
+ (policy == SCHED_FIFO) ? "SCHED_FIFO" :
+ (policy == SCHED_RR) ? "SCHED_RR" :
+ (policy == SCHED_OTHER) ? "SCHED_OTHER" :
+ "???",
+ sched.sched_priority);
+
+ gtk_main();
+
+ return 0;
+}
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-wavheader.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-wavheader.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-wavheader.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <string.h>
+
+/* simple WAVE header writer */
+
+#define WAVE_FORMAT_PCM 0x0001
+#define WAVE_FORMAT_EXTENSIBLE 0xfffe
+#define WAV_HEADER_LEN 68
+
+#define WRITE_U32(buf, x) *(buf) = (unsigned char)(x&0xff);\
+ *((buf)+1) = (unsigned char)((x>>8)&0xff); \
+ *((buf)+2) = (unsigned char)((x>>16)&0xff); \
+ *((buf)+3) = (unsigned char)((x>>24)&0xff);
+
+#define WRITE_U16(buf, x) *(buf) = (unsigned char)(x&0xff);\
+ *((buf)+1) = (unsigned char)((x>>8)&0xff);
+
+struct riff_struct {
+ char id[4]; /* RIFF */
+ unsigned int len;
+ char wave_id[4]; /* WAVE */
+};
+
+struct chunk_struct {
+ char id[4];
+ unsigned int len;
+};
+
+struct common_struct
+{
+ unsigned short wFormatTag;
+ unsigned short wChannels;
+ unsigned int dwSamplesPerSec;
+ unsigned int dwAvgBytesPerSec;
+ unsigned short wBlockAlign;
+ unsigned short wBitsPerSample;
+ unsigned short cbSize;
+ unsigned short wValidBitsPerSample;
+ unsigned int dwChannelMask;
+ unsigned short subFormat;
+};
+
+struct wave_header
+{
+ struct riff_struct riff;
+ struct chunk_struct format;
+ struct common_struct common;
+ struct chunk_struct data;
+};
+
+int header_out(FILE *out, int rate, int bits, int channels){
+ struct wave_header wave;
+ char buf[WAV_HEADER_LEN];
+
+ /* Store information */
+ wave.common.wChannels = channels;
+ wave.common.wBitsPerSample = ((bits+7)>>3)<<3;
+ wave.common.wValidBitsPerSample = bits;
+ wave.common.dwSamplesPerSec = rate;
+
+ memset(buf, 0, WAV_HEADER_LEN);
+
+ /* Fill out our wav-header with some information. */
+ strncpy(wave.riff.id, "RIFF",4);
+ wave.riff.len = 0xffffffff; //size - 8; Use a bogus size for streaming */
+ strncpy(wave.riff.wave_id, "WAVE",4);
+ strncpy(wave.format.id, "fmt ",4);
+ wave.format.len = 40;
+
+ wave.common.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wave.common.dwAvgBytesPerSec =
+ wave.common.wChannels *
+ wave.common.dwSamplesPerSec *
+ (wave.common.wBitsPerSample >> 3);
+
+ wave.common.wBlockAlign =
+ wave.common.wChannels *
+ (wave.common.wBitsPerSample >> 3);
+ wave.common.cbSize = 22;
+ wave.common.subFormat = WAVE_FORMAT_PCM;
+ wave.common.dwChannelMask = 0;
+
+ strncpy(wave.data.id, "data",4);
+
+ wave.data.len = 0xffffffff; // size - WAV_HEADER_LEN; Use a bogus size for streaming */
+
+ strncpy(buf, wave.riff.id, 4);
+ WRITE_U32(buf+4, wave.riff.len);
+ strncpy(buf+8, wave.riff.wave_id, 4);
+ strncpy(buf+12, wave.format.id,4);
+ WRITE_U32(buf+16, wave.format.len);
+ WRITE_U16(buf+20, wave.common.wFormatTag);
+ WRITE_U16(buf+22, wave.common.wChannels);
+ WRITE_U32(buf+24, wave.common.dwSamplesPerSec);
+ WRITE_U32(buf+28, wave.common.dwAvgBytesPerSec);
+ WRITE_U16(buf+32, wave.common.wBlockAlign);
+ WRITE_U16(buf+34, wave.common.wBitsPerSample);
+ WRITE_U16(buf+36, wave.common.cbSize);
+ WRITE_U16(buf+38, wave.common.wValidBitsPerSample);
+ WRITE_U32(buf+40, wave.common.dwChannelMask);
+ WRITE_U16(buf+44, wave.common.subFormat);
+ memcpy(buf+46,"\x00\x00\x00\x00\x10\x00\x80\x00\x00\xAA\x00\x38\x9B\x71",14);
+ strncpy(buf+60, wave.data.id, 4);
+ WRITE_U32(buf+64, wave.data.len);
+
+ if (fwrite(buf, sizeof(char), WAV_HEADER_LEN, out)
+ != WAV_HEADER_LEN) {
+ return 0; /* Could not write wav header */
+ }
+
+ return 1;
+}
+
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-widget.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-widget.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-widget.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,983 @@
+#include "gtk-bounce.h"
+#include "gtk-bounce-widget.h"
+
+/* implement a peudowidget (rowwidget) for most entries in the panel */
+
+/* Because we're in a GtkFixed that's being scrolled around, but
+ drawing is being done to the GdkWindow owned by the toplevel, the
+ clip region is set incorrectly to allow us to overdraw things in
+ the toplevel outside our parents' bounds. Follow the tree up and
+ reclip. */
+static void narrow_clip(GtkWidget *w,GdkRectangle *area){
+ int x1 = w->allocation.x;
+ int y1 = w->allocation.y;
+ int x2 = w->allocation.x+w->allocation.width;
+ int y2 = w->allocation.y+w->allocation.height;
+
+ if(x1>area->x)area->x=x1;
+ if(x2<area->width)area->width=x2;
+ if(y1>area->y)area->y=y1;
+ if(y2<area->height)area->height=y2;
+
+ if(w->parent && w->parent->window == w->window)
+ narrow_clip(w->parent,area);
+}
+
+static void clip_to_ancestors(GtkWidget *w, cairo_t *cr){
+ GdkRectangle a;
+ a.x = w->allocation.x;
+ a.width = a.x + w->allocation.width; /* overload */
+ a.y = w->allocation.y;
+ a.height = a.y + w->allocation.height; /* overload */
+ if(w->parent && w->parent->window == w->window)
+ narrow_clip(w->parent,&a);
+ cairo_rectangle(cr,a.x,a.y,a.width-a.x,a.height-a.y);
+ cairo_clip(cr);
+}
+
+static void expose_a_widget(gpointer a, gpointer b){
+ GtkWidget *widget = (GtkWidget *)a;
+ GdkEventExpose *event = (GdkEventExpose *)b;
+ GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->
+ expose_event (widget, event);
+}
+
+static gboolean expose_rectarea(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ if(!widget->window)return TRUE;
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ rowwidget *t = (rowwidget *)userdata;
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+ int lit=t->lit;
+ /* rounded rectangle path */
+
+ double degrees = M_PI / 180.0;
+ double radius = t->radius;
+ double rounding = t->rounding;
+ double height = h-2;
+ double rxd = radius-cos(rounding*degrees)*radius;
+ double ryd = sin(rounding*degrees)*radius;
+ double R = (height/2-radius+ryd)/sin(rounding*degrees);
+ double RxD = (isinf(R)?0:R-cos(rounding*degrees)*R);
+
+ double x = widget->allocation.x+RxD-rxd+1;
+ double width = w - RxD*2 + rxd*2 - 2;
+ double y = widget->allocation.y+1;
+
+ double Rx1 = widget->allocation.x+R+1;
+ double Rx2 = widget->allocation.x+w-R-1;
+
+ if(state == GTK_STATE_INSENSITIVE){
+ y+=.5;
+ x+=.5;
+ height-=1;
+ width-=1;
+ }
+
+ clip_to_ancestors(widget,cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, -rounding*degrees);
+ if(!isinf(R))
+ cairo_arc (cr, Rx2, y+height/2, R, -rounding*degrees, rounding*degrees);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, rounding*degrees, 90*degrees);
+
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, (180-rounding)*degrees);
+ if(!isinf(R))
+ cairo_arc (cr, Rx1, y+height/2, R, (180-rounding)*degrees, (180+rounding)*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, (180+rounding)*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill background */
+ if(state != GTK_STATE_INSENSITIVE){
+ /* won't get here is not togglebutton */
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ cairo_set_source_rgba (cr, .1, .3, .6, 1);
+ else
+ cairo_set_source_rgba (cr, .1, .1, .1, 1);
+ }else{
+ if(lit)
+ cairo_set_source_rgba (cr, .18, .5, 1, .3);
+ else
+ cairo_set_source_rgba (cr, 0, 0, 0, .2);
+ }
+ cairo_fill_preserve (cr);
+
+ /* gradient */
+ if(state != GTK_STATE_INSENSITIVE && !t->pressed){
+ cairo_pattern_t *pat = cairo_pattern_create_linear(x, y, x, y+h);
+ cairo_pattern_add_color_stop_rgba(pat, 1.0, 0, 0, 0, .2);
+ cairo_pattern_add_color_stop_rgba(pat, 0.8, 0, 0, 0, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0.3, 1, 1, 1, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, .15);
+ cairo_set_source(cr, pat);
+ cairo_fill_preserve (cr);
+ cairo_pattern_destroy(pat);
+ }
+
+ /* stroke border */
+ switch(state){
+ case GTK_STATE_ACTIVE:
+ case GTK_STATE_NORMAL:
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_set_line_width(cr,2.0);
+ break;
+ case GTK_STATE_PRELIGHT:
+ case GTK_STATE_SELECTED:
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_set_line_width(cr,2.0);
+ break;
+ case GTK_STATE_INSENSITIVE:
+ if(lit)
+ cairo_set_source_rgba (cr, .18, .5, 1, .9);
+ else
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ cairo_set_line_width(cr,1.0);
+ break;
+ }
+
+ cairo_stroke(cr);
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static void rowslider_update_size(rowslider *t){
+ int i,w,h;
+
+ pango_layout_get_pixel_size(t->caption,&w,&h);
+ t->capwidth = w;
+ t->labelheight = h*1.2;
+
+ for(i=0;i<t->label_n;i++){
+ pango_layout_get_pixel_size(t->labels[i],&w,&h);
+ if(w>t->labelwidth)t->labelwidth=w;
+ }
+
+ gtk_widget_set_size_request
+ (t->button,
+ t->capwidth + t->labelwidth*t->label_n*1.5,
+ t->labelheight*2.8);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ w = t->w = t->button->allocation.width;
+ h = t->h = t->button->allocation.height;
+
+ int nh = t->labelheight*2.5;
+ t->y0 = (h-nh)/2;
+ t->y1 = t->y0 + nh;
+
+ double capw = t->capwidth;
+ double cellw = t->labelwidth;
+
+ t->radiusS = (nh-t->labelheight)/4;
+ t->radiusL = (nh-t->labelheight)/2-1.5;
+ t->lpad = t->radiusL;
+
+ t->pastdot = (t->radiusL*2-cellw/2 > 0 ?
+ t->radiusL*2-cellw/2 : 0);
+
+ double padw = (w-capw-t->lpad-t->pastdot-1) / t->label_n - cellw;
+
+ t->pipw = cellw+padw;
+ t->pipx = t->lpad+capw+padw+(cellw/2);
+ t->pastdot += cellw/2;
+
+}
+
+static gboolean expose_slider(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ if(!widget->window)return TRUE;
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ rowslider *t = (rowslider *)userdata;
+ int w = widget->allocation.width;
+ int h = widget->allocation.height;
+ int x = widget->allocation.x;
+ int y = widget->allocation.y;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(t->w!=w || t->h!=h)
+ rowslider_update_size(t);
+
+ double dif = (t->radiusL-t->radiusS);
+ double pipw = t->pipw;
+ double pipx = x+t->pipx;
+
+ /* rounded slider track */
+ double degrees = M_PI / 180.0;
+ double yy = y + t->y0 + t->labelheight+t->radiusL;
+ double sx = pipx + (t->label_n-1)*pipw;
+
+ clip_to_ancestors(widget,cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, pipx+(t->label_n-1)*t->pipw+t->pastdot-t->radiusL,
+ yy, t->radiusS, -90*degrees, 90*degrees);
+ cairo_arc (cr, x+dif+t->radiusS, yy, t->radiusS, 90*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ cairo_set_source_rgba (cr, .1, .1, .1, 1);
+ cairo_fill_preserve(cr);
+
+ cairo_set_source_rgba (cr, 1, 1, 1, .6);
+ cairo_set_line_width(cr,1.0);
+ cairo_stroke(cr);
+
+ /* pips */
+ int i;
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ for(i=0;i<t->label_n;i++){
+ sx = pipx + i*pipw;
+ cairo_new_sub_path (cr);
+ cairo_move_to(cr,rint(sx)+.5, yy-t->radiusL);
+ cairo_line_to(cr,rint(sx)+.5, yy-t->radiusS);
+ cairo_move_to(cr,rint(sx)+.5, yy+t->radiusL);
+ cairo_line_to(cr,rint(sx)+.5, yy+t->radiusS);
+ }
+ cairo_stroke(cr);
+
+ /* slider grip */
+ sx = pipx + t->delta*pipw;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, sx + t->pastdot - t->radiusL-1, yy,
+ t->radiusL, -90*degrees, 90*degrees);
+ cairo_arc (cr, x+t->radiusL+1, yy, t->radiusL, 90*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ cairo_set_source_rgba (cr, .1, .3, .6, 1);
+ cairo_fill_preserve(cr);
+
+ if(state!=GTK_STATE_ACTIVE){
+ cairo_pattern_t *pat =
+ cairo_pattern_create_linear(x, y+t->y0+t->labelheight, x, y+t->y1);
+ cairo_pattern_add_color_stop_rgba(pat, 1.0, 0, 0, 0, .3);
+ cairo_pattern_add_color_stop_rgba(pat, 0.6, 0, 0, 0, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0.4, 1, 1, 1, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, .2);
+ cairo_set_source(cr, pat);
+ cairo_fill_preserve (cr);
+ cairo_pattern_destroy(pat);
+ }
+
+ /* slider pips */
+ cairo_save(cr);
+ cairo_clip(cr);
+
+ cairo_set_source_rgba(cr,.9, .9, .9, .8);
+
+ for(i=0;i<t->label_n;i++){
+ double psx = pipx + i*pipw;
+ cairo_move_to(cr,rint(psx)+.5, yy-t->radiusL);
+ cairo_line_to(cr,rint(psx)+.5, yy-t->radiusL*.6);
+
+ cairo_move_to(cr,rint(psx)+.5, yy+t->radiusL);
+ cairo_line_to(cr,rint(psx)+.5, yy+t->radiusL*.6);
+
+ }
+ cairo_stroke(cr);
+ cairo_restore(cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, sx + t->pastdot - t->radiusL-1, yy,
+ t->radiusL, -90*degrees, 90*degrees);
+ cairo_arc (cr, x+t->radiusL+1, yy, t->radiusL, 90*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ switch(state){
+ default:
+ case GTK_STATE_ACTIVE:
+ case GTK_STATE_NORMAL:
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_set_line_width(cr,2.0);
+ break;
+ case GTK_STATE_PRELIGHT:
+ case GTK_STATE_SELECTED:
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_set_line_width(cr,2.0);
+ break;
+ }
+
+ cairo_stroke(cr);
+
+ /* slider dot */
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, rint(sx)+.5, yy, 3, 0*degrees, 360*degrees);
+ cairo_fill(cr);
+
+
+ /* labels */
+ {
+ int w,d;
+
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ cairo_move_to(cr,x+t->lpad,y+t->y0);
+ pango_cairo_show_layout(cr,t->caption);
+
+ for(i=0;i<t->label_n;i++){
+ sx = pipx + i*pipw;
+ pango_layout_get_pixel_size(t->labels[i],&w,&d);
+ cairo_move_to(cr,sx-w/2,y+t->y0);
+ pango_cairo_show_layout(cr,t->labels[i]);
+ }
+ }
+
+ cairo_destroy(cr);
+ return TRUE;
+}
+
+static gboolean expose_rowwidget(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+
+ /* be sure of the expose order as the widgets overlap */
+ /* toggle button is not a class native expose, call the draw directly */
+ expose_rectarea(t->button,event,t);
+ /* expose labels */
+ expose_a_widget(t->vbox,event);
+
+ return TRUE;
+}
+
+/* modify toggle button behavior that assumes an activated button ==
+ pressed. I don't want active toggles to be down, just lit */
+static gboolean press_rowtoggle(GtkWidget *widget,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ t->pressed=1;
+ return FALSE;
+}
+static gboolean release_rowtoggle(GtkWidget *widget,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ t->pressed=0;
+ return FALSE;
+}
+
+static gboolean toggle_rowtoggle(GtkWidget *widget,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ if(t->callback)t->callback(t);
+ return FALSE;
+}
+
+void rowwidget_add_label(rowwidget *t, char *label, int row){
+ if(!t)return;
+ if(row<0 || row>4)return;
+ if(t->label[row] && label==NULL){
+ gtk_widget_destroy(t->label[row]);
+ t->label[row]=NULL;
+ }else if(label){
+ if(!t->label[row]){
+ t->label[row]=gtk_label_new(NULL);
+ gtk_box_pack_start(GTK_BOX(t->vbox),t->label[row],0,0,0);
+ gtk_widget_show(t->label[row]);
+ }
+ gtk_label_set_markup(GTK_LABEL(t->label[row]),label);
+ }
+}
+
+rowwidget *rowtoggle_new(GtkBox *box, char *text,
+ void (*callback)(rowwidget *)){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->rounding = 15;
+ ret->radius = 12;
+ ret->parent = box;
+ ret->table = gtk_table_new(1,1,1);
+ ret->button = gtk_toggle_button_new();
+ ret->alignment = gtk_alignment_new(.5,.5,0,0);
+ ret->vbox = gtk_vbox_new(0,2);
+ ret->callback = callback;
+
+ memset(ret->label,0,sizeof(ret->label));
+
+ gtk_box_pack_start(box,ret->table,0,0,0);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->alignment,0,1,0,1);
+ gtk_container_add(GTK_CONTAINER(ret->alignment),ret->vbox);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->button,0,1,0,1);
+
+ g_signal_connect(G_OBJECT(ret->button), "expose-event",
+ G_CALLBACK(expose_rectarea), ret);
+ g_signal_connect(G_OBJECT(ret->button), "pressed",
+ G_CALLBACK(press_rowtoggle), ret);
+ g_signal_connect(G_OBJECT(ret->button), "released",
+ G_CALLBACK(release_rowtoggle), ret);
+ g_signal_connect(G_OBJECT(ret->button), "toggled",
+ G_CALLBACK(toggle_rowtoggle), ret);
+ g_signal_connect(G_OBJECT(ret->table), "expose-event",
+ G_CALLBACK(expose_rowwidget), ret);
+ gtk_widget_set_size_request(ret->button,
+ GTK_WIDGET(box)->allocation.height*1.5,-1);
+
+ if(text)
+ rowwidget_add_label(ret, text, 0);
+
+ gtk_widget_show_all(ret->table);
+ return ret;
+}
+
+void rowtoggle_set_active(rowwidget *w, int state){
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w->button),state);
+}
+
+int rowtoggle_get_active(rowwidget *w){
+ return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w->button));
+}
+
+rowwidget *rowreadout_new(GtkBox *box, char *text){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->rounding = 0;
+ ret->radius = 6;
+ ret->parent = box;
+ ret->table = gtk_table_new(1,1,1);
+ ret->button = gtk_toggle_button_new();
+ ret->alignment = gtk_alignment_new(.5,.5,0,0);
+ ret->vbox = gtk_vbox_new(0,2);
+
+ memset(ret->label,0,sizeof(ret->label));
+
+ gtk_box_pack_start(box,ret->table,0,0,0);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->alignment,0,1,0,1);
+ gtk_container_add(GTK_CONTAINER(ret->alignment),ret->vbox);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->button,0,1,0,1);
+ gtk_widget_set_sensitive(ret->button,FALSE);
+
+ g_signal_connect(G_OBJECT(ret->button), "expose-event",
+ G_CALLBACK(expose_rectarea), ret);
+ g_signal_connect(G_OBJECT(ret->table), "expose-event",
+ G_CALLBACK(expose_rowwidget), ret);
+ gtk_widget_set_size_request(ret->button,GTK_WIDGET(box)->allocation.height*1.8,-1);
+
+ if(text)
+ rowwidget_add_label(ret, text, 0);
+
+ gtk_widget_show_all(ret->table);
+ return ret;
+}
+
+void rowreadout_light(rowwidget *t,int state){
+ GdkRectangle rect;
+ t->lit=state;
+ if(t->button->window){
+ rect.x = t->button->allocation.x;
+ rect.y = t->button->allocation.y;
+ rect.width = t->button->allocation.width;
+ rect.height = t->button->allocation.height;
+ gdk_window_invalidate_rect(t->button->window,&rect,1);
+ }
+}
+
+rowwidget *rowspacer_new(GtkBox *box,int pad){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->parent = box;
+ ret->alignment = gtk_alignment_new(.5,.5,0,0);
+
+ gtk_box_pack_start(box,ret->alignment,0,0,0);
+
+ gtk_widget_set_size_request(ret->alignment,pad,-1);
+
+ gtk_widget_show_all(ret->alignment);
+ return ret;
+}
+
+rowwidget *rowlabel_new(GtkBox *box,char *text){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->vbox = gtk_vbox_new(1,2);
+ ret->parent = box;
+
+ memset(ret->label,0,sizeof(ret->label));
+ gtk_box_pack_start(box,ret->vbox,0,0,0);
+ gtk_widget_set_name(ret->vbox,"rowlabel");
+
+ if(text)
+ rowwidget_add_label(ret, text, 0);
+ gtk_widget_show_all(ret->vbox);
+ return ret;
+}
+
+static void slider_set(rowslider *t, int ex){
+
+ t->delta = (ex - t->pipx)/t->pipw;
+
+ if(t->delta<0)t->delta=0;
+ if(t->delta>t->label_n-1)t->delta=t->label_n-1;
+
+ int i=floor(t->delta);
+ if(t->stops[i]){
+ if(t->delta-i>.5){
+ t->value=t->values[i+1];
+ t->delta=i+1;
+ }else{
+ t->value=t->values[i];
+ t->delta=i;
+ }
+ }else{
+ if(i==t->label_n-1){
+ t->value=t->values[t->label_n-1];
+ }else{
+ t->value = (i-t->delta+1)*t->values[i] + (t->delta-i)*t->values[i+1];
+ }
+ }
+
+ if(t->button->window){
+ GdkRectangle rect;
+ rect.x = t->button->allocation.x;
+ rect.y = t->button->allocation.y;
+ rect.width = t->button->allocation.width;
+ rect.height = t->button->allocation.height;
+ gdk_window_invalidate_rect(t->button->window,&rect,1);
+ }
+
+ if(t->callback)t->callback(t);
+}
+
+static gboolean motion_slider(GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata){
+ rowslider *t = (rowslider *)userdata;
+ GdkEventMotion *ev = (GdkEventMotion *)event;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(t->capwidth==0)
+ rowslider_update_size(t);
+
+ if(state==GTK_STATE_ACTIVE)
+ slider_set(t,ev->x);
+
+ t->x = ev->x;
+ return TRUE;
+}
+
+static gboolean press_slider(GtkWidget *widget,
+ gpointer userdata){
+ rowslider *t = (rowslider *)userdata;
+ slider_set(t,t->x);
+
+ return TRUE;
+}
+
+static gboolean press_slider_logx(GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata){
+ GdkEventButton *ev = (GdkEventButton *)event;
+ rowslider *t = (rowslider *)userdata;
+ t->x = ev->x;
+ return FALSE;
+}
+
+rowslider *rowslider_new(GtkBox *box,
+ char *caption,
+ void (*callback)(rowslider *)){
+ rowslider *ret = calloc(1,sizeof(*ret));
+ ret->parent = box;
+ ret->button = gtk_button_new();
+ gtk_widget_set_name(ret->button,"rowslider");
+
+ char buf[256];
+ snprintf(buf,256,"<span weight=\"bold\">%s</span>",caption);
+ ret->caption = gtk_widget_create_pango_layout(ret->button,NULL);
+ pango_layout_set_markup(ret->caption,buf,-1);
+
+ ret->callback = callback;
+ ret->labels = calloc(1,sizeof(*ret->labels));
+ ret->values = calloc(1,sizeof(*ret->values));
+ ret->stops = calloc(1,sizeof(*ret->stops));
+
+ gtk_box_pack_start(GTK_BOX(box),ret->button,1,1,0);
+
+ gtk_widget_add_events (ret->button,
+ GDK_POINTER_MOTION_MASK);
+
+ g_signal_connect(G_OBJECT(ret->button), "expose-event",
+ G_CALLBACK(expose_slider), ret);
+ g_signal_connect(G_OBJECT(ret->button), "motion_notify_event",
+ G_CALLBACK(motion_slider), ret);
+ g_signal_connect(G_OBJECT(ret->button), "button-press-event",
+ G_CALLBACK(press_slider_logx), ret);
+ g_signal_connect(G_OBJECT(ret->button), "pressed",
+ G_CALLBACK(press_slider), ret);
+
+ gtk_widget_show_all(ret->button);
+ return ret;
+}
+
+void rowslider_add_stop(rowslider *t,
+ char *ltext,
+ float lvalue,
+ int snap){
+ int n = t->label_n;
+
+ t->labels = realloc(t->labels,sizeof(*t->labels)*(n+1));
+ t->values = realloc(t->values,sizeof(*t->values)*(n+1));
+ t->stops = realloc(t->stops,sizeof(*t->stops)*(n+1));
+ t->labels[n]=gtk_widget_create_pango_layout(t->button,ltext);
+
+ t->values[n]=lvalue;
+ t->stops[n]=snap;
+ t->label_n++;
+
+ rowslider_update_size(t);
+}
+
+void rowslider_set(rowslider *t,float lvalue){
+ int i;
+ int flip=1.0;
+ if(t->values[0] > t->values[t->label_n-1])
+ flip=-1.0;
+
+ lvalue*=flip;
+ if(lvalue<=t->values[0]*flip){
+ t->value = t->values[0];
+ t->delta = 0;
+ } else if(lvalue>=t->values[t->label_n-1]*flip){
+ t->value = t->values[t->label_n-1];
+ t->delta = t->label_n-1;
+ }else{
+ for(i=0;i<t->label_n-1;i++){
+ if(lvalue>=t->values[i]*flip && lvalue<=t->values[i+1]*flip){
+ float delta = (lvalue-t->values[i]*flip) /
+ (t->values[i+1]-t->values[i])*flip;
+ if(t->stops[i]){
+ if(delta<.5){
+ t->value = t->values[i];
+ t->delta = i;
+ }else{
+ t->value = t->values[i+1];
+ t->delta = i+1;
+ }
+ }else{
+ t->value = lvalue*flip;
+ t->delta = i+delta;
+ }
+ }
+ }
+ }
+
+ if(t->button->window){
+ GdkRectangle rect;
+ rect.x = t->button->allocation.x;
+ rect.y = t->button->allocation.y;
+ rect.width = t->button->allocation.width;
+ rect.height = t->button->allocation.height;
+ gdk_window_invalidate_rect(t->button->window,&rect,1);
+ }
+
+ if(t->callback)t->callback(t);
+}
+
+
+/* housekeeping for the up and down buttons, along with the panel
+ scrolling implementation */
+
+static gboolean expose_upbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+radius+.5;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, 225*degrees, 315*degrees);
+ cairo_arc (cr, x+scale, y+scale, radius, -45*degrees, 135*degrees);
+ cairo_line_to (cr, x, y+sqrt(2)*radius);
+ cairo_arc (cr, x-scale, y+scale, radius, 45*degrees, 225*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ cairo_set_source_rgba (cr, .8, .8, .8, .2);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static gboolean expose_downbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+h-radius-1.5;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, -315*degrees, -225*degrees);
+ cairo_arc (cr, x-scale, y-scale, radius, -225*degrees, -45*degrees);
+ cairo_line_to (cr, x, y-sqrt(2)*radius);
+ cairo_arc (cr, x+scale, y-scale, radius, -135*degrees, 45*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ cairo_set_source_rgba (cr, .8, .8, .8, .2);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static void animate_panel(rowpanel *p){
+ int h = p->panel_scrollfix->allocation.height;
+ double new_now = filter_filter(p->current_panel,&p->display_filter);
+ if(fabs(new_now - p->current_panel)*h<1.){
+ p->display_animating=0;
+ p->display_now=p->current_panel;
+ }else{
+ p->display_now=new_now;
+ g_timeout_add(25,(GSourceFunc)animate_panel,p);
+ }
+ gtk_fixed_move(GTK_FIXED(p->panel_scrollfix),p->panel_lefttable,
+ 0,rint(-h*p->display_now));
+}
+
+static void set_current_panel(rowpanel *p, int n){
+ if(p->display_animating){
+ p->current_panel=n;
+ }else{
+ p->display_animating = 1;
+ filter_set(&p->display_filter,p->current_panel);
+ p->current_panel=n;
+ animate_panel(p);
+ }
+
+ if(p->current_panel==0){
+ gtk_widget_set_sensitive(p->upbutton,FALSE);
+ }else{
+ gtk_widget_set_sensitive(p->upbutton,TRUE);
+ }
+ if(p->current_panel+1==p->num_panels){
+ gtk_widget_set_sensitive(p->downbutton,FALSE);
+ }else{
+ gtk_widget_set_sensitive(p->downbutton,TRUE);
+ }
+ if(p->callback)p->callback(p);
+}
+
+static void upbutton_clicked(GtkWidget *widget, gpointer data){
+ rowpanel *p=(rowpanel *)data;
+ if(p->current_panel>0)
+ set_current_panel(p,p->current_panel-1);
+}
+
+static void downbutton_clicked(GtkWidget *widget, gpointer data){
+ rowpanel *p=(rowpanel *)data;
+ if(p->current_panel+1<p->num_panels)
+ set_current_panel(p,p->current_panel+1);
+}
+
+gboolean supports_alpha = FALSE;
+static void screen_changed(GtkWidget *widget,
+ GdkScreen *old_screen, gpointer userdata){
+
+ /* To check if the display supports alpha channels, get the colormap */
+ GdkScreen *screen = gtk_widget_get_screen(widget);
+ GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+
+ if (!colormap){
+ printf("Your screen does not support alpha channels!\n");
+ colormap = gdk_screen_get_rgb_colormap(screen);
+ supports_alpha = FALSE;
+ }else{
+ supports_alpha = TRUE;
+ }
+
+ gtk_widget_set_colormap(widget, colormap);
+}
+
+static gboolean expose_toplevel(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+
+ /* clear background to transparent */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0); /* transparent */
+ cairo_paint (cr);
+
+ /* rounded rectangle path */
+ double
+ x = 1, /* parameters like cairo_rectangle */
+ y = 1,
+ width = w-2,
+ height = h-2,
+ radius = 8.0;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, 0);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, 0, 90*degrees);
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, 180*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, 180*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent dark background */
+ cairo_set_source_rgba (cr, 0, 0, 0, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,2.0);
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+/* hide the X cursor upon entry -- this is a touch tablet app */
+static gboolean hide_mouse(GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata){
+ GdkCursor *cursor=gdk_cursor_new(GDK_BLANK_CURSOR);
+ gdk_window_set_cursor(widget->window, cursor);
+ gdk_cursor_destroy(cursor);
+ return TRUE;
+}
+
+rowpanel *rowpanel_new(int w, int h, void (*callback)(rowpanel *)){
+ rowpanel *p = calloc(1,sizeof(*p));
+
+ GtkWidget *topbox = gtk_hbox_new(0,0);
+ GtkWidget *rightbox = gtk_vbox_new(1,0);
+ GtkWidget *leftbox = gtk_hbox_new(1,0);
+
+ p->toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ p->callback = callback;
+ gtk_widget_set_app_paintable(p->toplevel, TRUE);
+ g_signal_connect(G_OBJECT(p->toplevel), "screen-changed",
+ G_CALLBACK(screen_changed), p);
+ g_signal_connect(G_OBJECT(p->toplevel), "expose-event",
+ G_CALLBACK(expose_toplevel), p);
+ g_signal_connect(G_OBJECT(p->toplevel), "enter-notify-event",
+ G_CALLBACK(hide_mouse), p);
+
+ /* toplevel is a fixed size, meant to be nailed to the screen in one
+ spot */
+ p->w=w;
+ p->h=h;
+ gtk_widget_set_size_request(GTK_WIDGET(p->toplevel),w,h);
+ gtk_window_set_resizable(GTK_WINDOW(p->toplevel),FALSE);
+ gtk_window_set_decorated(GTK_WINDOW(p->toplevel),FALSE);
+ gtk_window_set_gravity(GTK_WINDOW(p->toplevel),GDK_GRAVITY_SOUTH_WEST);
+ gtk_window_move (GTK_WINDOW(p->toplevel),0,gdk_screen_height()-1);
+
+ p->panel_scrollfix = gtk_fixed_new();
+ p->panel_lefttable = gtk_table_new(1,1,1);
+ p->upbutton = gtk_button_new();
+ p->downbutton = gtk_button_new();
+
+ gtk_widget_set_size_request(GTK_WIDGET(p->upbutton),40,25);
+ gtk_widget_set_size_request(GTK_WIDGET(p->downbutton),40,25);
+
+ gtk_container_add(GTK_CONTAINER(p->toplevel),topbox);
+ gtk_container_set_border_width(GTK_CONTAINER(topbox),2);
+ gtk_box_pack_start(GTK_BOX(topbox),leftbox,1,1,0);
+ gtk_box_pack_end(GTK_BOX(topbox),rightbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(leftbox),p->panel_scrollfix,1,1,0);
+ gtk_box_pack_start(GTK_BOX(rightbox),p->upbutton,0,1,3);
+ gtk_box_pack_end(GTK_BOX(rightbox),p->downbutton,0,1,3);
+
+ g_signal_connect(G_OBJECT(p->upbutton), "expose-event",
+ G_CALLBACK(expose_upbutton), p);
+ g_signal_connect(G_OBJECT(p->downbutton), "expose-event",
+ G_CALLBACK(expose_downbutton), p);
+ g_signal_connect(G_OBJECT(p->upbutton), "pressed",
+ G_CALLBACK(upbutton_clicked), p);
+ g_signal_connect(G_OBJECT(p->downbutton), "pressed",
+ G_CALLBACK(downbutton_clicked), p);
+
+ filter_make_critical(.07,1,&p->display_filter);
+ gtk_widget_set_size_request(p->panel_lefttable,w-44,-1);
+ gtk_fixed_put(GTK_FIXED(p->panel_scrollfix),p->panel_lefttable,0,0);
+
+ screen_changed(p->toplevel, NULL, NULL);
+ set_current_panel(p,0);
+ gtk_widget_show_all(p->toplevel);
+
+ return p;
+}
+
+GtkBox *rowpanel_new_row(rowpanel *p, int fill_p){
+ int boxborder=8;
+ int n = ++p->num_panels;
+ GtkWidget *heightbox = gtk_hbox_new(0,0);
+ GtkWidget *heightforce = gtk_vbox_new(1,0);
+ GtkWidget *panelframe = gtk_frame_new(NULL);
+ GtkWidget *centerbox = gtk_hbox_new(1,0);
+ GtkWidget *buttonbox = gtk_hbox_new(0,6);
+
+ gtk_table_resize(GTK_TABLE(p->panel_lefttable),n,1);
+
+ gtk_table_attach(GTK_TABLE(p->panel_lefttable),heightbox,0,1,n-1,n,
+ GTK_EXPAND|GTK_FILL,0,0,0);
+ gtk_box_pack_start(GTK_BOX(heightbox),heightforce,0,0,0);
+ gtk_widget_set_size_request(heightforce,1,p->h-4);
+
+ gtk_widget_set_name(panelframe,"topframe");
+ gtk_frame_set_label_align(GTK_FRAME(panelframe),.5,.5);
+ gtk_container_set_border_width(GTK_CONTAINER(panelframe),boxborder);
+ gtk_frame_set_shadow_type(GTK_FRAME(panelframe),GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(heightbox),panelframe,1,1,0);
+ gtk_container_add(GTK_CONTAINER(panelframe),centerbox);
+ gtk_container_set_border_width(GTK_CONTAINER(buttonbox),boxborder);
+
+ if(fill_p){
+ /* expanding box will fill */
+ gtk_box_pack_start(GTK_BOX(centerbox),buttonbox,1,1,0);
+ }else{
+ /* nonexpanding box will center */
+ gtk_box_pack_start(GTK_BOX(centerbox),buttonbox,0,0,0);
+ }
+ gtk_widget_show_all(heightbox);
+
+ while (gtk_events_pending())
+ gtk_main_iteration();
+
+ return GTK_BOX(buttonbox);
+}
Added: trunk/Xiph-episode-II/bounce/gtk-bounce-widget.h
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce-widget.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce-widget.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,93 @@
+#ifndef _BOUNCE_WIDGET_H_
+#define _BOUNCE_WIDGET_H_
+
+#include <gtk/gtk.h>
+#include "twopole.h"
+
+typedef struct rowpanel {
+ int w;
+ int h;
+ GtkWidget *toplevel;
+
+ int num_panels;
+ int current_panel;
+ pole2 display_filter;
+ double display_now;
+ int display_animating;
+ GtkWidget *panel_scrollfix;
+ GtkWidget *panel_lefttable;
+ GtkWidget *upbutton;
+ GtkWidget *downbutton;
+ void (*callback)(struct rowpanel *);
+} rowpanel;
+
+typedef struct rowwidget {
+ GtkBox *parent;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *vbox;
+ GtkWidget *label[5];
+ GtkWidget *alignment;
+ int radius;
+ int rounding;
+ void (*callback)(struct rowwidget *);
+ int pressed;
+ int lit;
+} rowwidget;
+
+typedef struct rowslider {
+ GtkBox *parent;
+ GtkWidget *button;
+
+ PangoLayout *caption;
+ PangoLayout **labels;
+ float *values;
+ int *stops;
+ int label_n;
+ int capwidth;
+ int labelwidth;
+ int labelheight;
+
+ float value;
+ float delta;
+ int x;
+ void (*callback)(struct rowslider *);
+
+
+ double radiusS;
+ double radiusL;
+ double lpad;
+ double pastdot;
+ double pipw;
+ double pipx;
+ int w;
+ int h;
+ int y0;
+ int y1;
+
+} rowslider;
+
+extern GtkWidget *bounce_toplevel(void);
+
+extern rowwidget *rowtoggle_new(GtkBox *box, char *text,
+ void (*callback)(rowwidget *));
+extern void rowwidget_add_label(rowwidget *t, char *label, int row);
+
+extern int rowtoggle_get_active(rowwidget *w);
+extern void rowtoggle_set_active(rowwidget *w, int state);
+extern rowwidget *rowreadout_new(GtkBox *box, char *text);
+extern void rowreadout_light(rowwidget *t,int state);
+extern rowwidget *rowspacer_new(GtkBox *box,int pad);
+extern rowwidget *rowlabel_new(GtkBox *box,char *text);
+extern rowslider *rowslider_new(GtkBox *box,
+ char *caption,
+ void (*callback)(rowslider *));
+extern void rowslider_add_stop(rowslider *t,
+ char *ltext,
+ float lvalue,
+ int snap);
+extern void rowslider_set(rowslider *t,float lvalue);
+extern rowpanel *rowpanel_new(int w, int h, void (*callback)(rowpanel *));
+extern GtkBox *rowpanel_new_row(rowpanel *p, int fill_p);
+
+#endif
Added: trunk/Xiph-episode-II/bounce/gtk-bounce.c
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,628 @@
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gtk/gtk.h>
+
+#define PANEL_WIDTH 1024
+#define PANEL_HEIGHT 150
+
+int current_panel=0;
+int num_panels=4;
+
+gboolean supports_alpha = FALSE;
+static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
+{
+ /* To check if the display supports alpha channels, get the colormap */
+ GdkScreen *screen = gtk_widget_get_screen(widget);
+ GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+
+ if (!colormap){
+ printf("Your screen does not support alpha channels!\n");
+ colormap = gdk_screen_get_rgb_colormap(screen);
+ supports_alpha = FALSE;
+ }else{
+ supports_alpha = TRUE;
+ }
+
+ gtk_widget_set_colormap(widget, colormap);
+}
+
+static gboolean expose_toplevel(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+
+ /* clear background to transparent */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0); /* transparent */
+ cairo_paint (cr);
+
+ /* rounded rectangle path */
+ double
+ x = 1, /* parameters like cairo_rectangle */
+ y = 1,
+ width = w-2,
+ height = h-2,
+ radius = 8.0;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, 0);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, 0, 90*degrees);
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, 180*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, 180*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent dark background */
+ cairo_set_source_rgba (cr, 0, 0, 0, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,2.0);
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+/* Because we're in a GtkFixed that's being scrolled around, but
+ drawing is being done to the GdkWindow owned by the toplevel, the
+ clip region is set incorrectly to allow us to overdraw things in
+ the toplevel outside our parents' bounds. Follow the tree up and
+ reclip. */
+static void narrow_clip(GtkWidget *w,GdkRectangle *area){
+ int x1 = w->allocation.x;
+ int y1 = w->allocation.y;
+ int x2 = w->allocation.x+w->allocation.width;
+ int y2 = w->allocation.y+w->allocation.height;
+
+ if(x1>area->x)area->x=x1;
+ if(x2<area->width)area->width=x2;
+ if(y1>area->y)area->y=y1;
+ if(y2<area->height)area->height=y2;
+
+ if(w->parent && w->parent->window == w->window)
+ narrow_clip(w->parent,area);
+}
+
+static void clip_to_ancestors(GtkWidget *w, cairo_t *cr){
+ GdkRectangle a;
+ a.x = w->allocation.x;
+ a.width = a.x + w->allocation.width; /* overload */
+ a.y = w->allocation.y;
+ a.height = a.y + w->allocation.height; /* overload */
+ if(w->parent && w->parent->window == w->window)
+ narrow_clip(w->parent,&a);
+ cairo_rectangle(cr,a.x,a.y,a.width-a.x,a.height-a.y);
+ cairo_clip(cr);
+}
+
+void expose_a_widget(gpointer a, gpointer b){
+ GtkWidget *widget = (GtkWidget *)a;
+ GdkEventExpose *event = (GdkEventExpose *)b;
+ GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->
+ expose_event (widget, event);
+}
+
+typedef struct {
+ GtkBox *parent;
+ GtkWidget *table;
+ GtkWidget *button;
+ GtkWidget *vbox;
+ GtkWidget *label[5];
+ GtkWidget *alignment;
+ int pressed;
+ int lit;
+} rowwidget;
+
+static gboolean expose_rectarea(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ if(!widget->window)return TRUE;
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ rowwidget *t = (rowwidget *)userdata;
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+ int lit=t->lit;
+ /* rounded rectangle path */
+
+ double degrees = M_PI / 180.0;
+ double radius = 12;
+ double height = h-2;
+ double rxd = radius-cos(15*degrees)*radius;
+ double ryd = sin(15*degrees)*radius;
+ double R = (height/2-radius+ryd)/sin(15*degrees);
+ double RxD = R-cos(15*degrees)*R;
+
+ double x = widget->allocation.x+RxD-rxd+1;
+ double width = w - RxD*2 + rxd*2 - 2;
+ double y = widget->allocation.y+1;
+
+ double Rx1 = widget->allocation.x+R+1;
+ double Rx2 = widget->allocation.x+w-R-1;
+
+ clip_to_ancestors(widget,cr);
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, -15*degrees);
+ cairo_arc (cr, Rx2, y+height/2, R, -15*degrees, 15*degrees);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, 15*degrees, 90*degrees);
+
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, 165*degrees);
+ cairo_arc (cr, Rx1, y+height/2, R, 165*degrees, 195*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, 195*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill background */
+ if(state != GTK_STATE_INSENSITIVE){
+ /* won't get here is not togglebutton */
+ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
+ cairo_set_source_rgba (cr, .1, .3, .6, 1);
+ else
+ cairo_set_source_rgba (cr, .1, .1, .1, 1);
+ }else{
+ if(lit)
+ cairo_set_source_rgba (cr, 0, .5, 1, .4);
+ else
+ cairo_set_source_rgba (cr, 0, 0, 0, .5);
+ }
+ cairo_fill_preserve (cr);
+
+ /* gradient */
+ if(state != GTK_STATE_INSENSITIVE){
+ cairo_pattern_t *pat = cairo_pattern_create_linear(x, y, x, y+h);
+ if(!t->pressed){
+ cairo_pattern_add_color_stop_rgba(pat, 1.0, 0, 0, 0, .2);
+ cairo_pattern_add_color_stop_rgba(pat, 0.8, 0, 0, 0, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0.3, 1, 1, 1, 0);
+ cairo_pattern_add_color_stop_rgba(pat, 0, 1, 1, 1, .15);
+ }
+ cairo_set_source(cr, pat);
+ cairo_fill_preserve (cr);
+ cairo_pattern_destroy(pat);
+ }
+
+ /* stroke border */
+ if(state != GTK_STATE_INSENSITIVE){
+ switch(state){
+ case GTK_STATE_ACTIVE:
+ case GTK_STATE_NORMAL:
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ break;
+ case GTK_STATE_PRELIGHT:
+ case GTK_STATE_SELECTED:
+ cairo_set_source_rgba (cr, 1, 1, 1, 1);
+ break;
+ }
+
+ cairo_set_line_width(cr,2.0);
+ cairo_stroke(cr);
+ }
+ cairo_destroy(cr);
+
+ /* have to do this by hand as returning TRUE below short-circuits
+ the whole expose chain */
+ //GList *children=gtk_container_get_children(GTK_CONTAINER(widget));
+ //g_list_foreach(children,expose_a_widget,event);
+
+ return TRUE;
+}
+
+/* hide the X cursor upon entry -- this is a touch tablet app */
+static gboolean hide_mouse(GtkWidget *widget,
+ GdkEvent *event,
+ gpointer userdata){
+ GdkCursor *cursor=gdk_cursor_new(GDK_BLANK_CURSOR);
+ gdk_window_set_cursor(widget->window, cursor);
+ gdk_cursor_destroy(cursor);
+}
+
+static gboolean expose_rowwidget(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ int i;
+
+ /* be sure of the expose order as the widgets overlap */
+ /* toggle button is not a class native expose, call the draw directly */
+ expose_rectarea(t->button,event,t);
+ /* expose labels */
+ expose_a_widget(t->vbox,event);
+
+ return TRUE;
+}
+
+/* modify toggle button behavior that assumes an activated button ==
+ pressed. I don't want active toggles to be down, just lit */
+static gboolean press_rowtoggle(GtkWidget *widget,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ t->pressed=1;
+ return FALSE;
+}
+static gboolean release_rowtoggle(GtkWidget *widget,
+ gpointer userdata){
+ rowwidget *t = (rowwidget *)userdata;
+ t->pressed=0;
+ return FALSE;
+}
+
+static rowwidget *rowtoggle_new(GtkBox *box, GCallback callback){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->parent = box;
+ ret->table = gtk_table_new(1,1,1);
+ ret->button = gtk_toggle_button_new();
+ ret->alignment = gtk_alignment_new(.5,.5,0,0);
+ ret->vbox = gtk_vbox_new(0,2);
+
+ memset(ret->label,0,sizeof(ret->label));
+
+ gtk_box_pack_start(box,ret->table,0,0,10);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->alignment,0,1,0,1);
+ gtk_container_add(GTK_CONTAINER(ret->alignment),ret->vbox);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->button,0,1,0,1);
+
+ g_signal_connect(G_OBJECT(ret->button), "expose-event",
+ G_CALLBACK(expose_rectarea), ret);
+ g_signal_connect(G_OBJECT(ret->button), "pressed",
+ G_CALLBACK(press_rowtoggle), ret);
+ g_signal_connect(G_OBJECT(ret->button), "released",
+ G_CALLBACK(release_rowtoggle), ret);
+ g_signal_connect(G_OBJECT(ret->table), "expose-event",
+ G_CALLBACK(expose_rowwidget), ret);
+ gtk_widget_set_size_request(ret->button,PANEL_HEIGHT,-1);
+
+ return ret;
+}
+
+static rowwidget *rowlabel_new(GtkBox *box){
+ rowwidget *ret = calloc(1,sizeof(*ret));
+ ret->parent = box;
+ ret->table = gtk_table_new(1,1,1);
+ ret->button = gtk_toggle_button_new();
+ ret->alignment = gtk_alignment_new(.5,.5,0,0);
+ ret->vbox = gtk_vbox_new(0,2);
+
+ memset(ret->label,0,sizeof(ret->label));
+
+ gtk_box_pack_start(box,ret->table,0,0,10);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->alignment,0,1,0,1);
+ gtk_container_add(GTK_CONTAINER(ret->alignment),ret->vbox);
+ gtk_table_attach_defaults(GTK_TABLE(ret->table),ret->button,0,1,0,1);
+ gtk_widget_set_sensitive(ret->button,FALSE);
+
+ g_signal_connect(G_OBJECT(ret->button), "expose-event",
+ G_CALLBACK(expose_rectarea), ret);
+ g_signal_connect(G_OBJECT(ret->table), "expose-event",
+ G_CALLBACK(expose_rowwidget), ret);
+ gtk_widget_set_size_request(ret->button,PANEL_HEIGHT,-1);
+
+ return ret;
+}
+
+static rowwidget *rowlabel_light(rowwidget *t,int state){
+ t->lit=state;
+ expose_rectarea(t->button,NULL,t);
+}
+
+static void rowwidget_add_label(rowwidget *t, char *label, int row){
+ if(!t)return;
+ if(row<0 || row>4)return;
+ if(t->label[row] && label==NULL){
+ gtk_widget_destroy(t->label[row]);
+ t->label[row]=NULL;
+ }else{
+ if(!t->label[row]){
+ t->label[row]=gtk_label_new(NULL);
+ gtk_box_pack_start(GTK_BOX(t->vbox),t->label[row],0,0,0);
+ }
+ gtk_label_set_markup(GTK_LABEL(t->label[row]),label);
+ }
+}
+
+static gboolean expose_upbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+radius;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, 225*degrees, 315*degrees);
+ cairo_arc (cr, x+scale, y+scale, radius, -45*degrees, 135*degrees);
+ cairo_line_to (cr, x, y+sqrt(2)*radius);
+ cairo_arc (cr, x-scale, y+scale, radius, 45*degrees, 225*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ if(state == GTK_STATE_PRELIGHT)
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static gboolean expose_downbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+h-radius-1;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, -315*degrees, -225*degrees);
+ cairo_arc (cr, x-scale, y-scale, radius, -225*degrees, -45*degrees);
+ cairo_line_to (cr, x, y-sqrt(2)*radius);
+ cairo_arc (cr, x+scale, y-scale, radius, -135*degrees, 45*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ if(state == GTK_STATE_PRELIGHT)
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+typedef struct {
+ double a;
+ double b1;
+ double b2;
+ double x[2];
+ double y[2];
+} pole2;
+
+static void filter_reset(pole2 *p, double val){
+ p->x[0]=p->x[1]=val;
+ p->y[0]=p->y[1]=val;
+}
+
+static void filter_make_critical(double w, pole2 *f){
+ double w0 = tan(M_PI*w*pow(pow(2,.5)-1,-.5));
+ f->a = w0*w0/(1+(2*w0)+w0*w0);
+ f->b1 = 2*f->a*(1/(w0*w0)-1);
+ f->b2 = 1-(4*f->a+f->b1);
+ filter_reset(f,0);
+}
+
+static double filter_filter(double x, pole2 *p){
+ double y =
+ p->a*x + 2*p->a*p->x[0] + p->a*p->x[1] +
+ p->b1*p->y[0] + p->b2*p->y[1];
+ p->y[1] = p->y[0]; p->y[0] = y;
+ p->x[1] = p->x[0]; p->x[0] = x;
+ return y;
+}
+
+static pole2 display_filter;
+static double display_now=0;
+static int display_animating=0;
+GtkWidget *panel_scrollfix=NULL;
+GtkWidget *panel_lefttable=NULL;
+GtkWidget *upbutton=NULL;
+GtkWidget *downbutton=NULL;
+
+static void animate_panel(){
+ int h = panel_scrollfix->allocation.height;
+ double new_now = filter_filter(current_panel,&display_filter);
+ if(fabs(new_now - current_panel)*h<1.){
+ display_animating=0;
+ display_now=current_panel;
+ }else{
+ display_now=new_now;
+ g_timeout_add(25,(GSourceFunc)animate_panel,NULL);
+ }
+ gtk_fixed_move(GTK_FIXED(panel_scrollfix),panel_lefttable,
+ 0,rint(-h*display_now));
+}
+
+static void set_current_panel(int n){
+ if(display_animating){
+ current_panel=n;
+ }else{
+ display_animating = 1;
+ filter_reset(&display_filter,current_panel);
+ current_panel=n;
+ animate_panel();
+ }
+
+ if(current_panel==0){
+ gtk_widget_set_sensitive(upbutton,FALSE);
+ }else{
+ gtk_widget_set_sensitive(upbutton,TRUE);
+ }
+ if(current_panel+1==num_panels){
+ gtk_widget_set_sensitive(downbutton,FALSE);
+ }else{
+ gtk_widget_set_sensitive(downbutton,TRUE);
+ }
+}
+
+static void upbutton_clicked(GtkWidget *widget, gpointer data){
+ if(current_panel>0)
+ set_current_panel(current_panel-1);
+}
+
+static void downbutton_clicked(GtkWidget *widget, gpointer data){
+ if(current_panel+1<num_panels)
+ set_current_panel(current_panel+1);
+}
+
+static void make_panel(void){
+ int w=PANEL_WIDTH;
+ int h=PANEL_HEIGHT;
+
+ GtkWidget *toplevel=NULL;
+ GtkWidget *topbox=NULL;
+ GtkWidget *rightbox=NULL;
+ GtkWidget *leftbox=NULL;
+ GtkWidget *panels[num_panels];
+
+ int i;
+
+ toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_app_paintable(toplevel, TRUE);
+ g_signal_connect(G_OBJECT(toplevel), "screen-changed", G_CALLBACK(screen_changed), NULL);
+ g_signal_connect(G_OBJECT(toplevel), "expose-event", G_CALLBACK(expose_toplevel), NULL);
+ g_signal_connect(G_OBJECT(toplevel), "enter-notify-event", G_CALLBACK(hide_mouse), NULL);
+
+ /* toplevel is a fixed size, meant to be nailed to the screen in one
+ spot */
+ gtk_widget_set_size_request(GTK_WIDGET(toplevel),w,h);
+ gtk_window_set_resizable(GTK_WINDOW(toplevel),FALSE);
+ gtk_window_set_decorated(GTK_WINDOW(toplevel),FALSE);
+ gtk_window_set_gravity(GTK_WINDOW(toplevel),GDK_GRAVITY_SOUTH_WEST);
+ gtk_window_move (GTK_WINDOW(toplevel),0,gdk_screen_height()-1);
+
+ /* multiple sliding panes within */
+ topbox = gtk_hbox_new(0,0);
+ rightbox = gtk_vbox_new(1,0);
+ leftbox = gtk_hbox_new(1,0);
+ panel_scrollfix = gtk_fixed_new();
+ panel_lefttable = gtk_table_new(num_panels,1,1);
+ upbutton = gtk_button_new();
+ downbutton = gtk_button_new();
+
+ gtk_widget_set_size_request(GTK_WIDGET(upbutton),60,25);
+ gtk_widget_set_size_request(GTK_WIDGET(downbutton),60,25);
+
+ gtk_container_add(GTK_CONTAINER(toplevel),topbox);
+ gtk_container_set_border_width(GTK_CONTAINER(topbox),2);
+ gtk_box_pack_start(GTK_BOX(topbox),leftbox,1,1,0);
+ gtk_box_pack_end(GTK_BOX(topbox),rightbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(leftbox),panel_scrollfix,1,1,0);
+ gtk_box_pack_start(GTK_BOX(rightbox),upbutton,0,1,3);
+ gtk_box_pack_end(GTK_BOX(rightbox),downbutton,0,1,3);
+
+ g_signal_connect(G_OBJECT(upbutton), "expose-event", G_CALLBACK(expose_upbutton), NULL);
+ g_signal_connect(G_OBJECT(downbutton), "expose-event", G_CALLBACK(expose_downbutton), NULL);
+ g_signal_connect(G_OBJECT(upbutton), "pressed", G_CALLBACK(upbutton_clicked), NULL);
+ g_signal_connect(G_OBJECT(downbutton), "pressed", G_CALLBACK(downbutton_clicked), NULL);
+
+ filter_make_critical(.05,&display_filter);
+ gtk_widget_set_size_request(panel_lefttable,w-4,-1);
+ gtk_fixed_put(GTK_FIXED(panel_scrollfix),panel_lefttable,0,0);
+
+
+ /* build the sliding frame table */
+ int boxborder=10;
+ for(i=0;i<num_panels;i++){
+ GtkWidget *heightbox = gtk_hbox_new(0,0);
+ GtkWidget *heightforce = gtk_vbox_new(1,0);
+ GtkWidget *panelframe = gtk_frame_new("foo label");
+ GtkWidget *buttonbox = gtk_hbox_new(0,0);
+
+ gtk_table_attach(GTK_TABLE(panel_lefttable),heightbox,0,1,i,i+1,
+ GTK_EXPAND|GTK_FILL,0,0,0);
+ gtk_box_pack_start(GTK_BOX(heightbox),heightforce,0,0,0);
+ gtk_widget_set_size_request(heightforce,1,h-4);
+
+ gtk_widget_set_name(panelframe,"topframe");
+ gtk_frame_set_label_align(GTK_FRAME(panelframe),.5,.5);
+ gtk_container_set_border_width(GTK_CONTAINER(panelframe),boxborder);
+ gtk_frame_set_shadow_type(GTK_FRAME(panelframe),GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(heightbox),panelframe,1,1,0);
+ gtk_container_add(GTK_CONTAINER(panelframe),buttonbox);
+ gtk_container_set_border_width(GTK_CONTAINER(buttonbox),boxborder);
+
+ switch(i){
+ default:
+ {
+ rowwidget *t = rowtoggle_new(GTK_BOX(buttonbox), NULL);
+ rowwidget_add_label(t,"foo",0);
+ }
+ break;
+ case 1:
+ {
+ rowwidget *t = rowlabel_new(GTK_BOX(buttonbox));
+ rowwidget_add_label(t,"foo",0);
+ }
+ break;
+ case 2:
+ {
+ rowwidget *t = rowlabel_new(GTK_BOX(buttonbox));
+ rowwidget_add_label(t,"foo",0);
+ rowlabel_light(t,1);
+ }
+ break;
+ }
+ }
+
+
+ screen_changed(toplevel, NULL, NULL);
+ set_current_panel(0);
+
+ gtk_widget_show_all(toplevel);
+}
+
+int main(int argc, char **argv){
+ gtk_init (&argc, &argv);
+
+ gtk_rc_parse_string
+ ("style \"panel\" {"
+ " fg[NORMAL]=\"#ffffff\""
+ "}"
+ "style \"topframe\" {"
+ " font_name = \"sans 10 bold\""
+ " fg[NORMAL]=\"#cccccc\""
+ "}"
+ "class \"*\" style \"panel\""
+ "widget \"*.topframe.GtkLabel\" style \"topframe\""
+ );
+
+ make_panel();
+ gtk_main();
+
+ return 0;
+}
Added: trunk/Xiph-episode-II/bounce/gtk-bounce.c.old
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce.c.old (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce.c.old 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,393 @@
+#include <math.h>
+#include <gtk/gtk.h>
+
+int current_panel=0;
+
+gboolean supports_alpha = FALSE;
+static void screen_changed(GtkWidget *widget, GdkScreen *old_screen, gpointer userdata)
+{
+ /* To check if the display supports alpha channels, get the colormap */
+ GdkScreen *screen = gtk_widget_get_screen(widget);
+ GdkColormap *colormap = gdk_screen_get_rgba_colormap(screen);
+
+ if (!colormap){
+ printf("Your screen does not support alpha channels!\n");
+ colormap = gdk_screen_get_rgb_colormap(screen);
+ supports_alpha = FALSE;
+ }else{
+ supports_alpha = TRUE;
+ }
+
+ gtk_widget_set_colormap(widget, colormap);
+}
+
+static gboolean expose_toplevel(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+
+ /* clear background to transparent */
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_set_source_rgba (cr, 0, 0, 0, 0); /* transparent */
+ cairo_paint (cr);
+
+ /* rounded rectangle path */
+ double
+ x = 1, /* parameters like cairo_rectangle */
+ y = 1,
+ width = w-2,
+ height = h-2,
+ radius = 8.0;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, 0);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, 0, 90*degrees);
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, 180*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, 180*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent dark background */
+ cairo_set_source_rgba (cr, 0, 0, 0, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,2.0);
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return FALSE;
+}
+
+void expose_a_widget(gpointer a, gpointer b){
+ GtkWidget *widget = (GtkWidget *)a;
+ GdkEventExpose *event = (GdkEventExpose *)b;
+ GTK_WIDGET_CLASS(GTK_WIDGET_GET_CLASS(widget))->
+ expose_event (widget, event);
+}
+
+static gboolean expose_toggle_button(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+ int lit = userdata ? *(int *)userdata : 0;
+ /* rounded rectangle path */
+ double
+ x = widget->allocation.x,
+ y = widget->allocation.y,
+ width = w-2,
+ height = h-2,
+ radius = 4.0;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x+width-radius, y+radius, radius, -90*degrees, 0);
+ cairo_arc (cr, x+width-radius, y+height-radius, radius, 0, 90*degrees);
+ cairo_arc (cr, x+radius, y+height-radius, radius, 90*degrees, 180*degrees);
+ cairo_arc (cr, x+radius, y+radius, radius, 180*degrees, 270*degrees);
+ cairo_close_path (cr);
+
+ /* fill background */
+ switch(state){
+ case GTK_STATE_NORMAL:
+ cairo_set_source_rgba (cr, .1, .1, .1, 1);
+ break;
+ case GTK_STATE_ACTIVE:
+ cairo_set_source_rgba (cr, .1, .3, .6, 1);
+ break;
+ case GTK_STATE_SELECTED:
+ cairo_set_source_rgba (cr, .2, .4, .7, 1);
+ break;
+ case GTK_STATE_PRELIGHT:
+ if(gtk_toggle_button_get_active)
+ cairo_set_source_rgba (cr, .1, .3, .6, 1);
+ else
+ cairo_set_source_rgba (cr, .1, .1, .1, 1);
+ break;
+ case GTK_STATE_INSENSITIVE:
+ if(lit)
+ cairo_set_source_rgba (cr, .1, .3, .6, .5);
+ else
+ cairo_set_source_rgba (cr, 0, 0, 0, .5);
+ break;
+ }
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ if(state != GTK_STATE_INSENSITIVE){
+ cairo_set_line_width(cr,2.0);
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+ }
+ cairo_destroy(cr);
+
+ /* have to do this by hand as returning TRUE below short-circuits
+ the whole expose chain */
+ GList *children=gtk_container_get_children(GTK_CONTAINER(widget));
+ g_list_foreach(children,expose_a_widget,event);
+
+ return TRUE;
+}
+
+static gboolean expose_upbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+radius;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, 225*degrees, 315*degrees);
+ cairo_arc (cr, x+scale, y+scale, radius, -45*degrees, 135*degrees);
+ cairo_line_to (cr, x, y+sqrt(2)*radius);
+ cairo_arc (cr, x-scale, y+scale, radius, 45*degrees, 225*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ if(state == GTK_STATE_PRELIGHT)
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static gboolean expose_downbutton(GtkWidget *widget,
+ GdkEventExpose *event,
+ gpointer userdata){
+ cairo_t *cr = gdk_cairo_create(widget->window);
+ int w=widget->allocation.width;
+ int h=widget->allocation.height;
+ GtkStateType state = gtk_widget_get_state(widget);
+
+ if(state == GTK_STATE_INSENSITIVE) return TRUE;
+
+ /* rounded arrow path */
+ double
+ radius = 4.0,
+ scale = (w/2>h?h:w/2)-radius*2,
+ x = widget->allocation.x+w/2, /* parameters like cairo_rectangle */
+ y = widget->allocation.y+h-radius-1;
+ double degrees = M_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr, x, y, radius, -315*degrees, -225*degrees);
+ cairo_arc (cr, x-scale, y-scale, radius, -225*degrees, -45*degrees);
+ cairo_line_to (cr, x, y-sqrt(2)*radius);
+ cairo_arc (cr, x+scale, y-scale, radius, -135*degrees, 45*degrees);
+ cairo_close_path (cr);
+
+ /* fill translucent light background */
+ if(state == GTK_STATE_PRELIGHT)
+ cairo_set_source_rgba (cr, 1, 1, 1, .5);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, .5);
+ cairo_fill_preserve (cr);
+
+ /* stroke border */
+ cairo_set_line_width(cr,1.0);
+ if(state == GTK_STATE_ACTIVE)
+ cairo_set_source_rgba (cr, .5, .6, .83, 1);
+ else
+ cairo_set_source_rgba (cr, .8, .8, .8, 1);
+ cairo_stroke(cr);
+
+ cairo_destroy(cr);
+
+ return TRUE;
+}
+
+static void make_panel(void){
+ int w=1024;
+ int h=150;
+ int rows = 4;
+
+ GtkWidget *toplevel=NULL;
+ GtkWidget *topbox=NULL;
+ GtkWidget *rightbox=NULL;
+ GtkWidget *leftbox=NULL;
+ GtkWidget *scrollfix=NULL;
+ GtkWidget *lefttable=NULL;
+ GtkWidget *upbutton=NULL;
+ GtkWidget *downbutton=NULL;
+ GtkWidget *panels[rows];
+
+ int i;
+
+ toplevel = gtk_window_new (GTK_WINDOW_TOPLEVEL);
+ gtk_widget_set_app_paintable(toplevel, TRUE);
+ g_signal_connect(G_OBJECT(toplevel), "screen-changed", G_CALLBACK(screen_changed), NULL);
+ g_signal_connect(G_OBJECT(toplevel), "expose-event", G_CALLBACK(expose_toplevel), NULL);
+
+ /* toplevel is a fixed size, meant to be nailed to the screen in one
+ spot */
+ gtk_widget_set_size_request(GTK_WIDGET(toplevel),w,h);
+ gtk_window_set_resizable(GTK_WINDOW(toplevel),FALSE);
+ gtk_window_set_decorated(GTK_WINDOW(toplevel),FALSE);
+ gtk_window_set_gravity(GTK_WINDOW(toplevel),GDK_GRAVITY_SOUTH_WEST);
+ gtk_window_move (GTK_WINDOW(toplevel),0,gdk_screen_height()-1);
+
+ /* multiple sliding panes within */
+ topbox = gtk_hbox_new(0,0);
+ rightbox = gtk_vbox_new(0,0);
+ leftbox = gtk_hbox_new(1,0);
+ scrollfix = gtk_fixed_new();
+ lefttable = gtk_table_new(rows,1,1);
+ upbutton = gtk_button_new_with_label("^");
+ downbutton = gtk_button_new_with_label("v");
+
+ gtk_widget_set_size_request(GTK_WIDGET(upbutton),60,25);
+ gtk_widget_set_size_request(GTK_WIDGET(downbutton),60,25);
+
+ gtk_container_add(GTK_CONTAINER(toplevel),topbox);
+ gtk_container_set_border_width(GTK_CONTAINER(topbox),2);
+ gtk_box_pack_start(GTK_BOX(topbox),leftbox,1,1,0);
+ gtk_box_pack_end(GTK_BOX(topbox),rightbox,0,0,0);
+ gtk_box_pack_start(GTK_BOX(leftbox),scrollfix,1,1,0);
+ gtk_box_pack_start(GTK_BOX(rightbox),upbutton,0,0,0);
+ gtk_box_pack_end(GTK_BOX(rightbox),downbutton,0,0,0);
+
+ g_signal_connect(G_OBJECT(upbutton), "expose-event", G_CALLBACK(expose_upbutton), NULL);
+ g_signal_connect(G_OBJECT(downbutton), "expose-event", G_CALLBACK(expose_downbutton), NULL);
+
+ gtk_widget_set_size_request(lefttable,w-4,-1);
+ gtk_fixed_put(GTK_FIXED(scrollfix),lefttable,0,0);
+
+
+ /* build the sliding frame table */
+ int boxborder=10;
+ for(i=0;i<rows;i++){
+ GtkWidget *heightbox = gtk_hbox_new(0,0);
+ GtkWidget *heightforce = gtk_vbox_new(1,0);
+ GtkWidget *panelframe = gtk_frame_new("foo label");
+ GtkWidget *buttonbox = gtk_hbox_new(0,0);
+
+ gtk_table_attach(GTK_TABLE(lefttable),heightbox,0,1,i,i+1,
+ GTK_EXPAND|GTK_FILL,0,0,0);
+ gtk_box_pack_start(GTK_BOX(heightbox),heightforce,0,0,0);
+ gtk_widget_set_size_request(heightforce,1,h-4);
+
+ gtk_widget_set_name(panelframe,"topframe");
+ gtk_frame_set_label_align(GTK_FRAME(panelframe),.5,.5);
+ gtk_container_set_border_width(GTK_CONTAINER(panelframe),boxborder);
+ gtk_frame_set_shadow_type(GTK_FRAME(panelframe),GTK_SHADOW_NONE);
+ gtk_box_pack_start(GTK_BOX(heightbox),panelframe,1,1,0);
+ gtk_container_add(GTK_CONTAINER(panelframe),buttonbox);
+ gtk_container_set_border_width(GTK_CONTAINER(buttonbox),boxborder);
+
+ GtkWidget *tmp=gtk_toggle_button_new_with_label("foo");
+ g_signal_connect(G_OBJECT(tmp), "expose-event", G_CALLBACK(expose_rectarea), NULL);
+ gtk_widget_set_size_request(tmp,h,-1);
+ gtk_box_pack_start(GTK_BOX(buttonbox),tmp,0,0,10);
+ }
+
+
+ screen_changed(toplevel, NULL, NULL);
+ gtk_widget_show_all(toplevel);
+}
+
+main(int argc, char **argv){
+ gtk_init (&argc, &argv);
+
+ gtk_rc_parse_string
+ ("style \"panel\" {"
+ " bg[NORMAL]=\"#000000\""
+ " bg[ACTIVE]=\"#000000\""
+ " bg[PRELIGHT]=\"#000000\""
+ " bg[SELECTED]=\"#000000\""
+ " bg[INSENSITIVE]=\"#000000\""
+
+ " fg[NORMAL]=\"#ffffff\""
+ " fg[ACTIVE]=\"#ffffff\""
+ " fg[PRELIGHT]=\"#ffffff\""
+ " fg[SELECTED]=\"#000000\""
+ " fg[INSENSITIVE]=\"#000000\""
+
+ " base[NORMAL]=\"#000000\""
+ " base[ACTIVE]=\"#000000\""
+ " base[PRELIGHT]=\"#000000\""
+ " base[SELECTED]=\"#000000\""
+ " base[INSENSITIVE]=\"#000000\""
+
+ " text[NORMAL]=\"#ffffff\""
+ " text[ACTIVE]=\"#ffffff\""
+ " text[PRELIGHT]=\"#ffffff\""
+ " text[SELECTED]=\"#000000\""
+ " text[INSENSITIVE]=\"#000000\""
+ "}"
+
+ "style \"topframe\" {"
+ " font_name = \"sans 10 bold\""
+ " fg[NORMAL]=\"#cccccc\""
+ "}"
+
+ "style \"button\" {"
+ " font_name = \"sans 8\""
+ " GtkButton::focus-padding = 0"
+ " GtkButton::focus-line-width = 0"
+ " GtkButton::interior-focus = 0"
+ " bg[NORMAL]=\"#406090\""
+ " bg[ACTIVE]=\"#6080a0\""
+ " bg[PRELIGHT]=\"#6080a0\""
+ " bg[SELECTED]=\"#d0e6ff\""
+ " bg[INSENSITIVE]=\"#b0c0d8\""
+
+ " fg[NORMAL]=\"#ffffff\""
+ " fg[ACTIVE]=\"#ffffff\""
+ " fg[PRELIGHT]=\"#ffffff\""
+ " fg[SELECTED]=\"#000000\""
+ " fg[INSENSITIVE]=\"#000000\""
+
+ " base[NORMAL]=\"#000000\""
+ " base[ACTIVE]=\"#000000\""
+ " base[PRELIGHT]=\"#000000\""
+ " base[SELECTED]=\"#000000\""
+ " base[INSENSITIVE]=\"#000000\""
+
+ " text[NORMAL]=\"#ffffff\""
+ " text[ACTIVE]=\"#ffffff\""
+ " text[PRELIGHT]=\"#ffffff\""
+ " text[SELECTED]=\"#000000\""
+ " text[INSENSITIVE]=\"#000000\""
+ "}"
+
+ "class \"*\" style \"panel\""
+ "widget \"*.topframe\" style \"topframe\""
+ "widget \"*.topframe.GtkLabel\" style \"topframe\""
+ "class \"GtkButton\" style \"button\""
+ );
+
+ make_panel();
+ gtk_main();
+
+
+}
Added: trunk/Xiph-episode-II/bounce/gtk-bounce.h
===================================================================
--- trunk/Xiph-episode-II/bounce/gtk-bounce.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/gtk-bounce.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,52 @@
+#define _GNU_SOURCE
+#define _LARGEFILE_SOURCE
+#define _LARGEFILE64_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#ifndef _REENTRANT
+# define _REENTRANT
+#endif
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <gtk/gtk.h>
+
+extern sig_atomic_t exiting;
+extern int eventpipe[2];
+
+extern void *io_thread(void *dummy);
+
+extern sig_atomic_t request_bits;
+//extern sig_atomic_t request_ch;
+extern sig_atomic_t request_rate;
+
+extern sig_atomic_t request_ch1_quant;
+extern sig_atomic_t request_ch2_quant;
+extern sig_atomic_t prime_1kHz_notch;
+extern sig_atomic_t request_1kHz_notch;
+extern sig_atomic_t request_1kHz_sine;
+extern sig_atomic_t request_5kHz_sine;
+extern sig_atomic_t request_1kHz_sine2;
+extern sig_atomic_t request_1kHz_amplitude;
+extern sig_atomic_t request_1kHz_modulate;
+extern sig_atomic_t request_1kHz_notch;
+extern sig_atomic_t request_dither;
+extern sig_atomic_t request_dither_shaped;
+extern sig_atomic_t request_dither_amplitude;
+extern sig_atomic_t request_output_noise;
+extern sig_atomic_t request_output_sweep;
+extern sig_atomic_t request_output_logsweep;
+extern sig_atomic_t request_output_silence;
+
+extern sig_atomic_t request_lp_filter;
+extern sig_atomic_t request_1kHz_square;
+extern sig_atomic_t request_1kHz_square_offset;
+extern sig_atomic_t request_1kHz_square_spread;
+extern sig_atomic_t request_output_tone;
+extern sig_atomic_t request_output_duallisten;
+
+#define fromdB(x) (exp((x)*.11512925f))
Added: trunk/Xiph-episode-II/bounce/twopole.c
===================================================================
--- trunk/Xiph-episode-II/bounce/twopole.c (rev 0)
+++ trunk/Xiph-episode-II/bounce/twopole.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,80 @@
+/* specific filter recipes with thanks to Alex Matulich
+ http://unicorn.us.com/alex/2polefilters.html */
+
+#include <math.h>
+#include "twopole.h"
+
+void filter_make_bessel2(double w, int passes, pole2 *f){
+ double c = pow(pow(pow(2,(1./passes))-.75,-.5)-.5,-.5)/sqrt(3);
+ double wc = c*w;
+ double w0 = tan(M_PI*wc);
+ double g = 3;
+ double p = 3;
+ double K1 = p*w0;
+ double K2 = g*w0*w0;
+ double A0 = K2/(1+K1+K2);
+ double A1 = 2*A0;
+ double A2 = A0;
+ double B1 = 2*A0*(1/K2-1);
+ double B2 = 1-(A0+A1+A2+B1);
+
+ f->a = A0;
+ f->b1 = B1;
+ f->b2 = B2;
+ filter_set(f,0);
+}
+
+void filter_make_critical(double w, int passes, pole2 *f){
+ double c = pow(pow(2,(1./2*passes))-1,-.5);
+ double wc = c*w;
+ double w0 = tan(M_PI*wc);
+ double g = 1;
+ double p = 2;
+ double K1 = p*w0;
+ double K2 = g*w0*w0;
+ double A0 = K2/(1+K1+K2);
+ double A1 = 2*A0;
+ double A2 = A0;
+ double B1 = 2*A0*(1/K2-1);
+ double B2 = 1-(A0+A1+A2+B1);
+
+ f->a = A0;
+ f->b1 = B1;
+ f->b2 = B2;
+ filter_set(f,0);
+}
+
+void filter_make_butterworth(double w, int passes, pole2 *f){
+ double c = pow(pow(2,(1./passes))-1,-.25);
+ double wc = c*w;
+ double w0 = tan(M_PI*wc);
+ double g = 1;
+ double p = sqrt(2);
+ double K1 = p*w0;
+ double K2 = g*w0*w0;
+ double A0 = K2/(1+K1+K2);
+ double A1 = 2*A0;
+ double A2 = A0;
+ double B1 = 2*A0*(1/K2-1);
+ double B2 = 1-(A0+A1+A2+B1);
+
+ f->a = A0;
+ f->b1 = B1;
+ f->b2 = B2;
+ filter_set(f,0);
+}
+
+void filter_set(pole2 *p, double val){
+ p->x[0]=p->x[1]=val;
+ p->y[0]=p->y[1]=val;
+}
+
+
+double filter_filter(double x, pole2 *p){
+ double a = p->a;
+ double y = a*x + 2*a*p->x[0] + a*p->x[1] + p->b1*p->y[0] + p->b2*p->y[1];
+ p->y[1] = p->y[0]; p->y[0] = y;
+ p->x[1] = p->x[0]; p->x[0] = x;
+
+ return y;
+}
Added: trunk/Xiph-episode-II/bounce/twopole.h
===================================================================
--- trunk/Xiph-episode-II/bounce/twopole.h (rev 0)
+++ trunk/Xiph-episode-II/bounce/twopole.h 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,21 @@
+/* specific filter recipes with thanks to Alex Matulich
+ http://unicorn.us.com/alex/2polefilters.html */
+
+#ifndef _TWOPOLE_H_
+#define _TWOPOLE_H_
+
+typedef struct {
+ double a;
+ double b1;
+ double b2;
+ double x[2];
+ double y[2];
+} pole2;
+
+extern void filter_set(pole2 *p, double val);
+extern void filter_make_bessel2(double w, int passes, pole2 *f);
+extern void filter_make_critical(double w, int passes, pole2 *f);
+extern void filter_make_butterworth(double w, int passes, pole2 *f);
+extern double filter_filter(double x, pole2 *p);
+
+#endif
Added: trunk/Xiph-episode-II/cairo/README
===================================================================
--- trunk/Xiph-episode-II/cairo/README (rev 0)
+++ trunk/Xiph-episode-II/cairo/README 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,14 @@
+These C files are quick little Cairo applications used to generate
+images for the various animations in the Xiph Episode II video
+"Digital Show & Tell". Each one generates a series of PNG images that
+are then stitched togetehr into an animation using a utility like
+ffmpeg or mplayer, or an editor like Cinelerra.
+
+Compile each with the commandline:
+
+gcc inname.c -o outname -lm -lcairo -lfftw3f
+
+ch4-samplequant.c also requires the 'squishyio' library from Xiph.org
+SVN and the commandline:
+
+gcc ch4-samplequant.c -o ch4-samplequant -lm -lcairo -lfftw3f -lsquishyio
\ No newline at end of file
Added: trunk/Xiph-episode-II/cairo/ch3_convenient.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch3_convenient.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch3_convenient.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,192 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 300
+
+static int pstep=64;
+static int xstart=317;
+static int xbound=350;
+static int xend=1408;
+static int ystart=179;
+static int ybound=186;
+static int yend=910;
+
+#define OUT 48
+#define HOLD 72
+#define CIRCLE 24
+#define CIRCLEHOLD 72
+#define IN 18
+#define POST 48
+
+cairo_surface_t *read_frame(char *filename){
+ cairo_surface_t *ps=cairo_image_surface_create_from_png(filename);
+ cairo_status_t status = cairo_surface_status(ps);
+ if(!ps || status!=CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"CAIRO ERROR: Unable to load PNG file %s: %s\n\n",filename,cairo_status_to_string(status));
+ exit(1);
+ }
+ return ps;
+}
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch3_zero-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void copy_surface(cairo_surface_t *s,cairo_surface_t *d){
+ cairo_t *c = cairo_create(d);
+ cairo_set_source_surface(c,s,0,0);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_squares(cairo_surface_t *cs, float delta){
+ cairo_t *c = cairo_create(cs);
+ double max = pstep*.5-1;
+ double this = max*delta;
+ int x,y;
+
+ cairo_set_line_width(c,this);
+ cairo_set_source_rgb(c,0,0,0);
+
+ for(x=xbound-pstep;x<xend;x+=pstep){
+ int x1=(x<xstart?xstart:x);
+ int x2=(x+pstep>xend?xend:x+pstep);
+ for(y=ybound-pstep;y<yend;y+=pstep){
+ int y1=(y<ystart?ystart:y);
+ int y2=(y+pstep>yend?yend:y+pstep);
+
+ cairo_save(c);
+
+ /* set up clip area */
+ cairo_rectangle(c,x1,y1,x2-x1,y2-y1);
+ cairo_clip(c);
+
+ /* draw */
+ cairo_rectangle(c, x+this/2., y+this/2. ,pstep-this,pstep-this);
+ cairo_stroke(c);
+
+ cairo_restore(c);
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_circles(cairo_surface_t *cs, float delta){
+ cairo_t *c = cairo_create(cs);
+ int x,y;
+
+ cairo_set_line_width(c,3);
+ cairo_set_source_rgba(c,.6,.2,.2,delta);
+
+ for(x=xbound-pstep;x<xend;x+=pstep){
+ int x1=(x<xstart?xstart:x);
+ int x2=(x+pstep>xend?xend:x+pstep);
+ for(y=ybound-pstep;y<yend;y+=pstep){
+ int y1=(y<ystart?ystart:y);
+ int y2=(y+pstep>yend?yend:y+pstep);
+
+ cairo_save(c);
+
+ /* set up clip area */
+ cairo_rectangle(c,x1,y1,x2-x1,y2-y1);
+ cairo_clip(c);
+
+ /* draw */
+ cairo_arc(c,x+pstep/2.,y+pstep/2.,15,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_restore(c);
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ W,H);
+ cairo_surface_t *ps = read_frame("../framegrabs/convenient-squares.png");
+ int i,count=0;
+
+ /* shrink squares */
+ for(i=0;i<OUT;i++){
+ copy_surface(ps,cs);
+ draw_squares(cs,(double)i/OUT);
+ write_frame(cs,count++);
+ }
+
+ /* hold */
+ copy_surface(ps,cs);
+ draw_squares(cs,1.);
+ for(i=0;i<HOLD;i++)
+ write_frame(cs,count++);
+
+ /* fade in circles */
+ for(i=0;i<CIRCLE;i++){
+ copy_surface(ps,cs);
+ draw_squares(cs,1.);
+ draw_circles(cs,(double)i/CIRCLE);
+ write_frame(cs,count++);
+ }
+
+ /* hold */
+ copy_surface(ps,cs);
+ draw_squares(cs,1.);
+ draw_circles(cs,1.);
+ for(i=0;i<HOLD;i++)
+ write_frame(cs,count++);
+
+ /* fade out circles */
+ for(i=0;i<CIRCLE;i++){
+ copy_surface(ps,cs);
+ draw_squares(cs,1.);
+ draw_circles(cs,1.-(double)i/CIRCLE);
+ write_frame(cs,count++);
+ }
+
+ /* hold */
+ copy_surface(ps,cs);
+ draw_squares(cs,1.);
+ for(i=0;i<HOLD;i++)
+ write_frame(cs,count++);
+
+ /* grow squares */
+ for(i=0;i<IN;i++){
+ copy_surface(ps,cs);
+ draw_squares(cs,1-(double)i/IN);
+ write_frame(cs,count++);
+ }
+
+ /* post */
+ copy_surface(ps,cs);
+ for(i=0;i<POST;i++)
+ write_frame(cs,count++);
+
+
+ cairo_surface_destroy(cs);
+ cairo_surface_destroy(ps);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch3_minutely.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch3_minutely.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch3_minutely.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,310 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 300
+
+#define TEXTY 90
+#define TEXTY2 75
+#define TEXT_FONT_SIZE 60
+
+#define SAMPLES 18
+#define CYCLES 1
+
+#define PRE 108
+#define DRAW 72
+#define DRAWHOLD 2
+#define CIRCLE 6
+#define CIRCLEHOLD 12
+
+
+#define POST 240
+
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+#define WLINE_WIDTH 15
+#define WCOLOR 1,.2,.2,.8
+#define DCOLOR .2,.2,1,1
+#define CCOLOR .8,0,0,1
+
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch3_minute-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+double get_y(double x,double f){
+ return CR + WH*sin((double)x/W*CYCLES*f*2*M_PI);
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return rint(x/SW);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint(sample*SW);
+}
+
+double get_dy(double x,double f){
+ double s = (double)x*SAMPLES/W;
+ double y = sin((double)x/W*CYCLES*f*2*M_PI);
+ if(s>10 && s<11){
+ double d = sin((s-10)*M_PI);
+ y += .1 * d*d;
+ }
+ return CR+WH*y;
+}
+
+void draw_axis(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double SW = 105;
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+
+ cairo_set_source_rgba(c,AXIS_COLOR*del);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=0;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+void draw_waveform(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw sine */
+ cairo_set_source_rgba(c,WCOLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WLINE_WIDTH);
+
+ for(i=-WLINE_WIDTH;i<(W*del)+WLINE_WIDTH;i++){
+ double y = get_y(i,f);
+ if(!i)
+ cairo_move_to(c,i,y);
+ else
+ cairo_line_to(c,i,y);
+ }
+ cairo_stroke(c);
+
+ cairo_destroy(c);
+}
+
+void draw_different(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw sine */
+ cairo_set_source_rgba(c,DCOLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WLINE_WIDTH);
+
+ for(i=-WLINE_WIDTH;i<(W*del)+WLINE_WIDTH;i++){
+ double y = get_dy(i,f);
+ if(!i)
+ cairo_move_to(c,i,y);
+ else
+ cairo_line_to(c,i,y);
+ }
+ cairo_stroke(c);
+
+ cairo_destroy(c);
+}
+
+void draw_circle(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw sine */
+ cairo_set_source_rgba(c,CCOLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WLINE_WIDTH/2);
+
+ cairo_translate(c,W*10.5/SAMPLES,CR-WH*.95);
+ cairo_scale(c,.8,1);
+ for(i=0;i<del*1000;i++)
+ cairo_arc_negative(c,0,0,WH*(.3+.05*(i/1000.)),-i/800.*2*M_PI- M_PI/3,-(i+1)/800.*2*M_PI-M_PI/3);
+ cairo_stroke(c);
+
+ cairo_destroy(c);
+}
+
+void draw_stems(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ cairo_set_source_rgba(c,COLOR*del*.8);
+
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx,f);
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,sx,CR);
+ cairo_line_to(c,sx,y);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_samples(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx,f);
+
+ cairo_set_source_rgba(c,COLOR*del*.4);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,sx,y,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,COLOR*del);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,sx,y);
+ cairo_line_to(c,sx,y+.001);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+
+ int i,count=0;
+ double m,base_f = 44100. / SAMPLES * CYCLES;
+ double f = 20000;
+ m = f/base_f;
+
+#if 0
+ /* transparent 20kHz without the waveform */
+
+ transparent_surface(cs);
+ draw_stems(cs,1.,m);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,m);
+ draw_waveform(cs,1.,m);
+ write_frame(cs,9998);
+
+ transparent_surface(cs);
+ draw_stems(cs,1.,m);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,m);
+ write_frame(cs,9999);
+
+#else
+
+ /* static preroll */
+ transparent_surface(cs);
+ //draw_stems(cs,1.,m);
+ //draw_axis(cs,1.);
+ //draw_samples(cs,1.,m);
+ //draw_waveform(cs,1.,m);
+ for(i=0;i<PRE;i++){
+ write_frame(cs,count++);
+ }
+
+ /* draw 'different' waveform */
+ int circlecount=0;
+ for(i=0;i<DRAW;i++){
+ double s = (float)i/DRAW*SAMPLES;
+ transparent_surface(cs);
+ //draw_stems(cs,1.,m);
+ //draw_axis(cs,1.);
+ //draw_samples(cs,1.,m);
+ //draw_waveform(cs,1.,m);
+ draw_different(cs,(i+.5)/DRAW,m);
+ if(s>=14){
+ draw_circle(cs,(circlecount+.5)/CIRCLE,m);
+ if(circlecount+1<CIRCLE)
+ circlecount++;
+ }
+ write_frame(cs,count++);
+ }
+
+ for(i=0;i<POST;i++){
+ write_frame(cs,count++);
+ }
+
+#endif
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch3_samples.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch3_samples.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch3_samples.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,526 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <pango/pangocairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 300
+
+#define TEXTY 140
+#define TEXTY2 140
+#define TEXT_FONT_SIZE 90
+
+#define SAMPLES 18
+#define CYCLES 1
+
+#define PRE 108
+#define FADE 12
+#define ARROW 76
+#define BRACKETS 72
+#define MID 160
+#define WAVEFADE 48
+#define WAVEFORM 48
+#define KHZ_UP 72
+#define KHZ_20 160
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+#define WLINE_WIDTH 15
+#define WCOLOR 1,.2,.2,.8
+
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch3_samples-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+double get_y(double x,double f){
+ return CR + WH*sin((double)x/W*CYCLES*f*2*M_PI);
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return rint(x/SW);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint(sample*SW);
+}
+
+void draw_axis(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double SW = 105;
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+
+ cairo_set_source_rgba(c,AXIS_COLOR*del);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=0;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+void draw_waveform(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw sine */
+ cairo_set_source_rgba(c,WCOLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WLINE_WIDTH);
+
+ for(i=-WLINE_WIDTH;i<(W*del)+WLINE_WIDTH;i++){
+ double y = get_y(i,f);
+ if(!i)
+ cairo_move_to(c,i,y);
+ else
+ cairo_line_to(c,i,y);
+ }
+ cairo_stroke(c);
+
+ cairo_destroy(c);
+}
+
+void draw_stems(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ cairo_set_source_rgba(c,COLOR*del*.8);
+
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx,f);
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,sx,CR);
+ cairo_line_to(c,sx,y);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_samples(cairo_surface_t *cs, double del, double f){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx,f);
+
+ cairo_set_source_rgba(c,COLOR*del*.4);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,sx,y,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,COLOR*del);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,sx,y);
+ cairo_line_to(c,sx,y+.001);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_arrows(cairo_surface_t *cs,double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_MITER);
+ cairo_set_source_rgba(c,ARROW_COLOR,del);
+
+ for(i=1;i<SAMPLES;i++){
+ int x = tox(i);
+ double y = get_y(x,1.);
+
+ //if(y>CR)y=CR;
+
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH+3);
+ cairo_set_source_rgba(c,1.,1.,1.,del);
+ cairo_move_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x,y-LINE_WIDTH*5-ARROW_LINE_WIDTH*10-1.5);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+
+
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH);
+ cairo_set_source_rgba(c,ARROW_COLOR,del);
+ cairo_move_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x,y-LINE_WIDTH*5-ARROW_LINE_WIDTH*10);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+
+ cairo_stroke(c);
+ }
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+
+ for(i=1;i<SAMPLES;i++){
+ int x = tox(i);
+ double y = get_y(x,1.);
+
+ cairo_move_to(c,x,y);
+ cairo_line_to(c,x,y+.001);
+ }
+ cairo_stroke(c);
+
+ cairo_destroy(c);
+}
+
+void draw_brackets(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double r = 10;
+ cairo_font_face_t *ff;
+ cairo_text_extents_t extents;
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_MITER);
+ cairo_set_line_width(c,ARROW_LINE_WIDTH);
+ cairo_set_source_rgba(c,ARROW_COLOR,del);
+
+ for(i=0;i<SAMPLES;i++){
+ double x0 = tox(i)+LINE_WIDTH;
+ double x1 = tox(i+1)-LINE_WIDTH;
+ double y = CR+WH+ARROW_LINE_WIDTH*10;
+
+ cairo_move_to(c,x0,y);
+ cairo_line_to(c,x1,y);
+
+ cairo_move_to(c,x0+ARROW_LINE_WIDTH*4,y-ARROW_LINE_WIDTH*2.5);
+ cairo_line_to(c,x0,y);
+ cairo_line_to(c,x0+ARROW_LINE_WIDTH*4,y+ARROW_LINE_WIDTH*2.5);
+
+ cairo_move_to(c,x1-ARROW_LINE_WIDTH*4,y-ARROW_LINE_WIDTH*2.5);
+ cairo_line_to(c,x1,y);
+ cairo_line_to(c,x1-ARROW_LINE_WIDTH*4,y+ARROW_LINE_WIDTH*2.5);
+ cairo_stroke(c);
+ }
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ if(!ff){
+ fprintf(stderr,"Unable to create cursor font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, TEXT_FONT_SIZE);
+ cairo_text_extents(c, "undefined", &extents);
+ double TH = (TEXTY-extents.height)*2 + extents.height;
+ cairo_move_to(c, (W-extents.x_advance)/2, H-TH+TEXTY2+ARROW_LINE_WIDTH*5);
+
+ cairo_show_text(c, "unde\xEF\xAC\x81ned");
+
+ cairo_destroy(c);
+}
+
+void draw_regions(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ cairo_set_line_width(c,2);
+
+ for(i=0;i<SAMPLES;i++){
+ double x0 = tox(i)+LINE_WIDTH/2;
+ double x1 = tox(i+1)-LINE_WIDTH/2;
+ double y0 = CR-WH-ARROW_LINE_WIDTH*15;
+ double y1 = CR+WH+ARROW_LINE_WIDTH*15;
+
+ cairo_set_source_rgba(c,ARROW_COLOR,del*.1);
+ cairo_move_to(c,x0,y0);
+ cairo_line_to(c,x0,y1);
+ cairo_line_to(c,x1,y1);
+ cairo_line_to(c,x1,y0);
+ cairo_close_path(c);
+ cairo_fill(c);
+
+ cairo_set_source_rgba(c,ARROW_COLOR,del);
+ cairo_move_to(c,x0,y0);
+ cairo_line_to(c,x0,y1);
+ cairo_move_to(c,x1,y0);
+ cairo_line_to(c,x1,y1);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+
+}
+
+void draw_readout(cairo_surface_t *cs, double alpha, double f){
+ char buffer[80];
+
+ cairo_t *c = cairo_create(cs);
+ cairo_font_face_t *ff;
+ cairo_font_face_t *ft;
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extents2;
+ cairo_text_extents_t extents3;
+
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ ft = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create cursor font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ft);
+ cairo_set_font_size(c, TEXT_FONT_SIZE);
+
+ cairo_text_extents(c, "frequency: ", &extents);
+ cairo_text_extents(c, "kHz", &extents3);
+
+ cairo_set_font_face(c,ff);
+ cairo_text_extents(c, "10", &extents2);
+
+ double TH = (TEXTY-extents2.height)*2 + extents2.height;
+
+ cairo_set_line_width(c,2);
+
+ cairo_set_font_face(c,ft);
+ cairo_move_to(c, (W-extents.x_advance-extents2.x_advance-extents3.x_advance)/2, TEXTY);
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_show_text(c, "frequency: ");
+
+ cairo_set_font_face(c,ff);
+ snprintf(buffer, 80,"%d",(int)(f+.001));
+ cairo_set_source_rgba(c,.8,0,0,alpha);
+ cairo_show_text(c, buffer);
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_set_font_face(c,ft);
+ cairo_show_text(c, "kHz");
+
+ cairo_text_extents(c, "sampling rate: 44.1kHz", &extents);
+
+ cairo_move_to(c, (W-extents.x_advance)/2, H-TH+TEXTY2);
+ cairo_show_text(c, "sampling rate: 44.1kHz");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+
+ int i,count=0;
+
+ /* static preroll */
+ for(i=0;i<PRE;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade in arrows */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ draw_arrows(cs,(double)i/FADE);
+ write_frame(cs,count++);
+ }
+
+ /* hold arrows */
+ for(i=0;i<ARROW;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ draw_arrows(cs,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade out arrows, fade in brackets */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_brackets(cs,(double)i/FADE);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ draw_arrows(cs,1-(double)i/FADE);
+ write_frame(cs,count++);
+ }
+
+ /* fade in regions */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_regions(cs,(double)i/FADE);
+ draw_brackets(cs,1.);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* hold */
+ for(i=0;i<BRACKETS;i++){
+ clear_surface(cs);
+ draw_regions(cs,1.);
+ draw_brackets(cs,1.);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade out regions, brackets */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_regions(cs,1.-(double)i/FADE);
+ draw_brackets(cs,1.-(double)i/FADE);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* mid */
+ for(i=0;i<MID;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade in waveform */
+ for(i=0;i<WAVEFADE;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ draw_waveform(cs,(double)i/WAVEFADE,1.);
+ write_frame(cs,count++);
+ }
+
+ /* hold waveform */
+ for(i=0;i<WAVEFORM;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,1.);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.,1.);
+ draw_waveform(cs,1.,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade in and advance to 20kHz */
+ for(i=0;i<KHZ_UP;i++){
+ double alpha = (double)i/FADE;
+ double m,base_f = 44100. / SAMPLES * CYCLES;
+ double f = sin((i+.5)/KHZ_UP*M_PI/2.);
+ f = sin(f*M_PI/2.);
+ f = base_f + (20000.-base_f)*f*f;
+ m = f/base_f;
+ if(alpha>1.)alpha=1.;
+
+ clear_surface(cs);
+ draw_stems(cs,1,m);
+ draw_axis(cs,1.0);
+ draw_readout(cs,alpha,rint(f/1000.));
+ draw_samples(cs,1,m);
+ draw_waveform(cs,1.0,m);
+ write_frame(cs,count++);
+ }
+
+ /* hold 20kHz */
+ for(i=0;i<KHZ_20;i++){
+ double m,base_f = 44100. / SAMPLES * CYCLES;
+ double f = 20000;
+ m = f/base_f;
+
+ clear_surface(cs);
+ draw_stems(cs,1.,m);
+ draw_axis(cs,1.);
+ draw_readout(cs,1.,rint(f/1000.));
+ draw_samples(cs,1.,m);
+ draw_waveform(cs,1.,m);
+ write_frame(cs,count++);
+ }
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch3_stairsteps.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch3_stairsteps.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch3_stairsteps.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,378 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 300
+
+#define TEXTY 140
+#define TEXT_FONT_SIZE 90
+
+#define SAMPLES 18
+#define CYCLES 1
+
+#define PRE 96
+#define FRAMES 128
+#define POST 48
+#define FADE 24
+#define FADEMARGIN 2
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define FIRST_LINE_WIDTH 15
+#define FIRST_COLOR .6,.6,.6,1
+
+#define SECOND_LINE_WIDTH 15
+#define SECOND_COLOR .8,.4,.4,1
+#define HALO_TAIL 60
+#define HALO 15
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch3_stairstep-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+double get_y(double x){
+ return CR + rint(WH*sin((double)x/W*CYCLES*2*M_PI));
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint(sample*SW);
+}
+
+void draw_axis(cairo_surface_t *cs){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double SW = 105;
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=0;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+double sadj(double x){
+ if(x<.2)
+ return sin(x*2.5*M_PI);
+ if(x>.8)
+ return sin((1-x)*2.5*M_PI);
+ return 1.;
+}
+
+void draw_stairstep_trace(cairo_surface_t *cs,int frame){
+int i,p,flag=0;
+ double sx,y,y1;
+ cairo_t *c = cairo_create(cs);
+ double SW = (double)W/SAMPLES;
+
+ /* draw trace and halo in successive passes */
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ cairo_set_line_width(c,FIRST_LINE_WIDTH);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+
+ for(i=frame * W / FRAMES;i<W;i++){
+ if(i>=0){
+ sx = floor((double)i / SW) * SW;
+ y1 = get_y(sx);
+ if(!flag){
+ y=y1;
+ cairo_move_to(c,i,y);
+ flag=1;
+ }
+ cairo_line_to(c,i,y);
+ if(i>=W)break;
+ if(y!=y1){
+ cairo_stroke(c);
+ cairo_set_source_rgba(c,FIRST_COLOR*.5);
+ cairo_move_to(c,i,y);
+ cairo_line_to(c,i,y1);
+ cairo_stroke(c);
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ }
+ y=y1;
+ }
+ }
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ cairo_set_line_width(c,SECOND_LINE_WIDTH);
+
+ for(i=0;i<frame * W / FRAMES;i++){
+ sx = floor((double)i / SW) * SW;
+ y1 = get_y(sx);
+ if(!i){
+ y=y1;
+ cairo_move_to(c,i,y);
+ }
+ cairo_line_to(c,i,y);
+ if(i>=W)break;
+ if(y!=y1)
+ cairo_line_to(c,i,y1);
+ y=y1;
+ }
+ cairo_stroke(c);
+
+ /* halos */
+ cairo_save(c);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+
+ for(p=HALO_TAIL;p>=0;p--){
+
+ int start = (double)(frame-HALO_TAIL+p) * W / FRAMES;
+ int end = (double)frame * W / FRAMES;
+
+ for(i=start;i<=end;i++){
+ double y0 = get_y(floor((double)(i-1) / SW) * SW);
+ double y1 = get_y(floor((double)i / SW) * SW);
+ double fp = 1.-(end-i)/(double)(end-start);
+
+ if(p==0){
+ cairo_set_source_rgba(c,1,.2,.1,.25*fp*fp);
+ }else{
+ double dp = (HALO_TAIL-p+1.)/(HALO_TAIL+1);
+ cairo_set_source_rgba(c,1,.2,.1,.003*fp*fp+.01*dp*fp);
+ }
+
+ if(i>0){
+ if(p==0)
+ cairo_set_line_width(c,SECOND_LINE_WIDTH);
+ else
+ cairo_set_line_width(c,SECOND_LINE_WIDTH+2+HALO*
+ ((double)(p+3)/(HALO_TAIL+1))*
+ ((double)(p+3)/(HALO_TAIL+1)));
+
+
+ cairo_move_to(c,i-1,y0);
+ cairo_line_to(c,i,y0);
+ if(i<W && y0 != y1){
+ int j;
+ if(y0>y1){
+ int t = y0;
+ y0 = (int)y1;
+ y1 = t;
+ }
+ for(j=y0+1;j<y1;j++){
+ double adj = sadj((j-y0)/(y1-y0));
+ adj = 1.- .5*adj;
+
+ cairo_stroke(c);
+
+ if(p==0)
+ cairo_set_line_width(c,SECOND_LINE_WIDTH*adj);
+ else
+ cairo_set_line_width(c,(SECOND_LINE_WIDTH+2+HALO*
+ ((double)(p+3)/(HALO_TAIL+1))*
+ ((double)(p+3)/(HALO_TAIL+1)))*adj);
+
+ if(p==0){
+ cairo_set_source_rgba(c,1,.2,.1,.25*fp*fp*adj);
+ }else{
+ double dp = (HALO_TAIL-p+1.)/(HALO_TAIL+1);
+ cairo_set_source_rgba(c,1,.2,.1,(.003*fp*fp+.01*dp*fp)*adj);
+ }
+
+ cairo_move_to(c,i,j-1);
+ cairo_line_to(c,i,j);
+ }
+ }
+ cairo_stroke(c);
+ }
+
+ if(i>=W)break;
+ }
+ }
+ cairo_restore(c);
+
+ cairo_destroy(c);
+}
+
+void draw_cursor(cairo_surface_t *cs,int frame, double alpha){
+ char buffer[80];
+ double SW = (double)W/SAMPLES;
+ int x = (double)frame * W / FRAMES;
+ int y = get_y(floor((double)x / SW + .01) * SW);
+
+ cairo_t *c = cairo_create(cs);
+ cairo_font_face_t *ff,*ft;
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extents2;
+ cairo_text_extents_t extents3;
+ cairo_text_extents_t extents4;
+
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ ft = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create cursor font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ft);
+ cairo_set_font_size(c, TEXT_FONT_SIZE);
+ cairo_text_extents(c, "time: ", &extents);
+ cairo_text_extents(c, "value: ", &extents3);
+ cairo_set_font_face(c,ff);
+ cairo_text_extents(c, "00.00 ", &extents2);
+ cairo_text_extents(c, "-100", &extents4);
+
+ double TH = (TEXTY-extents.height)*2 + extents.height;
+
+ cairo_set_line_width(c,2);
+
+ cairo_set_source_rgba(c,.8,0,0,alpha);
+ cairo_move_to(c,x,TH);
+ cairo_line_to(c,x,H-TH);
+ cairo_stroke(c);
+
+#if 0
+ cairo_set_source_rgba(c,.9,.9,.9,alpha);
+ cairo_rectangle(c,0,0,W,TH);
+ cairo_fill_preserve(c);
+ cairo_set_source_rgba(c,.5,.5,.5,alpha);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,.9,.9,.9,alpha);
+ cairo_rectangle(c,0,H-TH,W,TH);
+ cairo_fill_preserve(c);
+ cairo_set_source_rgba(c,.5,.5,.5,alpha);
+ cairo_stroke(c);
+#endif
+
+ cairo_move_to(c, (W-extents.x_advance-extents2.x_advance-
+ extents3.x_advance-extents4.x_advance)/2, TEXTY);
+
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_set_font_face(c,ft);
+ cairo_show_text(c, "time: ");
+ snprintf(buffer, 80,"%2.2f ",
+ (double)frame/FRAMES*SAMPLES);
+ cairo_set_source_rgba(c,.8,0,0,alpha);
+ cairo_set_font_face(c,ff);
+ cairo_show_text(c, buffer);
+
+ cairo_move_to(c, (W+extents.x_advance+extents2.x_advance-
+ extents3.x_advance-extents4.x_advance)/2, TEXTY);
+
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_set_font_face(c,ft);
+ cairo_show_text(c, "value: ");
+ snprintf(buffer, 80,"%d",(int)rint((CR-(int)y)/2.4));
+ cairo_set_source_rgba(c,.8,0,0,alpha);
+ cairo_set_font_face(c,ff);
+ cairo_show_text(c, buffer);
+
+ cairo_font_face_destroy(ft);
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ W,H);
+
+ int i;
+
+ /* static preroll */
+ for(i=-PRE;i<FADEMARGIN;i++){
+ clear_surface(cs);
+ draw_axis(cs);
+ draw_stairstep_trace(cs,i);
+ write_frame(cs,i+PRE);
+ }
+
+ /* fade in */
+ for(;i<FADE+FADEMARGIN;i++){
+ clear_surface(cs);
+ draw_axis(cs);
+ draw_cursor(cs,i,(double)(i-FADEMARGIN)/FADE);
+ draw_stairstep_trace(cs,i);
+ write_frame(cs,i+PRE);
+ }
+
+ /* normal */
+ for(;i<FRAMES-FADE-FADEMARGIN;i++){
+ clear_surface(cs);
+ draw_axis(cs);
+ draw_cursor(cs,i,1.);
+ draw_stairstep_trace(cs,i);
+ write_frame(cs,i+PRE);
+ }
+
+ /* fade out */
+ for(;i<FRAMES-FADEMARGIN;i++){
+ clear_surface(cs);
+ draw_axis(cs);
+ draw_cursor(cs,i,(double)(FRAMES-i-FADEMARGIN)/FADE);
+ draw_stairstep_trace(cs,i);
+ write_frame(cs,i+PRE);
+ }
+
+ /* static outro */
+ for(;i<FRAMES+POST;i++){
+ clear_surface(cs);
+ draw_axis(cs);
+ draw_stairstep_trace(cs,i);
+ write_frame(cs,i+PRE);
+ }
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch3_zerohold.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch3_zerohold.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch3_zerohold.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,462 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 300
+
+#define TEXTY 140
+#define TEXTY2 140
+#define TEXT_FONT_SIZE 90
+
+#define SAMPLES 18
+#define CYCLES 1
+
+#define PRE 128
+#define FADE 24
+#define OFFSET 1
+#define ARROW 64
+#define ZERO_FADE 24
+#define ZERO 256
+
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define FIRST_LINE_WIDTH 15
+#define FIRST_COLOR .6,.6,.6,1
+
+#define SECOND_LINE_WIDTH 15
+#define SECOND_COLOR .8,.4,.4,1
+#define HALO_TAIL 10
+#define HALO 15
+
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch3_zero-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+double get_y(double x){
+ return CR + WH*sin((double)x/W*CYCLES*2*M_PI);
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return floor(x/SW+.001);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return ceil(sample*SW);
+}
+
+void draw_axis(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double SW = 105;
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+
+ cairo_set_source_rgba(c,AXIS_COLOR*del);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=0;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+void draw_stems(cairo_surface_t *cs, double del, int frame){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ /* draw lollipops */
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx);
+ double y0 = get_y(tox(i-1));
+
+ double frameoff = frame - (i*OFFSET) - ((ARROW-SAMPLES*OFFSET)/2);
+ double dp = 1.-frameoff/((ARROW-SAMPLES*OFFSET)/2);
+ if(dp<0)dp=0;
+ if(dp>1)dp=1;
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_source_rgba(c,COLOR*del*(.5+dp*.5)*.8);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,sx,CR);
+ cairo_line_to(c,sx,y);
+ cairo_stroke(c);
+
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_source_rgba(c,FIRST_COLOR*(1-dp)*del);
+ cairo_set_line_width(c,FIRST_LINE_WIDTH);
+ cairo_move_to(c,sx,y0);
+ cairo_line_to(c,sx,y);
+ cairo_stroke(c);
+
+
+
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_samples(cairo_surface_t *cs, double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=1;i<SAMPLES;i++){
+ int sx = tox(i);
+ double y = get_y(sx);
+
+ cairo_set_source_rgba(c,COLOR*del*.4);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,sx,y,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,COLOR*del);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,sx,y);
+ cairo_line_to(c,sx,y+.001);
+ cairo_stroke(c);
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_stairstep_full(cairo_surface_t *cs,double del){
+ int i,flag=0;
+ double y;
+ cairo_t *c = cairo_create(cs);
+
+ cairo_set_source_rgba(c,FIRST_COLOR*del);
+ cairo_set_line_width(c,FIRST_LINE_WIDTH);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+
+ for(i=0;i<W;i++){
+ double s = tosample(i);
+ double sx = tox(s);
+ double y1 = get_y(sx);
+ if(!flag){
+ y=y1;
+ cairo_move_to(c,i,y);
+ flag=1;
+ }
+ cairo_line_to(c,i,y);
+ if(i>=W)break;
+ if(y!=y1)
+ cairo_line_to(c,i,y1);
+ y=y1;
+ }
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_arrows(cairo_surface_t *cs,double del,int frame){
+ int i,p,s;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw trace and halo in successive passes */
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_MITER);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+
+
+ for(s=0;s<SAMPLES;s++){
+ int sx = tox(s);
+ double end = tox(s+1);
+ int y = get_y(sx);
+
+ if(end>sx){
+ /* sample highlight */
+ cairo_set_source_rgba(c,.8,0,0,del);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,sx,y);
+ cairo_line_to(c,sx,y+.001);
+ cairo_stroke(c);
+ }
+ }
+
+ /* trace */
+ for(s=SAMPLES-1;s>=0;s--){
+ int sx = tox(s);
+ double end = tox(s+1);
+ int y = get_y(sx);
+ double frameoff = frame - (s*OFFSET);
+ double dp = frameoff/(ARROW-(SAMPLES*OFFSET));
+ if(dp<0)dp=0;
+ if(dp>1)dp=1;
+ dp = sin(dp*M_PI/2);
+ dp = dp*dp;
+ dp = sin(dp*M_PI/2);
+ dp = dp*dp;
+ end = (end-sx)*dp+sx;
+
+ if(end>sx){
+
+#if 0
+ /* trace background */
+ cairo_set_line_width(c,LINE_WIDTH);
+ cairo_set_source_rgba(c,SECOND_COLOR*del*dp);
+ cairo_move_to(c,sx,y);
+ cairo_line_to(c,end,y);
+ cairo_stroke(c);
+
+ /* sample highlight */
+ cairo_set_source_rgba(c,.8,0,0,del);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,sx,y);
+ cairo_line_to(c,sx,y+.001);
+ cairo_stroke(c);
+
+ for(p=HALO_TAIL;p>=0;p--){
+ double sdp = (frameoff-HALO_TAIL+p)/(ARROW-(SAMPLES*OFFSET));
+ if(sdp>=0 && sdp<=1){
+ sdp = sin(sdp*M_PI/2);
+ sdp = sdp*sdp;
+ sdp = sin(sdp*M_PI/2);
+ sdp = sdp*sdp;
+ double start = (tox(s+1)-sx)*sdp+sx;
+
+ for(i=start;i<=end;i++){
+ double fp = 1.-(end-i)/(double)(end-start+1);
+
+ if(p==0){
+ cairo_set_source_rgba(c,1,.2,.1,.25*fp*fp*del);
+ cairo_set_line_width(c,SECOND_LINE_WIDTH);
+ }else{
+ double dp = (HALO_TAIL-p+1.)/(HALO_TAIL+1);
+ cairo_set_source_rgba(c,1,.2,.1,(.003*fp*fp+.01*dp*fp)*del);
+ cairo_set_line_width(c,SECOND_LINE_WIDTH+2+HALO*
+ ((double)(p+3)/(HALO_TAIL+1))*
+ ((double)(p+3)/(HALO_TAIL+1)));
+ }
+
+ /* squarewave halo */
+ cairo_move_to(c,i-1,y);
+ cairo_line_to(c,i,y);
+ cairo_stroke(c);
+ if(i>=W)break;
+ }
+ }
+ }
+#endif
+
+ /* arrow_clear */
+ double ddp = (double)(end-sx)/(ARROW_LINE_WIDTH*5);
+ if(ddp<0)ddp=0;
+ if(ddp>1.)ddp=1.;
+ cairo_set_line_width(c,SECOND_LINE_WIDTH+4);
+ cairo_set_source_rgba(c,1,1,1,1*del*ddp);
+
+ cairo_move_to(c,sx,y);
+ cairo_line_to(c,end-SECOND_LINE_WIDTH*.75,y);
+ cairo_stroke(c);
+ cairo_set_line_width(c,ARROW_LINE_WIDTH+4);
+ cairo_move_to(c,end-ARROW_LINE_WIDTH*5,y + ARROW_LINE_WIDTH*3);
+ cairo_line_to(c,end,y);
+ cairo_line_to(c,end-ARROW_LINE_WIDTH*5,y - ARROW_LINE_WIDTH*3);
+ cairo_stroke(c);
+
+ /* arrow */
+ cairo_set_line_width(c,SECOND_LINE_WIDTH);
+ cairo_set_source_rgba(c,.8,0,0,1*del*ddp);
+
+ cairo_move_to(c,sx,y);
+ cairo_line_to(c,end-SECOND_LINE_WIDTH*.75,y);
+ cairo_stroke(c);
+ cairo_set_line_width(c,ARROW_LINE_WIDTH);
+ cairo_move_to(c,end-ARROW_LINE_WIDTH*5,y + ARROW_LINE_WIDTH*3);
+ cairo_line_to(c,end,y);
+ cairo_line_to(c,end-ARROW_LINE_WIDTH*5,y - ARROW_LINE_WIDTH*3);
+ cairo_stroke(c);
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_readout(cairo_surface_t *cs, double alpha){
+ cairo_t *c = cairo_create(cs);
+ cairo_font_face_t *ff;
+ cairo_text_extents_t extents;
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create cursor font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, TEXT_FONT_SIZE);
+ cairo_text_extents(c, "\xE2\x80\x9CZero-order hold\xE2\x80\x9D", &extents);
+ double TH = (TEXTY-extents.height)*2 + extents.height;
+
+ cairo_move_to(c, (W-extents.width)/2, TEXTY);
+
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_show_text(c, "\xE2\x80\x9CZero-order hold\xE2\x80\x9D");
+
+ cairo_font_face_destroy(ff);
+
+ cairo_destroy(c);
+}
+
+void draw_alignment(cairo_surface_t *cs,double del){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_font_face_t *ff;
+ cairo_text_extents_t extents;
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create cursor font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, TEXT_FONT_SIZE);
+ cairo_text_extents(c, "\xE2\x80\x9CZero-order hold\xE2\x80\x9D", &extents);
+ double TH = (TEXTY-extents.height)*2 + extents.height;
+
+ cairo_set_source_rgba(c,.6,.8,1,del);
+ cairo_set_line_width(c,2);
+
+ for(i=0;i<=SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,TH);
+ cairo_line_to(c,x,H-TH);
+ }
+ cairo_stroke(c);
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ W,H);
+ int i,count=0;
+
+ /* static preroll */
+ for(i=0;i<PRE;i++){
+ clear_surface(cs);
+ draw_stems(cs,1.,0);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade in alignment lines */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_alignment(cs,(double)i/FADE);
+ draw_stems(cs,1.,0);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.);
+ //draw_readout(cs,(double)i/FADE);
+ write_frame(cs,count++);
+ }
+
+ /* arrows */
+ for(i=0;i<ARROW;i++){
+ clear_surface(cs);
+ draw_alignment(cs,1.);
+ draw_stems(cs,1.,i);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.);
+ draw_arrows(cs,1.,i);
+ //draw_readout(cs,1.);
+ write_frame(cs,count++);
+ }
+
+ /* fade in zero-hold, */
+ for(i=0;i<ZERO_FADE;i++){
+ clear_surface(cs);
+ draw_alignment(cs,1.);
+ draw_stems(cs,1.,i+ARROW);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.);
+ draw_stairstep_full(cs,(double)i/ZERO_FADE);
+ draw_arrows(cs,1.,ARROW+i);
+ draw_readout(cs,(double)i/ZERO_FADE);
+ write_frame(cs,count++);
+ }
+
+ /* zero-hold */
+ for(i=0;i<ZERO;i++){
+ clear_surface(cs);
+ draw_alignment(cs,1.);
+ draw_stems(cs,1.,i+ARROW);
+ draw_axis(cs,1.);
+ draw_samples(cs,1.);
+ draw_stairstep_full(cs,1.);
+ draw_arrows(cs,1.,ARROW+i);
+ draw_readout(cs,1.);
+ write_frame(cs,count++);
+ }
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch4-samplequant.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch4-samplequant.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch4-samplequant.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,354 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <fftw3.h>
+#include <squishyio/squishyio.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 200
+#define SPACING 44
+#define QUANT 6
+
+#define LINE_WIDTH 6
+#define WAVE_WIDTH 12
+
+#define WAVEORIG .2,1,.2,.7
+#define WAVENOISE 1,.3,.3,.8
+#define WAVEQUANT 1,.7,0,.8
+#define STEM .0,.0,.0,.3
+#define LOLLI .0,.0,.0,1
+
+#define QSTEM .8,.0,.0,.3
+#define QLOLLI .8,.0,.0,1
+
+#define LINE .6,.9,1.,1
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+void write_frame(cairo_surface_t *surface, char *base){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch4_%s.png",base);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_lines(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ int i=-SPACING;
+
+ cairo_set_source_rgba(c,LINE);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,2);
+
+ for(;i<W+SPACING;i+=SPACING){
+ cairo_move_to(c,i-8,CR-WH);
+ cairo_line_to(c,i-8,CR+WH);
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_quant(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ int i;
+
+ cairo_set_source_rgba(c,LINE);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,2);
+
+ for(i=-QUANT;i<=QUANT;i++){
+ float y = (float)WH*i/QUANT+CR;
+ cairo_move_to(c,0,y);
+ cairo_line_to(c,W,y);
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_axis(cairo_surface_t *cs,int mark){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double SW = 105;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ if(mark){
+ for(i=1;i*SPACING<W;i++){
+ double x = i*SPACING-8;
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+ }
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+void draw_waveform(cairo_surface_t *cs, float *data, float r, float g, float b, float a){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ int first=0;
+ cairo_set_source_rgba(c,r,g,b,a);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WAVE_WIDTH);
+ for(i=1;i-SPACING<W+LINE_WIDTH;i++){
+ float x = i-SPACING-8;
+ if(x-WAVE_WIDTH>0 && x+WAVE_WIDTH<W){
+ if(!first){
+ cairo_move_to(c,x,CR-data[i]*WH);
+ first=1;
+ }else{
+ cairo_line_to(c,x,CR-data[i]*WH);
+ }
+ }
+ }
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_samples(cairo_surface_t *cs, float *data){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=0;(i-1)*SPACING<=W+10;i++){
+ int x = (i-1)*SPACING-8;
+ double y = CR-WH*data[i*SPACING];
+
+ if(x-LINE_WIDTH*2>0 && x+LINE_WIDTH*2<W){
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_source_rgba(c,STEM);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,x,CR);
+ cairo_line_to(c,x,y);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,LOLLI*.5);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,x,y,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,LOLLI);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,x,y);
+ cairo_line_to(c,x,y+.001);
+ cairo_stroke(c);
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_qsamples(cairo_surface_t *cs, float *data, float *qdata){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=0;(i-1)*SPACING<=W+10;i++){
+ int x = (i-1)*SPACING-8;
+ double y1 = CR-WH*data[i*SPACING];
+ double y2 = CR-WH*qdata[i*SPACING];
+
+ if(x-LINE_WIDTH*2>0 && x+LINE_WIDTH*2<W){
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_source_rgba(c,QSTEM);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,x,y1);
+ cairo_line_to(c,x,y2);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,QLOLLI*.5);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,x,y2,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,QLOLLI);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,x,y2);
+ cairo_line_to(c,x,y2+.001);
+ cairo_stroke(c);
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+/* cheap, dirty, circular, insufficient for high-fidelity, but this is just an illustration */
+float *oversample_data(float *in,int n){
+ int overn = n*SPACING*4;
+ int i;
+
+ float *data=calloc(overn+2,sizeof(*data));
+ fftwf_plan plans = fftwf_plan_dft_r2c_1d(n*4,data,
+ (fftwf_complex *)data,
+ FFTW_ESTIMATE);
+ fftwf_plan plani = fftwf_plan_dft_c2r_1d(overn,(fftwf_complex *)data,
+ data,
+ FFTW_ESTIMATE);
+
+ memset(data,0,sizeof(*data)*(overn+2));
+ memcpy(data,in,sizeof(*in)*n);
+
+ fftwf_execute(plans);
+ for(i=0;i<overn+2;i+=2){
+ data[i]*=.25/n;
+ data[i+1]*=.25/n;
+ }
+ fftwf_execute(plani);
+ fftwf_destroy_plan(plani);
+ fftwf_destroy_plan(plans);
+
+ return data;
+}
+
+float *quantize_data(float *in,int n){
+ int i;
+ float *ret = calloc(n,sizeof(*ret));
+ for(i=0;i<n;i++)
+ ret[i] = rint(in[i]*QUANT)/QUANT;
+ return ret;
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+int main(){
+ int i;
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+
+ pcm_t *wave = squishyio_load_file("../frames/ch4-short-sample.wav");
+ if(wave->samples<((W+SPACING)/SPACING)){
+ fprintf(stderr,"Not enough input samples\n");
+ exit(1);
+ }
+ for(i=0;i<wave->samples;i++)
+ wave->data[0][i] = wave->data[0][i] * 30.;
+
+ /* oversample original wave into new storage */
+ float *over = oversample_data(wave->data[0],wave->samples);
+
+ /* quantize original wave into new storage */
+ float *quant = quantize_data(wave->data[0],wave->samples);
+
+ /* oversample quantized data into new storage */
+ float *overquant = oversample_data(quant,wave->samples);
+
+ /* compute noise */
+ float *noise = calloc(W+SPACING*2,sizeof(float));
+ for(i=0;i<W+SPACING*2;i++)
+ noise[i] = overquant[i]-over[i];
+
+ /* draw oversampled original waveform */
+ clear_surface(cs);
+ draw_axis(cs,0);
+ draw_waveform(cs,over,WAVEORIG);
+ write_frame(cs,"origcaption");
+
+ /* draw oversampled original waveform with lines and samples */
+ clear_surface(cs);
+ draw_lines(cs);
+ draw_axis(cs,1);
+ draw_waveform(cs,over,WAVEORIG);
+ draw_samples(cs,over);
+ write_frame(cs,"sample");
+
+ /* draw oversample with quantization lines */
+ clear_surface(cs);
+ draw_lines(cs);
+ draw_quant(cs);
+ draw_axis(cs,1);
+ draw_waveform(cs,over,WAVEORIG);
+ draw_samples(cs,over);
+ write_frame(cs,"qlines");
+
+ /* draw highlighted quantization samples */
+ clear_surface(cs);
+ draw_lines(cs);
+ draw_quant(cs);
+ draw_axis(cs,1);
+ draw_waveform(cs,over,WAVEORIG);
+ draw_samples(cs,over);
+ draw_qsamples(cs,over,overquant);
+ write_frame(cs,"hiquant");
+
+ /* draw unhighlighted quantization samples+waveforms */
+ clear_surface(cs);
+ draw_lines(cs);
+ draw_quant(cs);
+ draw_axis(cs,1);
+ draw_waveform(cs,over,WAVEORIG);
+ draw_waveform(cs,overquant,WAVEQUANT);
+ draw_samples(cs,overquant);
+ write_frame(cs,"quant");
+
+ /* draw all waveforms */
+ clear_surface(cs);
+ draw_lines(cs);
+ draw_quant(cs);
+ draw_axis(cs,1);
+ draw_waveform(cs,over,WAVEORIG);
+ draw_waveform(cs,overquant,WAVEQUANT);
+ draw_samples(cs,overquant);
+ draw_waveform(cs,noise,WAVENOISE);
+ write_frame(cs,"noise");
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch5-quantize.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch5-quantize.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch5-quantize.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,336 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 250
+#define QUANT 4
+#define CYCLES 1
+#define SPACING 120
+
+#define FRAMES 12
+
+#define LINE_WIDTH 12
+
+#define STEM .0,.0,.0,.3
+#define LOLLI .0,.0,.0,1
+#define ARROW .9,.0,.0,.1
+#define LINE .6,.9,1.,1
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+void write_frame(cairo_surface_t *surface, char *base, int o){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch5_%s-%04d.png",base,o);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_quant(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ int i;
+
+ cairo_set_source_rgba(c,LINE);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,2);
+
+ for(i=-QUANT;i<=QUANT;i++){
+ float y = (float)WH*i/QUANT+CR;
+ cairo_move_to(c,0,y);
+ cairo_line_to(c,W,y);
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_axis(cairo_surface_t *cs,int mark){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double SW = 105;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ if(mark){
+ for(i=1;i*SPACING<W;i++){
+ double x = i*SPACING-8;
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+ }
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+void draw_samples(cairo_surface_t *cs, float *data, float ldel){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=0;(i-1)*SPACING<=W+10;i++){
+ int x = (i-1)*SPACING-8;
+ double yf = CR-WH*data[i];
+ double yi = CR-rint(data[i]*QUANT)/QUANT*WH;
+
+ if(x-LINE_WIDTH*2>0 && x+LINE_WIDTH*2<W){
+ float y;
+ y = ldel<0 ? yf : ldel > 1. ? yi : ldel*(yi-yf)+yf;
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_source_rgba(c,STEM);
+ cairo_set_line_width(c,LINE_WIDTH*.75);
+ cairo_move_to(c,x,CR);
+ cairo_line_to(c,x,y);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,LOLLI*.5);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,x,y,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,LOLLI);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,x,y);
+ cairo_line_to(c,x,y+.001);
+ cairo_stroke(c);
+
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_targets(cairo_surface_t *cs, float *data, float adel){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=0;(i-1)*SPACING<=W+10;i++){
+ int x = (i-1)*SPACING-8;
+ //double yf = CR-WH*data[i];
+ double yi = CR-rint(data[i]*QUANT)/QUANT*WH;
+ //double yi2 = yi>yf ? CR-(rint(data[i]*QUANT)+1)/QUANT*WH : CR-(rint(data[i]*QUANT)-1)/QUANT*WH;
+
+ if(x-LINE_WIDTH*2>0 && x+LINE_WIDTH*2<W){
+
+ cairo_set_source_rgba(c,ARROW_COLOR,adel*.5);
+ cairo_set_line_width(c,LINE_WIDTH/3);
+ cairo_arc(c,x,yi,LINE_WIDTH*2,0,2*M_PI);
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,ARROW_COLOR,adel);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH*2);
+ cairo_line_to(c,x,yi);
+ cairo_line_to(c,x,yi+.001);
+ cairo_stroke(c);
+
+ }
+ }
+ cairo_destroy(c);
+}
+
+void draw_arrows(cairo_surface_t *cs, float *data, float ldel, float adel){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ /* draw lollipops */
+ for(i=0;(i-1)*SPACING<=W+10;i++){
+ int x = (i-1)*SPACING-8;
+ double yf = CR-WH*data[i];
+ double yi = CR-rint(data[i]*QUANT)/QUANT*WH;
+
+ if(x-LINE_WIDTH*2>0 && x+LINE_WIDTH*2<W){
+ float y;
+ y = ldel<0 ? yf : ldel > 1. ? yi : ldel*(yi-yf)+yf;
+
+
+ if(yi<yf){ /* down arrow */
+ //if(yi > CR)
+ //y -= LINE_WIDTH*10+ARROW_LINE_WIDTH*10;
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH+3);
+ cairo_set_source_rgba(c,1.,1.,1.,adel);
+ cairo_move_to(c,x,y+LINE_WIDTH*5);
+ cairo_line_to(c,x,y+LINE_WIDTH*5+ARROW_LINE_WIDTH*10+1.5);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y+LINE_WIDTH*5+ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y+LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y+LINE_WIDTH*5+ARROW_LINE_WIDTH*4);
+
+
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH);
+ cairo_set_source_rgba(c,ARROW_COLOR,adel);
+ cairo_move_to(c,x,y+LINE_WIDTH*5);
+ cairo_line_to(c,x,y+LINE_WIDTH*5+ARROW_LINE_WIDTH*10);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y+LINE_WIDTH*5+ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y+LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y+LINE_WIDTH*5+ARROW_LINE_WIDTH*4);
+
+ cairo_stroke(c);
+
+
+ }
+ if(yi>yf){
+ //if(yi < CR )
+ //y += LINE_WIDTH*10+ARROW_LINE_WIDTH*10;
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH+3);
+ cairo_set_source_rgba(c,1.,1.,1.,adel);
+ cairo_move_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x,y-LINE_WIDTH*5-ARROW_LINE_WIDTH*10-1.5);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+
+
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,ARROW_LINE_WIDTH);
+ cairo_set_source_rgba(c,ARROW_COLOR,adel);
+ cairo_move_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x,y-LINE_WIDTH*5-ARROW_LINE_WIDTH*10);
+
+ cairo_move_to(c,x-ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+ cairo_line_to(c,x,y-LINE_WIDTH*5);
+ cairo_line_to(c,x+ARROW_LINE_WIDTH*3,
+ y-LINE_WIDTH*5-ARROW_LINE_WIDTH*4);
+
+ cairo_stroke(c);
+
+ }
+ }
+ }
+
+ cairo_destroy(c);
+}
+
+int main(){
+ int i,count=0;
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+
+ int s=(W+SPACING-1)/SPACING+1;
+ float *data = calloc((W+SPACING-1)/SPACING,sizeof(*data));
+ for(i=0;i<s;i++)
+ data[i] = -sin((i-.35)/s*2*M_PI)*.9;
+
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_samples(cs,data,0);
+ write_frame(cs,"quant",count++);
+
+ /* fade in lines */
+ for(i=0;i<FRAMES;i++){
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_targets(cs,data,(float)i/FRAMES);
+ draw_samples(cs,data,0);
+ write_frame(cs,"quant",count++);
+ }
+
+ /* fade in arrows */
+ for(i=0;i<FRAMES;i++){
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_targets(cs,data,1);
+ draw_samples(cs,data,0);
+ draw_arrows(cs,data,0,(float)i/FRAMES);
+ write_frame(cs,"quant",count++);
+ }
+
+ /* move */
+ for(i=0;i<FRAMES;i++){
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_targets(cs,data,1);
+ draw_samples(cs,data,(float)i/FRAMES);
+ draw_arrows(cs,data,(float)i/FRAMES,1);
+ write_frame(cs,"quant",count++);
+ }
+
+ /* fade out all */
+ for(i=0;i<FRAMES;i++){
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_targets(cs,data,1.-(float)i/FRAMES);
+ draw_samples(cs,data,1);
+ draw_arrows(cs,data,1,1-(float)i/FRAMES);
+ write_frame(cs,"quant",count++);
+ }
+
+ clear_surface(cs);
+ draw_quant(cs);
+ draw_axis(cs,0);
+ draw_samples(cs,data,1);
+ write_frame(cs,"quant",count++);
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-overlay.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-overlay.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-overlay.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,114 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <fftw3.h>
+
+#define W 1920
+#define H 1080
+#define CR 540
+#define WH 330
+#define SAMPLES 44
+#define CYCLES 1.1
+#define OFFSET -.05
+
+#define LINE_WIDTH 14
+#define COLOR .6,0,0,1
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_overlay-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_overlay(cairo_surface_t *cs,float *data){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ cairo_set_source_rgba(c,COLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ for(i=0;i<W;i++){
+ double y = data[i]*WH+CR;
+ if(i==0)
+ cairo_move_to(c,i,y);
+ else
+ cairo_line_to(c,i,y);
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+ int period=SAMPLES;
+ int coeffs = period/4;
+ float *square_coeffs;
+ float *square_phases;
+ int square_coeffs_n;
+ int i,j;
+ double phase=OFFSET*2*M_PI - (M_PI*CYCLES/period);
+ float waveform[W];
+ float *work = fftwf_malloc((period+2)*sizeof(*work));
+ fftwf_plan plan = fftwf_plan_dft_r2c_1d(period,work,
+ (fftwf_complex *)work,
+ FFTW_ESTIMATE);
+ for(i=0;i<period/2;i++)
+ work[i]=1.;
+ for(;i<period;i++)
+ work[i]=-1.;
+ fftwf_execute(plan);
+
+ square_coeffs_n = coeffs;
+ square_coeffs = calloc(coeffs,sizeof(*square_coeffs));
+ square_phases = calloc(coeffs,sizeof(*square_phases));
+
+ for(i=1,j=0;j<square_coeffs_n;i+=2,j++){
+ square_coeffs[j] = hypotf(work[i<<1],work[(i<<1)+1]) / period;
+ square_phases[j] = atan2f(work[(i<<1)+1],work[i<<1]);
+ }
+
+ for(i=0;i<W;i++){
+ float acc=0.;
+ int k;
+ for(j=0,k=1;j<square_coeffs_n;j++,k+=2)
+ acc += square_coeffs[j] *
+ cos( square_phases[j] + k*phase);
+ waveform[i]=acc;
+ phase += 2*M_PI*CYCLES/W;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+
+ transparent_surface(cs);
+ draw_overlay(cs,waveform);
+ write_frame(cs,0);
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-right.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-right.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-right.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,324 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <cairo/cairo.h>
+#include <fftw3.h>
+
+#define W 3840
+#define H 2160
+#define CR1 907.5
+#define CR2 1237.5
+#define DE .2
+#define WH 225
+#define SAMPLES 3840
+#define SPACING 100
+#define IMPULSE 80
+
+#define MULT 1.
+#define RATE 48000
+
+#define TIME 8.5
+#define PER 36
+
+
+#define LINE_WIDTH 6
+#define WAVE_WIDTH 15
+#define BAND .6,0,0,1
+
+#define WAVETOP .0,.0,.0,1
+#define WAVEBOTTOM 0,.0,.0,.25
+#define STEM .0,.0,.0,.3
+#define LOLLI .0,.0,.0,1
+
+#define LINE .6,.9,1.,1
+
+void write_frame(cairo_surface_t *surface, char *base, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_%s-%04d.png",base,frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_lines(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ double o;
+
+ cairo_set_source_rgba(c,LINE);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ cairo_move_to(c,W/2.,CR2-WH);
+ cairo_line_to(c,W/2.,CR2+WH*DE);
+ o=SPACING*MULT;
+
+ while(o<W+LINE_WIDTH/2.){
+ double x0=W/2.-o;
+ double x1=W/2.+o;
+
+ cairo_move_to(c,x0,CR2-WH);
+ cairo_line_to(c,x0,CR2+WH*DE);
+ cairo_move_to(c,x1,CR2-WH);
+ cairo_line_to(c,x1,CR2+WH*DE);
+
+ o+=SPACING*MULT;
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_right(cairo_surface_t *cs,float *data, float center, float shift){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ float center_sample = center*RATE+shift;
+ float samples = (float)W/MULT;
+ int start_sample = rint(center_sample-(samples/2));
+ int end_sample = rint(start_sample+samples);
+ float hold = 0;
+ float thresh=0;
+ double o=SPACING*MULT;
+
+ while(o+LINE_WIDTH<W/2.){
+ thresh=W/2.-o;
+ o+=SPACING*MULT;
+ }
+
+ fprintf(stderr,"%d %d %f\n",start_sample,end_sample,shift);
+
+ cairo_set_source_rgba(c,WAVETOP);
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WAVE_WIDTH);
+
+ for(i=start_sample;i<end_sample;i++){
+ double t = (double)(i-shift)/RATE;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+ double y = CR1-data[i]*WH;
+
+ if(i==start_sample)
+ cairo_move_to(c,x,y);
+ else
+ cairo_line_to(c,x,y);
+ }
+
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,WAVEBOTTOM);
+
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WAVE_WIDTH);
+
+ for(i=start_sample;i<end_sample;i++){
+ double t = (double)(i-shift)/RATE;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+ double y = CR2-data[i]*WH;
+
+ if(i==start_sample)
+ cairo_move_to(c,x,y);
+ else
+ cairo_line_to(c,x,y);
+ }
+
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,LINE_WIDTH+2);
+ cairo_set_source_rgba(c,STEM);
+
+ for(i=start_sample;i<end_sample;i++){
+ double tp = (double)(i-shift-1)/RATE;
+ double t = (double)(i-shift)/RATE;
+ double xp = (double)W/2. + (tp-center)*RATE*MULT;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+
+ if(i==start_sample || (xp<thresh && x>=thresh)){
+ hold=CR2-data[i]*WH;
+ cairo_move_to(c,thresh,CR2);
+ cairo_line_to(c,thresh,hold);
+ thresh+=SPACING*MULT;
+ }
+ }
+ cairo_stroke(c);
+
+ o=SPACING*MULT;
+
+ while(o+LINE_WIDTH<W/2.){
+ thresh=W/2.-o;
+ o+=SPACING*MULT;
+ }
+
+ cairo_set_line_width(c,WAVE_WIDTH/2.);
+
+ for(i=start_sample;i<end_sample;i++){
+ double tp = (double)(i-shift-1)/RATE;
+ double t = (double)(i-shift)/RATE;
+ double xp = (double)W/2. + (tp-center)*RATE*MULT;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+
+ if(i==start_sample || (xp<thresh && x>=thresh)){
+ hold=CR2-data[i]*WH;
+
+ cairo_set_source_rgba(c,LOLLI);
+ cairo_move_to(c,thresh,hold);
+ cairo_arc(c,thresh,hold,LINE_WIDTH*2,0,2*M_PI);
+ cairo_fill(c);
+
+ cairo_set_source_rgba(c,STEM);
+ cairo_arc(c,thresh,hold,LINE_WIDTH*5,0,2*M_PI);
+ cairo_stroke(c);
+ thresh+=SPACING*MULT;
+ }
+ }
+
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+ float *data=calloc(5*48000,sizeof(float));
+
+ fftwf_plan planf = fftwf_plan_dft_r2c_1d(5*48000-2,data,
+ (fftwf_complex *)data,
+ FFTW_ESTIMATE);
+ fftwf_plan plani = fftwf_plan_dft_c2r_1d(5*48000-2,(fftwf_complex *)data,
+ data,
+ FFTW_ESTIMATE);
+
+ int i,count=0;
+
+ int start=2.5*48000-IMPULSE/2;
+ for(i=0;i<IMPULSE;i++){
+ float v = sin((i+.5)/IMPULSE*M_PI);
+ data[i+start]= v*v * (2./(5*48000));
+ }
+ /* this is just an illustration, so quick hack the bandlimit; the
+ below is _WRONG_ for audio use, but it will be visually OK */
+ fftwf_execute(planf);
+ for(i=5*48000/2/100*2;i<5*48000;i++)
+ data[i]=0;
+ fftwf_execute(plani);
+#define WIN 3840
+
+ for(i=0;i<(int)(2.5*48000-WIN/2);i++)
+ data[i]=0;
+ for(i=0;i<WIN;i++){
+ float v=sin((i+.5)/WIN*M_PI);
+ data[i+(int)(2.5*48000-WIN/2)]*=v*v;
+ }
+ for(i=(int)(2.5*48000+WIN/2);i<(int)(5*48000);i++)
+ data[i]=0;
+
+ float shift=0;
+ for(i=0;i<24;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_right(cs,data, 2.5, shift);
+ write_frame(cs,"impulse-right",count++);
+ shift -= SPACING/PER*sin((i+.5)/48.*M_PI)*sin((i+.5)/48.*M_PI);
+ }
+
+ for(;i<TIME*24;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_right(cs,data, 2.5, shift);
+ write_frame(cs,"impulse-right",count++);
+ shift-=SPACING/PER;
+ }
+
+#define OFFSET 0
+
+ int period = 200;
+ int coeffs = period/4;
+ float *square_coeffs;
+ float *square_phases;
+ int square_coeffs_n;
+ int j;
+ double phase = 0;
+ float *work = fftwf_malloc((period+2)*sizeof(*work));
+ fftwf_plan plan = fftwf_plan_dft_r2c_1d(period,work,
+ (fftwf_complex *)work,
+ FFTW_ESTIMATE);
+ for(i=0;i<period/2;i++)
+ work[i]=1.;
+ for(;i<period;i++)
+ work[i]=-1.;
+ fftwf_execute(plan);
+
+ square_coeffs_n = coeffs;
+ square_coeffs = calloc(coeffs,sizeof(*square_coeffs));
+ square_phases = calloc(coeffs,sizeof(*square_phases));
+
+ for(i=1,j=0;j<square_coeffs_n;i+=2,j++){
+ square_coeffs[j] = hypotf(work[i<<1],work[(i<<1)+1]) / period;
+ square_phases[j] = atan2f(work[(i<<1)+1],work[i<<1]);
+ }
+
+ for(i=0;i<5*48000;i++){
+ float acc=0.;
+ int k;
+ for(j=0,k=1;j<square_coeffs_n;j++,k+=2)
+ acc += square_coeffs[j] *
+ cos( square_phases[j] + k*phase);
+ data[i]=(acc+.5)*.8;
+ phase += 2*M_PI/period/SPACING;
+ if(phase>=2*M_PI)phase-=2*M_PI;
+ }
+
+ for(i=0;i<(int)(2.5*48000-WIN/2);i++)
+ data[i]=0;
+ for(i=0;i<WIN/2;i++){
+ float v=sin((i+.5)/WIN*M_PI);
+ data[i+(int)(2.5*48000-WIN/2)]*=v*v;
+ }
+ for(;i<WIN;i++){
+ float v=sin((i+.5)/WIN*M_PI);
+ data[i+(int)(2.5*48000-WIN/2)] *= v*v;
+ data[i+(int)(2.5*48000-WIN/2)] += .8*(1.-v*v);
+ }
+ for(i=(int)(2.5*48000+WIN/2);i<(int)(5*48000);i++)
+ data[i]=.8;
+
+ count=0;
+ shift=0;
+ for(i=0;i<24;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_right(cs,data, 2.5, shift);
+ write_frame(cs,"box-right",count++);
+ shift -= SPACING/PER*sin((i+.5)/48.*M_PI)*sin((i+.5)/48.*M_PI);
+ }
+ for(;i<TIME*24;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_right(cs,data, 2.5, shift);
+ write_frame(cs,"box-right",count++);
+ shift-=SPACING/PER;
+ }
+
+
+
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-squareband.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-squareband.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-squareband.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,594 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 300
+#define WH 120
+#define CYCLES 3.1
+#define SAMPLES (3.1*4)
+#define OFFSET .2
+
+#define TEXTY 600
+#define TEXTH 500
+#define XMARGINL 90
+#define XMARGINR 60
+#define TEXTSIZE 60
+#define REDUCA .8
+#define REDUCB .78
+
+#define OVER 20
+#define YUP (24*4)
+
+#define PRE 48
+#define FHOLD 16
+#define FFADE 24
+#define BANDHOLD 72
+#define BANDFADE 18
+#define POST 48
+
+#define PLUS 1
+#define ACCEL .01
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+#define LINE_WIDTH 15
+
+#define FIRST_COLOR .6,.6,.6,1
+#define SECOND_COLOR .7,.0,.0,1
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_squareband-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_axis(cairo_surface_t *cs){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double SW = 105;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=-2;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+double sign(double x){
+ if(x<0)return -1;
+ if(x>0)return 1;
+ return 0;
+}
+
+double get_y(double x){
+ float v = sin(((double)x/W*CYCLES+OFFSET)*2*M_PI);
+ return CR + WH*sign(v);
+}
+
+
+double memo[(W+LINE_WIDTH*2)*OVER];
+static int lastmemo=-1;
+void compute_ys(int p){
+ int i,j;
+ if(lastmemo==-1 || p<lastmemo){
+ for(j=-LINE_WIDTH*OVER;j<(W+LINE_WIDTH)*OVER;j++){
+ double v=0.;
+ for(i=0;i<p;i++)
+ v += (4/((i*2+1)*M_PI))*sin(((j*(1./OVER)/W*CYCLES+OFFSET)*(i*2+1))*2*M_PI);
+ memo[j+LINE_WIDTH*OVER]=v;
+ }
+ }else if(p>lastmemo){
+ for(j=-LINE_WIDTH*OVER;j<(W+LINE_WIDTH)*OVER;j++){
+ double v=memo[j+LINE_WIDTH*OVER];
+ for(i=lastmemo;i<p;i++)
+ v += (4/((i*2+1)*M_PI))*sin(((j*(1./OVER)/W*CYCLES+OFFSET)*(i*2+1))*2*M_PI);
+ memo[j+LINE_WIDTH*OVER]=v;
+ }
+ }
+ lastmemo=p;
+}
+
+double get_ys(int x){
+ int i;
+ float v=0;
+ x+=LINE_WIDTH;
+ x*=OVER;
+ for(i=x-OVER+1;i<x+OVER;i++){
+ float d = 1.-fabs(x-i)/OVER;
+ v+=d*memo[i];
+ }
+ return CR+WH*v/OVER;
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return rint(x/SW);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint((sample-OFFSET*SAMPLES/CYCLES)*SW);
+}
+
+void draw_square(cairo_surface_t *cs, float alpha){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double y0;
+
+ cairo_set_source_rgba(c,FIRST_COLOR*.5*alpha);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ for(i=-LINE_WIDTH;i<W+LINE_WIDTH;i++){
+ double y = get_y(i);
+ cairo_move_to(c,i,y0);
+ cairo_line_to(c,i,y);
+ cairo_stroke(c);
+ y0=y;
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_waveform(cairo_surface_t *cs,int p, double alpha){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double ym=0,yM=0;
+ int x=-LINE_WIDTH;
+ if(p<1)return;
+
+ cairo_set_source_rgba(c,SECOND_COLOR*alpha);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ compute_ys(p);
+ for(i=-LINE_WIDTH;i<W+LINE_WIDTH;i++){
+ double y = get_ys(i);
+ cairo_line_to(c,i,y);
+ }
+
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+double draw_formula_Y(cairo_t *c,int size,double *xx, double y, int show){
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extentsB;
+ cairo_font_face_t *fi,*ff;
+ double x=*xx;
+ char *text;
+
+ fi = cairo_toy_font_face_create ("Liberation Serif",
+ CAIRO_FONT_SLANT_OBLIQUE,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!fi){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+ cairo_set_font_size(c, size);
+
+ cairo_set_font_face(c,fi);
+ text = "y";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text = "(";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+
+ cairo_set_font_face(c,fi);
+ text = "t";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text = ") = ";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_font_face_destroy(fi);
+ cairo_font_face_destroy(ff);
+ *xx=x;
+ return extents.height;
+}
+
+double draw_formula_T(cairo_t *c,int n,int size,double *xx,double y, int plus, int show){
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extentsB;
+ cairo_font_face_t *fi,*ff,*fb;
+ double x=*xx;
+ char *text;
+ char buf[80];
+ double th;
+
+ fi = cairo_toy_font_face_create ("Liberation Serif",
+ CAIRO_FONT_SLANT_OBLIQUE,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ fb = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ if(!ff){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+ if(!fi){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+ /* fraction */
+
+ cairo_set_font_face(c,fb);
+ cairo_set_font_size(c, size*.5);
+ text = "4";
+ cairo_text_extents(c, text, &extents);
+ th = extents.height;
+
+ text = "\xE2\x80\x94";
+ cairo_text_extents(c, text, &extentsB);
+ if(extents.x_advance>extentsB.x_advance)extentsB.x_advance=extents.x_advance;
+ if(n>1){
+ snprintf(buf,80,"%d\xCF\x80",(n*2-1));
+ }else{
+ snprintf(buf,80,"\xCF\x80");
+ }
+ cairo_text_extents(c, buf, &extents);
+ if(extents.x_advance>extentsB.x_advance)extentsB.x_advance=extents.x_advance;
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y+th-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,buf);
+
+ text = "\xE2\x80\x94";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y-extents.y_bearing);
+ if(show)
+ cairo_show_text(c,text);
+
+ text="4";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y-th-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x+= extentsB.x_advance+size/10;
+
+ cairo_set_font_size(c, size);
+ cairo_set_font_face(c,ff);
+
+ if(n>1)
+ snprintf(buf,80,"sin(%d",n*2-1);
+ else
+ snprintf(buf,80,"sin(");
+ text=buf;
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ cairo_set_font_face(c,fi);
+ text="\xCF\x89t";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text=")";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show)
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text=" + ";
+ cairo_text_extents(c, text, &extentsB);
+ cairo_move_to(c,x,y-extentsB.y_bearing/2);
+ if(plus)
+ cairo_show_text(c,text);
+ x+=extentsB.x_advance;
+
+ cairo_font_face_destroy(ff);
+ cairo_font_face_destroy(fi);
+ *xx=x;
+ return extents.height;
+}
+
+double draw_formula_K(cairo_t *c,int n,int size,double *xx,double y, int show){
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double x=*xx;
+ char *text;
+ char buf[80];
+ double th;
+
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ if(!ff){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+ cairo_set_font_size(c, size*1.2);
+ cairo_set_font_face(c,ff);
+
+ snprintf(buf,80,"%dkHz + ",n*2-1);
+ text=buf;
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ if(show){
+ snprintf(buf,80,"%dkHz",n*2-1);
+ cairo_show_text(c,text);
+ }
+ x+=extents.x_advance;
+
+ cairo_font_face_destroy(ff);
+ *xx=x;
+ return extents.height;
+}
+
+void draw_formula(cairo_t *c, int from, int to, int plusp, int fracp, int kHzp){
+ double y = TEXTY;
+ double size = TEXTSIZE;
+ int i = 1;
+ int alignw=0;
+ float reduce = REDUCA;
+ double lw = W-(XMARGINL+XMARGINR);
+
+ while(y<TEXTY+TEXTH){
+
+ double w = alignw;
+ double ww = alignw;
+ int ii=i;
+ double x=XMARGINL;
+ while(w < lw){
+ ww=w;
+ if(ii==1){
+ draw_formula_Y(c,size,&w, 0, 0);
+ alignw=w;
+ }
+ draw_formula_T(c,ii,size,&w, 0, 0, 0);
+ ii++;
+ }
+
+ lw=ww-8;
+ ii--;
+
+ double yh=0;
+ if(i>1)
+ x+=alignw;
+
+ for(;i<ii;i++){
+ if(i==1){
+ draw_formula_Y(c,size,&x, y, from==0);
+ if(to==0)return;
+ }
+
+ double wT=x;
+ double wK=0;
+ double hh = draw_formula_T(c,i,size, &wT, y, plusp && i>=from && i<to, fracp && i>=from && i<to);
+ wT-=x;
+ if(hh>yh)yh=hh;
+ draw_formula_K(c,i,size,&wK, y, 0);
+ if(i>=from && i<to && kHzp){
+ wK = x + (wT-wK)/2;
+ draw_formula_K(c,i,size,&wK, y, 1);
+ }
+
+ x+=wT;
+ if(hh>yh)yh=hh;
+ if(i>=to)return;
+ }
+
+ size *= reduce;
+ reduce = REDUCB;
+ yh*=1.5;
+ y += yh+10;
+ if(size<1)size=1;
+ }
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+ int i,j;
+ int count=0;
+
+ transparent_surface(cs);
+ draw_waveform(cs,10,1);
+ write_frame(cs,count++);
+
+ clear_surface(cs);
+ draw_square(cs,1);
+ draw_axis(cs);
+
+ cairo_t *c=cairo_create(cs);
+ cairo_set_source_rgba(c,0,0,0,1);
+ draw_formula(c,0,0,0,0,0);
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ draw_formula(c,1,1000,1,1,0);
+ cairo_destroy(c);
+
+ for(i=0;i<PRE;i++){
+ write_frame(cs,count++);
+ }
+
+ /* kHz markers */
+ int hold = FHOLD;
+ for(j=1;j<20;j+=2){
+ int jj = (j+1)/2;
+
+ for(i=1;i<=hold;i++){
+
+ clear_surface(cs);
+ draw_square(cs,1);
+ draw_axis(cs);
+
+ /* new waveform */
+ draw_waveform(cs,jj,1);
+
+ cairo_t *c=cairo_create(cs);
+ /* draw Y and all pluses */
+ cairo_set_source_rgba(c,0,0,0,1);
+ draw_formula(c,0,0,0,0,0);
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ draw_formula(c,1,1000,1,0,0);
+
+ /* draw fractions to date */
+ cairo_set_source_rgba(c,0,0,0,1);
+ draw_formula(c,1,jj,0,1,0);
+
+ /* draw fractions past */
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ draw_formula(c,jj+1,1000,0,1,0);
+
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ draw_formula(c,jj,jj+1,0,0,1);
+
+ write_frame(cs,count++);
+ }
+ hold--;
+ }
+
+ for(i=1;i<=FHOLD-hold;i++)
+ write_frame(cs,count++);
+
+ for(i=1;i<=FFADE;i++){
+ clear_surface(cs);
+ draw_square(cs,(1-(float)i/FFADE));
+ draw_axis(cs);
+
+ /* waveform */
+ draw_waveform(cs,10,1);
+
+ cairo_t *c=cairo_create(cs);
+ /* draw Y */
+ cairo_set_source_rgba(c,0,0,0,1);
+ draw_formula(c,0,0,0,0,0);
+
+ /* draw fractions to date */
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ draw_formula(c,1,10,1,0,0);
+ cairo_set_source_rgba(c,0,0,0,1);
+ draw_formula(c,1,10,0,1,0);
+
+ /* fade in last fraction */
+ cairo_set_source_rgba(c,0,0,0,(float)i/FFADE);
+ draw_formula(c,10,11,0,1,0);
+
+ /* fade out last marker */
+ cairo_set_source_rgba(c,SECOND_COLOR*(1-(float)i/FFADE));
+ draw_formula(c,10,11,0,0,1);
+
+ /* fade pluses past */
+ cairo_set_source_rgba(c,FIRST_COLOR*(1-(float)i/FFADE));
+ draw_formula(c,9,1000,1,0,0);
+
+ /* fade fractions past */
+ draw_formula(c,11,1000,0,1,0);
+
+ write_frame(cs,count++);
+ }
+
+ for(i=1;i<=BANDHOLD;i++)
+ write_frame(cs,count++);
+
+
+
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-squarefreq.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-squarefreq.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-squarefreq.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,542 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 300
+#define WH 120
+#define CYCLES 3.1
+#define SAMPLES (3.1*4)
+#define OFFSET .2
+
+#define TEXT0 750
+#define TEXTY 600
+#define TEXTH 500
+#define XMARGINL 90
+#define XMARGINR 60
+#define TEXTSIZE 60
+#define REDUCA .8
+#define REDUCB .78
+
+#define OVER 20
+#define YUP (24*4)
+
+#define PRE 240
+#define FADE 12
+#define FUNDAMENTAL 33
+#define PLUS (1./12)
+#define ACCEL 1.00
+#define ACCELB .0005
+#define APLUS 120
+#define APLUSPLUS 128
+#define POST 32
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+#define LINE_WIDTH 15
+
+#define FIRST_COLOR .6,.6,.6,1
+#define SECOND_COLOR .7,.0,.0,1
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_squarefreq-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_axis(cairo_surface_t *cs){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double SW = 105;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=-2;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+double sign(double x){
+ if(x<0)return -1;
+ if(x>0)return 1;
+ return 0;
+}
+
+double get_y(double x){
+ float v = sin(((double)x/W*CYCLES+OFFSET)*2*M_PI);
+ return CR + WH*sign(v);
+}
+
+
+double memo[(W+LINE_WIDTH*2)*OVER];
+static int lastmemo=-1;
+void compute_ys(int p){
+ int i,j;
+ if(lastmemo==-1 || p<lastmemo){
+ for(j=-LINE_WIDTH*OVER;j<(W+LINE_WIDTH)*OVER;j++){
+ double v=0.;
+ for(i=0;i<p;i++)
+ v += (4/((i*2+1)*M_PI))*sin(((j*(1./OVER)/W*CYCLES+OFFSET)*(i*2+1))*2*M_PI);
+ memo[j+LINE_WIDTH*OVER]=v;
+ }
+ }else if(p>lastmemo){
+ for(j=-LINE_WIDTH*OVER;j<(W+LINE_WIDTH)*OVER;j++){
+ double v=memo[j+LINE_WIDTH*OVER];
+ for(i=lastmemo;i<p;i++)
+ v += (4/((i*2+1)*M_PI))*sin(((j*(1./OVER)/W*CYCLES+OFFSET)*(i*2+1))*2*M_PI);
+ memo[j+LINE_WIDTH*OVER]=v;
+ }
+ }
+ lastmemo=p;
+}
+
+double get_ys(int x){
+ int i;
+ float v=0;
+ x+=LINE_WIDTH;
+ x*=OVER;
+ for(i=x-OVER+1;i<x+OVER;i++){
+ float d = 1.-fabs(x-i)/OVER;
+ v+=d*memo[i];
+ }
+ return CR+WH*v/OVER;
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return rint(x/SW);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint((sample-OFFSET*SAMPLES/CYCLES)*SW);
+}
+
+void draw_square(cairo_surface_t *cs){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double y0;
+
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ for(i=-LINE_WIDTH;i<W+LINE_WIDTH;i++){
+ double y = get_y(i);
+ cairo_move_to(c,i,y0);
+ cairo_set_source_rgba(c,FIRST_COLOR*.5);
+ cairo_line_to(c,i,y);
+ cairo_stroke(c);
+ y0=y;
+ }
+
+ cairo_destroy(c);
+}
+
+void draw_waveform(cairo_surface_t *cs,int p, double alpha){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double ym=0,yM=0;
+ int x=-LINE_WIDTH;
+ if(p<1)return;
+
+ cairo_set_source_rgba(c,SECOND_COLOR*alpha);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ compute_ys(p);
+ for(i=-LINE_WIDTH;i<W+LINE_WIDTH;i++){
+ double y = get_ys(i);
+ cairo_line_to(c,i,y);
+ }
+
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+double draw_formula_Y(cairo_t *c,int size,double *xx, double y, double alpha){
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extentsB;
+ cairo_font_face_t *fi,*ff;
+ double x=*xx;
+ char *text;
+
+ fi = cairo_toy_font_face_create ("Liberation Serif",
+ CAIRO_FONT_SLANT_OBLIQUE,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!fi){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+ cairo_set_source_rgba(c,0,0,0,alpha);
+ cairo_set_font_size(c, size);
+
+ cairo_set_font_face(c,fi);
+ text = "y";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text = "(";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+
+ cairo_set_font_face(c,fi);
+ text = "t";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text = ") = ";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_font_face_destroy(fi);
+ cairo_font_face_destroy(ff);
+ *xx=x;
+ return extents.height;
+}
+
+double draw_formula_T(cairo_t *c,int n,int size,double *xx,double y, int color, double alpha){
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extentsB;
+ cairo_font_face_t *fi,*ff,*fb;
+ double x=*xx;
+ char *text;
+ char buf[80];
+ double th;
+
+ fi = cairo_toy_font_face_create ("Liberation Serif",
+ CAIRO_FONT_SLANT_OBLIQUE,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ fb = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_BOLD);
+ if(!ff){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+ if(!fi){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+
+ switch(color){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,0);
+ break;
+ case 1:
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR*alpha);
+ break;
+ }
+
+ /* fraction */
+
+ cairo_set_font_face(c,fb);
+ cairo_set_font_size(c, size*.5);
+ text = "4";
+ cairo_text_extents(c, text, &extents);
+ th = extents.height;
+
+ text = "\xE2\x80\x94";
+ cairo_text_extents(c, text, &extentsB);
+ if(extents.x_advance>extentsB.x_advance)extentsB.x_advance=extents.x_advance;
+ if(n>1){
+ snprintf(buf,80,"%d\xCF\x80",(n*2-1));
+ }else{
+ snprintf(buf,80,"\xCF\x80");
+ }
+ cairo_text_extents(c, buf, &extents);
+ if(extents.x_advance>extentsB.x_advance)extentsB.x_advance=extents.x_advance;
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y+th-extents.y_bearing/2);
+ cairo_show_text(c,buf);
+
+ text = "\xE2\x80\x94";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y-extents.y_bearing);
+ cairo_show_text(c,text);
+
+ text="4";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (extentsB.x_advance - extents.x_advance)/2,y-th-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x+= extentsB.x_advance+size/10;
+
+ cairo_set_font_size(c, size);
+ cairo_set_font_face(c,ff);
+
+ if(n>1)
+ snprintf(buf,80,"sin(%d",n*2-1);
+ else
+ snprintf(buf,80,"sin(");
+ text=buf;
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ cairo_set_font_face(c,fi);
+ text="\xCF\x89t";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ cairo_set_font_face(c,ff);
+ text=")";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x+=extents.x_advance;
+
+ switch(color){
+ case 0:
+ case 1:
+ cairo_set_source_rgba(c,0,0,0,0);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR*alpha);
+ break;
+ }
+
+ cairo_set_font_face(c,ff);
+ text=" + ";
+ cairo_text_extents(c, text, &extentsB);
+ cairo_move_to(c,x,y-extentsB.y_bearing/2);
+ cairo_show_text(c,text);
+ x+=extentsB.x_advance;
+
+ cairo_font_face_destroy(ff);
+ cairo_font_face_destroy(fi);
+ *xx=x;
+ return extents.height;
+}
+
+static int startframe=-1;
+
+void draw_formula(cairo_surface_t *cs, double pp, int frame, double alpha){
+ cairo_t *c = cairo_create(cs);
+ int p = pp;
+ double y = TEXTY;
+ double size = TEXTSIZE;
+ int i = 1;
+ int alignw=0;
+ float reduce = REDUCA;
+ double lw = W-(XMARGINL+XMARGINR);
+
+ while(y<TEXTY+TEXTH){
+
+ double w = alignw;
+ double ww = alignw;
+ int ii=i;
+ double x=XMARGINL;
+ while(w < lw){
+ ww=w;
+ if(ii==1){
+ draw_formula_Y(c,size,&w, 0, 0);
+ alignw=w;
+ }
+ draw_formula_T(c,ii,size,&w, 0, 0,0);
+ if(i==1 && ii>p && w<lw){
+ ii++;
+ x = (W-XMARGINL-XMARGINR-ww)/2+XMARGINL;
+ y = TEXT0;
+ break;
+ }
+ ii++;
+ }
+
+ if(i==1 && ii-1>p && w>=lw && startframe==-1){
+ startframe=frame;
+ }
+ if(i==1 && startframe>-1){
+ float del = (float)(frame-startframe)/YUP;
+ if(del<=1){
+ del = sin(del*.5*M_PI);
+ del *= del;
+ y = del*TEXTY + (1-del)*TEXT0;
+ }else{
+ y = TEXTY;
+ }
+ }
+
+ lw=ww-8;
+ ii--;
+
+
+ double yh=0;
+
+ if(i>1)
+ x+=alignw;
+
+ for(;i<ii;i++){
+ if(i==1)
+ draw_formula_Y(c,size,&x, y, alpha);
+
+ double hh=draw_formula_T(c,i,size,&x, y, i<p?2:(p&&i<=p?1:0), alpha);
+ if(hh>yh)yh=hh;
+ }
+
+ size *= reduce;
+ reduce = REDUCB;
+ yh*=1.5;
+ y += yh+10;
+ if(size<1)size=1;
+ }
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ W,H);
+ int i;
+ int count=0;
+
+ clear_surface(cs);
+ draw_square(cs);
+ draw_axis(cs);
+ for(i=0;i<PRE;i++){
+ write_frame(cs,count++);
+ }
+
+ /* fade to fundamental */
+ for(i=0;i<FADE;i++){
+ clear_surface(cs);
+ draw_square(cs);
+ draw_axis(cs);
+ draw_waveform(cs,1,(float)i/FADE);
+ draw_formula(cs,1,0,(float)i/FADE);
+ write_frame(cs,count++);
+ }
+
+ /* hold fundamental */
+ clear_surface(cs);
+ draw_square(cs);
+ draw_axis(cs);
+ draw_waveform(cs,1,1);
+ draw_formula(cs,1,0,1);
+ for(i=0;i<FUNDAMENTAL;i++){
+ write_frame(cs,count++);
+ }
+
+ /* count up */
+ float p = 2;
+ float a = ACCEL;
+ float a2 = PLUS;
+ int lastinc=0;
+ int cont=0;
+ for(i=1;i<=APLUS;i++){
+ clear_surface(cs);
+ draw_square(cs);
+ draw_axis(cs);
+ draw_waveform(cs,(int)p,1);
+ draw_formula(cs,p,i,1);
+ write_frame(cs,count++);
+ a+=(i<APLUS/2)?ACCELB:ACCELB*(1+20*((float)(i-APLUS/2)/(APLUS/2)));
+ a2*=a;
+ int inc = (int)(p+a2) - (int)(p);
+ if(inc>0 && lastinc>0)cont=1;
+ if(cont && inc<lastinc){
+ p+=inc=lastinc;
+ }else{
+ p+=a2;
+ }
+ lastinc=inc;
+ fprintf(stderr,"p=%f, togo=%d\n",p,APLUSPLUS-i);
+ }
+
+ for(;i<=APLUSPLUS;i++){
+ clear_surface(cs);
+ draw_square(cs);
+ draw_axis(cs);
+ draw_waveform(cs,(int)p,1);
+ draw_formula(cs,p,i,1);
+ write_frame(cs,count++);
+ fprintf(stderr,"p=%f, togo=%d\n",p,APLUSPLUS-i);
+ }
+
+ for(i=0;i<POST;i++)
+ write_frame(cs,count++);
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-squaretime.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-squaretime.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-squaretime.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,509 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 1920
+#define H 1080
+#define CR 300
+#define WH 120
+#define CYCLES 3.1
+#define SAMPLES (3.1*4)
+#define OFFSET .2
+
+#define TEXTY 750
+#define TEXTSIZE 80
+
+/*
+-1 0 < t mod T <= .5T
+ 1 .5T < t mod T <= T
+*/
+
+
+#define AXIS_LINE_WIDTH 6
+#define AXIS_COLOR 0,0,0,1
+#define AXIS_FONT_SIZE 54
+
+#define LINE_WIDTH 15
+#define COLOR .4,.4,.4,1
+
+#define ARROW_LINE_WIDTH 6
+#define ARROW_COLOR 1,0,0
+
+#define LINE_WIDTH 15
+
+#define FIRST_COLOR .6,.6,.6,1
+#define SECOND_COLOR .7,.0,.0,1
+
+void write_frame(cairo_surface_t *surface, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_squaretime-%04d.png",frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void clear_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgb(c,1.,1.,1.);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_axis(cairo_surface_t *cs){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ cairo_text_extents_t extents;
+ cairo_font_face_t *ff;
+ double SW = 105;
+
+ cairo_set_source_rgba(c,AXIS_COLOR);
+ cairo_set_line_width(c,AXIS_LINE_WIDTH);
+ cairo_move_to(c,0,CR);
+ cairo_line_to(c,W,CR);
+ cairo_move_to(c,W-SW/2,CR-SW/3);
+ cairo_line_to(c,W,CR);
+ cairo_line_to(c,W-SW/2,CR+SW/3);
+ cairo_stroke(c);
+
+ cairo_set_line_width(c,AXIS_LINE_WIDTH*.75);
+
+ for(i=-2;i<SAMPLES;i++){
+ double x = tox(i);
+ cairo_move_to(c,x,CR-AXIS_LINE_WIDTH*2);
+ cairo_line_to(c,x,CR+AXIS_LINE_WIDTH*2);
+ }
+ cairo_stroke(c);
+
+
+ ff = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_ITALIC,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create axis font");
+ exit(1);
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, AXIS_FONT_SIZE);
+ cairo_text_extents(c, "time", &extents);
+ cairo_move_to(c, W-extents.width-SW*.6, CR + AXIS_FONT_SIZE);
+ cairo_show_text(c, "time");
+
+ cairo_font_face_destroy(ff);
+ cairo_destroy(c);
+}
+
+double sign(double x){
+ if(x<0)return -1;
+ if(x>0)return 1;
+ return 0;
+}
+
+double get_y(double x){
+ float v = sin(((double)x/W*CYCLES+OFFSET)*2*M_PI);
+ return CR + WH*sign(v);
+}
+
+int tosample(double x){
+ double SW = (double)W/SAMPLES;
+ return rint(x/SW);
+}
+
+int tox(int sample){
+ double SW = (double)W/SAMPLES;
+ return rint((sample-OFFSET*SAMPLES/CYCLES)*SW);
+}
+
+void draw_waveform(cairo_surface_t *cs,int p){
+ int i;
+ cairo_t *c = cairo_create(cs);
+ double y0;
+
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ for(i=-LINE_WIDTH;i<W+LINE_WIDTH;i++){
+ double y = get_y(i);
+ if(i==-LINE_WIDTH){
+ }else{
+ cairo_move_to(c,i,y0);
+ if(y!=y0){
+ cairo_set_source_rgba(c,FIRST_COLOR*.5);
+ }else{
+ if( (y<CR && p==1) || (y>CR && p==2) )
+ cairo_set_source_rgba(c,SECOND_COLOR*.5);
+ else
+ cairo_set_source_rgba(c,FIRST_COLOR*.5);
+ }
+ cairo_line_to(c,i,y);
+ cairo_stroke(c);
+ }
+ y0=y;
+ }
+
+ cairo_destroy(c);
+}
+
+#define MAX(x,y) ((x)>(y)?(x):(y))
+void draw_formula(cairo_surface_t *cs,int p){
+ cairo_text_extents_t extents;
+ cairo_text_extents_t extentsB;
+ cairo_text_extents_t extentsT;
+ cairo_font_face_t *fi,*ff,*fb;
+ cairo_t *c = cairo_create(cs);
+
+ ff = cairo_toy_font_face_create ("Liberation Sans",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ fi = cairo_toy_font_face_create ("Liberation Serif",
+ CAIRO_FONT_SLANT_OBLIQUE,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ fb = cairo_toy_font_face_create ("Adobe Garamond",
+ CAIRO_FONT_SLANT_NORMAL,
+ CAIRO_FONT_WEIGHT_NORMAL);
+ if(!ff){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+ if(!fi){
+ fprintf(stderr,"Unable to create formula font");
+ exit(1);
+ }
+
+ int th=0,tw=0,bw=0,bh=0,w5=0,wT;
+
+ cairo_set_font_face(c,fi);
+ cairo_set_font_size(c, TEXTSIZE);
+
+ cairo_text_extents(c, "y(t) = ", &extents);
+ tw += extents.x_advance;
+ cairo_text_extents(c, " t (mod T)", &extents);
+ tw += extents.x_advance;
+
+ cairo_text_extents(c, "T", &extentsT);
+ wT = extentsT.x_advance;
+ th = extentsT.height;
+
+ cairo_set_font_face(c,ff);
+ cairo_text_extents(c, " +1, ", &extents);
+ cairo_text_extents(c, " -1, ", &extentsB);
+
+ tw += MAX(extents.x_advance, extentsB.x_advance);
+
+ cairo_set_font_size(c, TEXTSIZE/2);
+ cairo_text_extents(c, "\xE2\x80\x94", &extents);
+ w5 = extents.x_advance + extentsT.x_advance;
+ tw += w5+w5;
+
+ cairo_set_font_size(c, TEXTSIZE);
+ cairo_text_extents(c, " \x3C", &extents);
+ tw += extents.x_advance;
+ cairo_text_extents(c, " \xE2\x89\xA4 ", &extents);
+ tw += extents.x_advance;
+
+ cairo_set_font_face(c,fb);
+ cairo_set_font_size(c, TEXTSIZE*4);
+ cairo_text_extents(c, "{", &extents);
+ tw += extents.x_advance;
+
+ cairo_set_font_face(c,fi);
+ cairo_set_font_size(c, TEXTSIZE);
+ cairo_set_source_rgba(c,0,0,0,1);
+
+ int x = W/2-(tw/2)*1.05;
+ int y = (th*1.5);
+ char *text;
+
+ cairo_set_source_rgba(c,0,0,0,1);
+ text="y(t) = ";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c, x, TEXTY-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ cairo_set_font_face(c,fb);
+ cairo_set_font_size(c, TEXTSIZE*4);
+ text="{";
+ cairo_text_extents(c, text, &extents);
+ int d = extents.height+extents.y_bearing;
+
+ cairo_move_to(c,x,TEXTY+extents.height/2-d);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, TEXTSIZE);
+ text=" +1, ";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ text=" -1, ";
+ cairo_text_extents(c, text, &extentsB);
+ cairo_move_to(c,x+(extents.x_advance-extentsB.x_advance),TEXTY+y-extentsB.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ text="0";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - extents.x_advance)/2,TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ cairo_set_font_size(c, TEXTSIZE/2);
+ text="1";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY+y-th*.5-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ text="\xE2\x80\x94";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY+y-extents.y_bearing);
+ cairo_show_text(c,text);
+
+ text="2";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY+y+th*.5-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ cairo_set_font_size(c, TEXTSIZE);
+ cairo_set_font_face(c,fi);
+ text="T";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT),TEXTY+y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += w5;
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,ff);
+ text=" \x3C";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,TEXTY+y-extents.y_bearing/2);
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,fi);
+ text=" t (mod T)";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ cairo_text_extents(c, text, &extents);
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ cairo_move_to(c,x,TEXTY+y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,ff);
+ text=" \xE2\x89\xA4 ";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x,TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ cairo_text_extents(c, text, &extents);
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ cairo_move_to(c,x,TEXTY+y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += extents.x_advance;
+
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,fi);
+ text="T";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - extents.x_advance)/2,TEXTY+y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ switch(p){
+ case 0:
+ cairo_set_source_rgba(c,0,0,0,1);
+ break;
+ case 1:
+ cairo_set_source_rgba(c,SECOND_COLOR);
+ break;
+ case 2:
+ cairo_set_source_rgba(c,FIRST_COLOR);
+ break;
+ }
+ cairo_set_font_face(c,ff);
+ cairo_set_font_size(c, TEXTSIZE/2);
+ text="1";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY-y-th*.5-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ text="\xE2\x80\x94";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY-y-extents.y_bearing);
+ cairo_show_text(c,text);
+
+ text="2";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT - extents.x_advance)/2,TEXTY-y+th*.5-extents.y_bearing/2);
+ cairo_show_text(c,text);
+
+ cairo_set_font_size(c, TEXTSIZE);
+ cairo_set_font_face(c,fi);
+ text="T";
+ cairo_text_extents(c, text, &extents);
+ cairo_move_to(c,x + (w5 - wT),TEXTY-y-extents.y_bearing/2);
+ cairo_show_text(c,text);
+ x += w5;
+
+ cairo_font_face_destroy(ff);
+ cairo_font_face_destroy(fi);
+ cairo_font_face_destroy(fb);
+}
+
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+ W,H);
+
+ clear_surface(cs);
+ draw_waveform(cs,0);
+ draw_axis(cs);
+ draw_formula(cs,0);
+ write_frame(cs,0);
+
+ clear_surface(cs);
+ draw_waveform(cs,1);
+ draw_axis(cs);
+ draw_formula(cs,1);
+ write_frame(cs,1);
+
+ clear_surface(cs);
+ draw_waveform(cs,2);
+ draw_axis(cs);
+ draw_formula(cs,2);
+ write_frame(cs,2);
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
Added: trunk/Xiph-episode-II/cairo/ch6-wrong.c
===================================================================
--- trunk/Xiph-episode-II/cairo/ch6-wrong.c (rev 0)
+++ trunk/Xiph-episode-II/cairo/ch6-wrong.c 2013-02-24 22:04:10 UTC (rev 18812)
@@ -0,0 +1,180 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <cairo/cairo.h>
+
+#define W 3840
+#define H 2160
+#define CR1 907.5
+#define CR2 1237.5
+#define DE .2
+#define WH 225
+#define SAMPLES 3840
+#define SPACING 100
+#define IMPULSE 80
+
+#define MULT 1.
+#define RATE 48000
+
+#define TIME 25
+#define PER 36
+
+
+#define LINE_WIDTH 6
+#define WAVE_WIDTH 15
+#define BAND .6,0,0,1
+#define WAVETOP .0,.0,.0,1
+#define WAVEBOTTOM .0,.0,.0,.25
+#define LINE .6,.9,1.,1
+
+void write_frame(cairo_surface_t *surface, char *base, int frameno){
+ char buffer[80];
+ cairo_status_t ret;
+
+ snprintf(buffer,80,"ch6_%s-%04d.png",base,frameno);
+ ret = cairo_surface_write_to_png(surface,buffer);
+ if(ret != CAIRO_STATUS_SUCCESS){
+ fprintf(stderr,"Could not write %s: %s\n",
+ buffer,cairo_status_to_string(ret));
+ exit(1);
+ }
+}
+
+void transparent_surface(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ cairo_set_source_rgba(c,1.,1.,1.,1.);
+ cairo_set_operator(c,CAIRO_OPERATOR_CLEAR);
+ cairo_paint(c);
+ cairo_destroy(c);
+}
+
+void draw_lines(cairo_surface_t *cs){
+ cairo_t *c = cairo_create(cs);
+ double o;
+
+ cairo_set_source_rgba(c,LINE);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_BUTT);
+ cairo_set_line_width(c,LINE_WIDTH);
+
+ cairo_move_to(c,W/2.,CR2-WH);
+ cairo_line_to(c,W/2.,CR2+WH*DE);
+ o=SPACING*MULT;
+
+ while(o<W+LINE_WIDTH/2.){
+ double x0=W/2.-o;
+ double x1=W/2.+o;
+
+ cairo_move_to(c,x0,CR2-WH);
+ cairo_line_to(c,x0,CR2+WH*DE);
+ cairo_move_to(c,x1,CR2-WH);
+ cairo_line_to(c,x1,CR2+WH*DE);
+
+ o+=SPACING*MULT;
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+void draw_wrong(cairo_surface_t *cs,float *data, float center, float shift){
+ int i;
+ cairo_t *c = cairo_create(cs);
+
+ float center_sample = center*RATE+shift;
+ float samples = (float)W/MULT;
+ int start_sample = rint(center_sample-(samples/2));
+ int end_sample = rint(start_sample+samples);
+ float hold = CR2;
+ float thresh=0;
+ double o=SPACING*MULT;
+
+ while(o+LINE_WIDTH<W/2.){
+ thresh=W/2.-o;
+ o+=SPACING*MULT;
+ }
+
+ fprintf(stderr,"%d %d %f\n",start_sample,end_sample,shift);
+
+ cairo_set_source_rgba(c,WAVETOP);
+ cairo_set_line_cap(c,CAIRO_LINE_CAP_ROUND);
+ cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND);
+ cairo_set_line_width(c,WAVE_WIDTH);
+
+ for(i=start_sample;i<end_sample;i++){
+ double t = (double)(i-shift)/RATE;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+ double y = CR1-data[i]*WH;
+
+ if(i==start_sample)
+ cairo_move_to(c,x,y);
+ else
+ cairo_line_to(c,x,y);
+ }
+
+ cairo_stroke(c);
+
+ cairo_set_source_rgba(c,WAVEBOTTOM);
+
+ cairo_move_to(c,thresh,CR2);
+ for(i=start_sample;i<end_sample;i++){
+ double tp = (double)(i-shift-1)/RATE;
+ double t = (double)(i-shift)/RATE;
+ double xp = (double)W/2. + (tp-center)*RATE*MULT;
+ double x = (double)W/2. + (t-center)*RATE*MULT;
+
+ if(xp<thresh && x>=thresh){
+ cairo_line_to(c,thresh,hold);
+
+ hold=CR2-data[i]*WH;
+
+ cairo_line_to(c,thresh,hold);
+ thresh += SPACING*MULT;
+ }
+
+ cairo_line_to(c,x,hold);
+ }
+
+ cairo_stroke(c);
+ cairo_destroy(c);
+}
+
+int main(){
+ cairo_surface_t *cs = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
+ W,H);
+ float *data=calloc(5*48000,sizeof(float));
+ int i,count=0;
+
+ int start=2.5*48000-IMPULSE/2;
+ for(i=0;i<IMPULSE;i++){
+ float v = sin((i+.5)/IMPULSE*M_PI);
+ data[i+start]= v*v*.8;
+ }
+
+ for(i=0;i<360;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_wrong(cs,data, 2.5, (TIME*24/2.+2.2*24-i)*SPACING/PER);
+ write_frame(cs,"impulse-wrong",count++);
+ }
+
+ memset(data,0,sizeof(*data)*48000*5);
+
+ for(i=0;i<2.5*48000;i++)
+ data[i]=0;
+ for(;i<5*48000;i++)
+ data[i]=.8;
+
+ count=0;
+
+ for(i=0;i<360;i++){
+ transparent_surface(cs);
+ draw_lines(cs);
+ draw_wrong(cs,data, 2.5, (TIME*24/2.+2.2*24-i)*SPACING/PER);
+ write_frame(cs,"box-wrong",count++);
+ }
+
+ cairo_surface_destroy(cs);
+ return 0;
+}
More information about the commits
mailing list