1 /* inverse.c -- compute the inverse of a sampled function */
2 
3 /* CHANGE LOG
4  * --------------------------------------------------------------------
5  * 28Apr03  dm  changes for portability and fix compiler warnings
6  */
7 
8 
9 #include "stdio.h"
10 #ifndef mips
11 #include "stdlib.h"
12 #endif
13 #include "xlisp.h"
14 #include "sound.h"
15 #include "cext.h"
16 
17 #include "falloc.h"
18 #include "inverse.h"
19 
20 void inverse_free(snd_susp_type a_susp);
21 
22 
23 typedef struct inverse_susp_struct {
24     snd_susp_node susp;
25     int64_t terminate_cnt;
26     boolean logically_stopped;
27     sound_type s;
28     int s_cnt;
29     sample_block_values_type s_ptr;
30     double s_prev;
31     double s_time;
32     double s_time_increment;
33     double out_time_increment;
34     boolean started;
35 } inverse_susp_node, *inverse_susp_type;
36 
inverse_fetch(snd_susp_type a_susp,snd_list_type snd_list)37 void inverse_fetch(snd_susp_type a_susp, snd_list_type snd_list)
38 {
39     inverse_susp_type susp = (inverse_susp_type) a_susp;
40     int cnt = 0; /* how many samples read from s */
41     int out_cnt = 0; /* how many samples output */
42     int togo = 0; /* how many more to read from s in inner loop */
43     int n;
44     sample_block_type out;
45     double out_time = susp->susp.current * susp->out_time_increment;
46 
47     register sample_block_values_type out_ptr;
48 
49     register sample_block_values_type s_ptr_reg;
50     falloc_sample_block(out, "inverse_fetch");
51     out_ptr = out->samples;
52     snd_list->block = out;
53 
54     /* make sure we are primed with first value */
55     /* This is a lot of work just to prefetch susp->s_prev! */
56     if (!susp->started) {
57         susp->started = true;
58         /* see comments below about susp_check_term_log_samples() */
59         if (susp->s_cnt == 0) {
60             susp_get_samples(s, s_ptr, s_cnt);
61             if (susp->s_ptr == zero_block->samples) {
62                 susp->terminate_cnt = susp->susp.current;
63             }
64         }
65         susp->s_prev = susp_fetch_sample(s, s_ptr, s_cnt);
66     }
67 
68     while (out_cnt < max_sample_block_len) { /* outer loop */
69         /* first compute how many samples to generate in inner loop: */
70         /* don't run past the s input sample block: */
71         /* most fetch routines call susp_check_term_log_samples() here
72          * but we can't becasue susp_check_term_log_samples() assumes
73           * that output time progresses at the same rate as input time.
74          * Here, some time warping is going on, so this doesn't work.
75          * Instead, check for termination of s and fix terminate_cnt to
76          * be the current output count rather than the current input time.
77          */
78         if (susp->s_cnt == 0) {
79             susp_get_samples(s, s_ptr, s_cnt);
80             if (susp->s_ptr == zero_block->samples) {
81                 susp->terminate_cnt = susp->susp.current + out_cnt;
82                 /* we can't simply terminate here because we might have
83                  * some output samples computed already, in which case we
84                  * want to return them now and terminate the NEXT time we're
85                  * called.
86                  */
87             }
88         }
89         togo = susp->s_cnt;
90 
91         /* if we ran past terminate time, fix up output */
92         if (susp->terminate_cnt != UNKNOWN &&
93             susp->terminate_cnt <= susp->susp.current + out_cnt) {
94             /* pretend like we computed the correct number of samples */
95             togo = 0;
96             out_cnt = (long) (susp->terminate_cnt - susp->susp.current);
97             /* exit the loop to complete the termination */
98             break;
99         }
100         n = togo;
101         s_ptr_reg = susp->s_ptr;
102         if (n) do { /* the inner sample computation loop */
103             /* scan s_ptr_reg to time t, output and loop */
104             register double next_value = *s_ptr_reg++;
105             while (out_time < next_value) {
106                 *out_ptr++ = (float) (susp->s_time +
107                              (out_time - susp->s_prev) /
108                              (susp->s->sr * (next_value - susp->s_prev)));
109                 out_time += susp->out_time_increment;
110                 if (++out_cnt >= max_sample_block_len) goto output_full;
111             }
112             susp->s_prev = next_value;
113             susp->s_time += susp->s_time_increment;
114         } while (--n); /* inner loop */
115   output_full:
116         /* using s_ptr_reg is a bad idea on RS/6000: */
117         susp->s_ptr += (togo - n);
118         susp_took(s_cnt, (togo - n));
119         cnt += (togo - n);
120     } /* outer loop */
121 
122     /* test for termination */
123     if (togo == 0 && out_cnt == 0) {
124         snd_list_terminate(snd_list);
125     } else {
126         snd_list->block_len = out_cnt;
127         susp->susp.current += out_cnt;
128     }
129 } /* inverse_fetch */
130 
131 
inverse_toss_fetch(snd_susp_type a_susp,snd_list_type snd_list)132 void inverse_toss_fetch(snd_susp_type a_susp, snd_list_type snd_list)
133 {
134     inverse_susp_type susp = (inverse_susp_type) a_susp;
135     int64_t final_count = MIN(susp->susp.current + max_sample_block_len,
136                               susp->susp.toss_cnt);
137     time_type final_time = susp->susp.t0 + final_count / susp->susp.sr;
138     long n;
139 
140     /* fetch samples from s up to final_time for this block of zeros */
141     while (((long) ((final_time - susp->s->t0) * susp->s->sr + 0.5)) >=
142            susp->s->current)
143         susp_get_samples(s, s_ptr, s_cnt);
144     /* convert to normal processing when we hit final_count */
145     /* we want each signal positioned at final_time */
146     if (final_count == susp->susp.toss_cnt) {
147         n = (long) ROUNDBIG((final_time - susp->s->t0) * susp->s->sr -
148                             (susp->s->current - susp->s_cnt));
149         susp->s_ptr += n;
150         susp_took(s_cnt, n);
151         susp->susp.fetch = susp->susp.keep_fetch;
152     }
153     snd_list->block_len = (short) (final_count - susp->susp.current);
154     susp->susp.current = final_count;
155     snd_list->u.next = snd_list_create((snd_susp_type) susp);
156     snd_list->block = internal_zero_block;
157 }
158 
159 
inverse_mark(snd_susp_type a_susp)160 void inverse_mark(snd_susp_type a_susp)
161 {
162     inverse_susp_type susp = (inverse_susp_type) a_susp;
163     sound_xlmark(susp->s);
164 }
165 
166 
inverse_free(snd_susp_type a_susp)167 void inverse_free(snd_susp_type a_susp)
168 {
169     inverse_susp_type susp = (inverse_susp_type) a_susp;
170     sound_unref(susp->s);
171     ffree_generic(susp, sizeof(inverse_susp_node), "inverse_free");
172 }
173 
174 
inverse_print_tree(snd_susp_type a_susp,int n)175 void inverse_print_tree(snd_susp_type a_susp, int n)
176 {
177     inverse_susp_type susp = (inverse_susp_type) a_susp;
178     indent(n);
179     stdputstr("s:");
180     sound_print_tree_1(susp->s, n);
181 }
182 
183 
snd_make_inverse(sound_type s,time_type t0,rate_type sr)184 sound_type snd_make_inverse(sound_type s, time_type t0, rate_type sr)
185 {
186     register inverse_susp_type susp;
187 
188     falloc_generic(susp, inverse_susp_node, "snd_make_inverse");
189     susp->susp.fetch = inverse_fetch;
190     susp->terminate_cnt = UNKNOWN;
191 
192     /* initialize susp state */
193     susp->susp.free = inverse_free;
194     susp->susp.sr = sr;
195     susp->susp.t0 = t0;
196     susp->susp.mark = inverse_mark;
197     susp->susp.print_tree = inverse_print_tree;
198     susp->susp.name = "inverse";
199     susp->logically_stopped = false;
200     susp->susp.log_stop_cnt = UNKNOWN; /* log stop time = term time */
201     susp->susp.current = 0;
202     susp->s = s;
203     susp->s_cnt = 0;
204     susp->s_prev = 0;
205     susp->s_time = 0;
206     susp->s_time_increment = 1 / s->sr;
207     susp->out_time_increment = 1 / (sr * s->scale);
208     susp->started = false;
209     return sound_create((snd_susp_type)susp, t0, sr, 1.0 /* scale */);
210 }
211 
212 
snd_inverse(sound_type s,time_type t0,rate_type sr)213 sound_type snd_inverse(sound_type s, time_type t0, rate_type sr)
214 {
215     sound_type s_copy = sound_copy(s);
216     return snd_make_inverse(s_copy, t0, sr);
217 }
218