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