1 
2 /*
3  * The Real SoundTracker - Pseudo-mixer for sample playback parameter tracing
4  *
5  * Copyright (C) 2001-2019 Michael Krause
6  * Copyright (C) 2003 Yury Aliaev
7 
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22 
23 #include <config.h>
24 
25 #include <math.h>
26 #include <string.h>
27 
28 #include "audio.h"
29 #include "gui-settings.h"
30 #include "main.h"
31 #include "mixer.h"
32 #include "tracer.h"
33 #include "xm-player.h"
34 
35 static int num_channels, mixfreq;
36 static float fmixfreq;
37 
38 // This is an artificial limit. The code can do more channels.
39 static tracer_channel channels[32];
40 
tracer_setnumch(int n)41 void tracer_setnumch(int n)
42 {
43     g_assert(n >= 1 && n <= 32);
44 
45     num_channels = n;
46 }
47 
48 static void
tracer_updatesample(st_mixer_sample_info * si)49 tracer_updatesample(st_mixer_sample_info* si)
50 {
51     int i;
52     tracer_channel* c;
53 
54     for (i = 0; i < 32; i++) {
55         c = &channels[i];
56 
57         if (c->sample != si || !(c->flags & TR_FLAG_SAMPLE_RUNNING)) {
58             continue;
59         }
60 
61         if (c->data != si->data
62             || c->length != si->length
63             || c->looptype != si->looptype) {
64             c->flags &= ~TR_FLAG_SAMPLE_RUNNING;
65             continue;
66         }
67 
68         /* No relevant data has changed. Don't stop the sample, but update
69 	   our local loop data instead. */
70         c->looptype = si->looptype;
71         if (c->looptype != ST_MIXER_SAMPLE_LOOPTYPE_NONE) {
72             if (c->positionw < si->loopstart) {
73                 c->positionw = si->loopstart;
74                 c->positionf = 0x7fffffff;
75             } else if (c->positionw >= si->loopend) {
76                 c->positionw = si->loopend - 1;
77                 c->positionf = 0x7fffffff;
78             }
79         }
80     }
81 }
82 
83 static void
tracer_setmixfreq(guint32 frequency)84 tracer_setmixfreq(guint32 frequency)
85 {
86     mixfreq = frequency;
87     fmixfreq = 48000.0 / (float)mixfreq;
88 }
89 
90 static void
tracer_reset(void)91 tracer_reset(void)
92 {
93     memset(channels, 0, sizeof(channels));
94 }
95 
96 static void
tracer_startnote(int channel,st_mixer_sample_info * s)97 tracer_startnote(int channel,
98     st_mixer_sample_info* s)
99 {
100     tracer_channel* c = &channels[channel];
101 
102     c->flags = 0;
103 
104     c->sample = s;
105 
106     // The following three for update_sample()
107     c->data = s->data;
108     c->length = s->length;
109     c->looptype = s->looptype;
110 
111     c->positionw = 0;
112     c->positionf = 0;
113     c->playend = 0;
114     if (s->looptype == ST_MIXER_SAMPLE_LOOPTYPE_AMIGA) {
115         c->flags |= TR_FLAG_LOOP_UNIDIRECTIONAL;
116     } else if (s->looptype == ST_MIXER_SAMPLE_LOOPTYPE_PINGPONG) {
117         c->flags |= TR_FLAG_LOOP_BIDIRECTIONAL;
118     }
119     c->direction = 1;
120     c->freso = 0.0;
121     c->ffreq = fmixfreq;
122     c->filter_on = FALSE;
123     c->flags |= TR_FLAG_SAMPLE_RUNNING;
124 }
125 
126 static void
tracer_stopnote(int channel)127 tracer_stopnote(int channel)
128 {
129     tracer_channel* c = &channels[channel];
130 
131     c->flags = 0; /* Just stop the note without reverances */
132 }
133 
134 static void
tracer_setsmplpos(int channel,guint32 offset)135 tracer_setsmplpos(int channel,
136     guint32 offset)
137 {
138     tracer_channel* c = &channels[channel];
139 
140     if (c->sample && c->flags != 0) {
141         if (offset < c->sample->length) {
142             c->positionw = offset;
143             c->positionf = 0;
144             c->direction = 1;
145         } else {
146             c->flags = 0;
147         }
148     }
149 }
150 
151 static void
tracer_setsmplend(int channel,guint32 offset)152 tracer_setsmplend(int channel,
153     guint32 offset)
154 {
155     tracer_channel* c = &channels[channel];
156 
157     if (c->sample && c->flags != 0) {
158         if (c->positionw != 0 || offset < c->sample->length) {
159             // only end if the selection is not the whole sample
160             c->playend = offset;
161         }
162     }
163 }
164 
165 static void
tracer_setfreq(int channel,float frequency)166 tracer_setfreq(int channel,
167     float frequency)
168 {
169     tracer_channel* c = &channels[channel];
170 
171     frequency /= mixfreq;
172 
173     c->freqw = (guint32)floor(frequency);
174     c->freqf = (guint32)((frequency - c->freqw) * 4294967296.0);
175 }
176 
177 static void
tracer_setvolume(int channel,float volume)178 tracer_setvolume(int channel,
179     float volume)
180 {
181     tracer_channel* c = &channels[channel];
182 
183     c->volume = volume;
184 }
185 
186 static void
tracer_setpanning(int channel,float panning)187 tracer_setpanning(int channel,
188     float panning)
189 {
190     tracer_channel* c = &channels[channel];
191 
192     c->panning = 0.5 * (panning + 1.0);
193 }
194 
195 static void
tracer_setchcutoff(int channel,float freq)196 tracer_setchcutoff(int channel,
197     float freq)
198 {
199     tracer_channel* c = &channels[channel];
200 
201     if (freq < 0.0) {
202         c->ffreq = fmixfreq;
203         c->freso = 0.0;
204         c->filter_on = FALSE;
205     } else {
206         g_assert(0.0 <= freq);
207         g_assert(freq <= 1.0);
208         c->ffreq = freq * fmixfreq;
209         c->filter_on = TRUE;
210     }
211 }
212 
213 static void
tracer_setchreso(int channel,float reso)214 tracer_setchreso(int channel,
215     float reso)
216 {
217     tracer_channel* c = &channels[channel];
218 
219     g_assert(0.0 <= reso);
220     g_assert(reso <= 1.0);
221     c->freso = reso;
222 }
223 
224 static guint32
tracer_mix_sub(tracer_channel * ch,guint32 num_samples_left)225 tracer_mix_sub(tracer_channel* ch,
226     guint32 num_samples_left)
227 {
228     const gboolean loopit = (ch->playend == 0) && (ch->flags & (TR_FLAG_LOOP_UNIDIRECTIONAL | TR_FLAG_LOOP_BIDIRECTIONAL));
229     const gboolean gonnapingpong = loopit && (ch->flags & TR_FLAG_LOOP_BIDIRECTIONAL);
230 
231     const gint64 lstart64 = ((guint64)ch->sample->loopstart) << 32;
232     const gint64 freq64 = (((guint64)ch->freqw) << 32) + (guint64)ch->freqf;
233     gint64 pos64 = ((guint64)(ch->positionw) << 32) + (guint64)ch->positionf;
234     const gint32 ende = (ch->playend != 0) ? (ch->playend) : (loopit ? ch->sample->loopend : ch->length);
235     const gint64 ende64 = (guint64)ende << 32;
236 
237     gint64 vieweit64;
238     guint32 num_samples;
239 
240     if (ch->direction == 1)
241         vieweit64 = pos64 + freq64 * num_samples_left;
242     else
243         vieweit64 = pos64 - freq64 * num_samples_left;
244 
245     if ((ch->direction == 1 && vieweit64 < ende64) || (ch->direction == -1 && vieweit64 > lstart64)) {
246         /* Normal conditions; we are far from loop boundaries and sample end */
247         ch->positionw = vieweit64 >> 32;
248         ch->positionf = vieweit64 & 0xffffffff;
249 
250         return num_samples_left;
251     }
252 
253     /* If we've reached this point, this means we are close to any `critical point' of the sample */
254     if (!loopit) {
255         /* Sample without loop just stopped; we don't care about it more */
256         ch->flags = 0;
257 
258         return 0;
259     }
260 
261     if (!gonnapingpong) {
262         /* End of Amiga-type loop */
263         num_samples = (ende64 - pos64) / freq64 + 1; /* plus one sample from the next loop cycle */
264         pos64 += num_samples * freq64 - ende64 + lstart64;
265 
266         ch->positionw = pos64 >> 32;
267         ch->positionf = pos64 & 0xffffffff;
268 
269         g_assert(num_samples <= num_samples_left);
270         return num_samples;
271     }
272 
273     if (ch->direction == 1) {
274         /* Reflection at the end of pingpong loop */
275         num_samples = (ende64 - pos64) / freq64 + 1; /* plus one sample from the next loop cycle */
276         pos64 += num_samples * freq64;
277         pos64 = ende64 - (pos64 - ende64);
278 
279         ch->positionw = pos64 >> 32;
280         ch->positionf = pos64 & 0xffffffff;
281         ch->direction = -1;
282 
283         g_assert(num_samples <= num_samples_left);
284         return num_samples;
285     }
286 
287     /* The same but at the start... */
288     num_samples = (pos64 - lstart64) / freq64 + 1; /* plus one sample from the next loop cycle */
289     pos64 -= num_samples * freq64;
290     pos64 = lstart64 - (pos64 - lstart64);
291 
292     ch->positionw = pos64 >> 32;
293     ch->positionf = pos64 & 0xffffffff;
294     ch->direction = 1;
295 
296     g_assert(num_samples <= num_samples_left);
297     return num_samples;
298 }
299 
300 static void*
tracer_mix(void * dest,guint32 count,gint16 * scopebufs[],int scopebufs_offset)301 tracer_mix(void* dest, guint32 count, gint16* scopebufs[], int scopebufs_offset)
302 {
303     int chnr;
304 
305     for (chnr = 0; chnr < num_channels; chnr++) {
306         tracer_channel* ch = channels + chnr;
307         int num_samples_left = count;
308 
309         if (!((ch->flags & TR_FLAG_SAMPLE_RUNNING) && (gui_settings.permanent_channels & (1 << chnr))))
310             continue;
311 
312         g_mutex_lock(&ch->sample->lock);
313 
314         while (num_samples_left && (ch->flags & TR_FLAG_SAMPLE_RUNNING)) {
315             int num_samples = 0;
316 
317             num_samples = tracer_mix_sub(ch, num_samples_left);
318 
319             num_samples_left -= num_samples;
320         }
321 
322         g_mutex_unlock(&ch->sample->lock);
323     }
324     return NULL;
325 }
326 
327 static st_mixer mixer_tracer = {
328     "tracer",
329     "Pseudo-mixer for channel settings tracing", /* It will NEVER be used and hence translated */
330 
331     tracer_setnumch,
332     tracer_updatesample,
333     NULL,
334     NULL,
335     tracer_setmixfreq,
336     NULL,
337     NULL,
338     tracer_reset,
339     tracer_startnote,
340     tracer_stopnote,
341     tracer_setsmplpos,
342     tracer_setsmplend,
343     tracer_setfreq,
344     tracer_setvolume,
345     tracer_setpanning,
346     tracer_setchcutoff,
347     tracer_setchreso,
348     tracer_mix,
349     NULL,
350     NULL,
351 
352     0x7fffffff,
353 
354     NULL
355 };
356 
tracer_trace(int mixfreq,int songpos,int patpos)357 void tracer_trace(int mixfreq, int songpos, int patpos)
358 {
359     /* Attemp to take pitchband into account */
360     /* Test if tempo and BPM are traced */
361     st_mixer* real_mixer = mixer;
362     mixer = &mixer_tracer;
363 
364     int stopsongpos = songpos;
365     int stoppatpos = patpos;
366 
367     double rest = 0, previous = 0; /* Fractional part of the samples */
368 
369     if ((stoppatpos -= 1) < 0) {
370         stopsongpos -= 1;
371         stoppatpos = xm->patterns[xm->pattern_order_table[stopsongpos]].length - 1;
372     }
373 
374     tracer_setmixfreq(mixfreq);
375     tracer_reset();
376 
377     while (1) {
378         double t;
379 
380         double current = xmplayer_play();
381         t = current - previous + rest;
382         previous = current;
383 
384         guint32 samples = t * mixfreq;
385         rest = t - (double)samples / (double)mixfreq;
386 
387         tracer_mix(NULL, samples, NULL, 0);
388         if (player_songpos > stopsongpos || (player_songpos == stopsongpos && player_patpos > stoppatpos) || (player_songpos == stopsongpos && player_patpos == stoppatpos && curtick >= player_tempo - 1))
389             break; //? maybe player_patpos - 1
390     }
391 
392     mixer = real_mixer;
393 }
394 
395 tracer_channel*
tracer_return_channel(int n)396 tracer_return_channel(int n)
397 {
398     g_assert(n < num_channels);
399 
400     return &channels[n];
401 }
402