pimp_mixer.c 6.57 KB
Newer Older
1
/* pimp_mixer.c -- High level mixer code
Erik Faye-Lund's avatar
Erik Faye-Lund committed
2
 * Copyright (C) 2005-2006 Jørn Nystad and Erik Faye-Lund
3
4
5
 * For conditions of distribution and use, see copyright notice in LICENSE.TXT
 */

6
7
8
9
#ifdef TARGET_GBA
#include <gba.h>
#endif

Erik Faye-Lund's avatar
Erik Faye-Lund committed
10
#include "pimp_mixer.h"
11
#include "pimp_debug.h"
12

Erik Faye-Lund's avatar
Erik Faye-Lund committed
13
void pimp_mixer_reset(struct pimp_mixer *mixer)
Erik Faye-Lund's avatar
Erik Faye-Lund committed
14
15
16
17
{
	u32 c;
	ASSERT(mixer != NULL);
	
18
	for (c = 0; c < PIMP_CHANNEL_COUNT; ++c)
Erik Faye-Lund's avatar
Erik Faye-Lund committed
19
20
21
22
23
24
25
	{
		mixer->channels[c].sample_data = 0;
		mixer->channels[c].sample_cursor = 0;
		mixer->channels[c].sample_cursor_delta = 0;
	}
}

Erik Faye-Lund's avatar
Erik Faye-Lund committed
26
27
28
29
#define CALC_LOOP_EVENT

#ifndef CALC_LOOP_EVENT

Erik Faye-Lund's avatar
Erik Faye-Lund committed
30
PURE int pimp_linear_search_loop_event(int event_cursor, int event_delta, const int max_samples)
31
32
33
34
35
36
37
38
39
40
41
42
{
	int i;
	for (i = 0; i < max_samples; ++i)
	{
		event_cursor -= event_delta;
		if (event_cursor <= 0)
		{
			return i + 1;
		}
	}
	return -1;
}
Erik Faye-Lund's avatar
Erik Faye-Lund committed
43

Erik Faye-Lund's avatar
Erik Faye-Lund committed
44
45
#else /* CALC_LOOP_EVENT */

Erik Faye-Lund's avatar
Erik Faye-Lund committed
46
PURE int pimp_calc_loop_event(int event_cursor, int event_delta, const int max_samples)
47
48
49
50
{
	int result;
	if (event_cursor == 0) return 1;
	if ((event_cursor - event_delta * max_samples) > 0) return -1;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
51
	
52
#ifdef TARGET_GBA
53
54
55
56
57
	{
		int number = event_cursor + event_delta - 1;
		int denom = event_delta;
		
		/* call BIOS-function to divide, as it's fast enough for our needs. */
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
		result = Div(number, denom);
//		asm (
//			"mov r0, %[number] \n"
//			"mov r1, %[denom]  \n"
//#ifdef __thumb__
//			"swi 0x6           \n"
//#else
//			"swi 0x6 << 16     \n"
//#endif
//			"mov %[result], r0 \n"
//			: [result] "=r" (result) /* output */
//			: /* inputs */
//				[number] "r" (number),
//				[denom] "r" (denom)
//			: "r0", "r1", "r2", "r3" /* clobbers */
//		);
74
	}
75
76
77
#else
	result = (event_cursor + event_delta - 1) / event_delta;
#endif
Erik Faye-Lund's avatar
Erik Faye-Lund committed
78
	
79
80
81
82
	ASSERT(result <= max_samples);
	return result;
}

Erik Faye-Lund's avatar
Erik Faye-Lund committed
83
84
#endif

85
/* returns the number of samples that can be mixed before a loop event occurs */
Erik Faye-Lund's avatar
Erik Faye-Lund committed
86
PURE int pimp_mixer_detect_loop_event(const struct pimp_mixer_channel_state *chan, const int max_samples)
87
{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
88
89
	int event_delta, event_cursor;
	
Erik Faye-Lund's avatar
Erik Faye-Lund committed
90
	ASSERT(max_samples > 0);
91
	ASSERT(NULL != chan);
92
	
Erik Faye-Lund's avatar
Erik Faye-Lund committed
93
	switch (chan->loop_type)
94
	{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
95
		case LOOP_TYPE_NONE:
96
97
98
			/* event is end of sample */
			event_delta = chan->sample_cursor_delta;
			event_cursor = (chan->sample_length << 12) - chan->sample_cursor;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
99
100
101
		break;
		
		case LOOP_TYPE_FORWARD:
102
103
104
			/* event is loop end */
			event_delta = chan->sample_cursor_delta;
			event_cursor = (chan->loop_end << 12) - chan->sample_cursor;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
105
106
107
108
		break;
		
		case LOOP_TYPE_PINGPONG:
			if (chan->sample_cursor_delta >= 0)
109
			{
110
				/* moving forwards through the sample */
111
112
113
				/* event is loop end */
				event_delta = chan->sample_cursor_delta;
				event_cursor = (chan->loop_end << 12) - chan->sample_cursor;
114
			}
Erik Faye-Lund's avatar
Erik Faye-Lund committed
115
116
			else
			{
117
				/* moving backwards through the sample */
118
119
120
				/* event is loop start */
				event_delta = -chan->sample_cursor_delta;
				event_cursor = -((chan->loop_start << 12) - chan->sample_cursor - 1);
Erik Faye-Lund's avatar
Erik Faye-Lund committed
121
122
			}
		break;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
123
124
125
126
		default:
			/* should never happen */
			ASSERT(FALSE);
			return 0;
127
128
	}
	
129
	ASSERT((event_cursor > 0) && (event_delta > 0));
130
	
Erik Faye-Lund's avatar
Erik Faye-Lund committed
131
#ifndef CALC_LOOP_EVENT
132
	return pimp_linear_search_loop_event(event_cursor, event_delta, max_samples);
Erik Faye-Lund's avatar
Erik Faye-Lund committed
133
134
#else
	return pimp_calc_loop_event(event_cursor, event_delta, max_samples);
135
#endif
136
137
}

138
/* returns false if we hit sample-end */
Erik Faye-Lund's avatar
Erik Faye-Lund committed
139
STATIC BOOL pimp_process_loop_event(struct pimp_mixer_channel_state *chan)
140
{
141
	ASSERT(NULL != chan);
Erik Faye-Lund's avatar
Erik Faye-Lund committed
142
	switch (chan->loop_type)
143
	{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
144
		case LOOP_TYPE_NONE: return FALSE;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
145
146
147
		
		case LOOP_TYPE_FORWARD:
			do
148
			{
149
				ASSERT(chan->sample_cursor >= (chan->loop_end << 12)); /* we should be positioned AT or BEYOND event cursor when responding to event */
Erik Faye-Lund's avatar
Erik Faye-Lund committed
150
				chan->sample_cursor -= (chan->loop_end - chan->loop_start) << 12;
151
			}
Erik Faye-Lund's avatar
Erik Faye-Lund committed
152
153
154
155
156
			while (chan->sample_cursor >= (chan->loop_end << 12));
		break;
		
		case LOOP_TYPE_PINGPONG:
			do
157
			{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
158
159
				if (chan->sample_cursor_delta >= 0)
				{
160
					ASSERT(chan->sample_cursor >= (chan->loop_end << 12)); /* we should be positioned AT or BEYOND event cursor when responding to event */
Erik Faye-Lund's avatar
Erik Faye-Lund committed
161
					chan->sample_cursor -=  chan->loop_end << 12;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
162
					chan->sample_cursor  = -chan->sample_cursor;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
163
164
165
					chan->sample_cursor +=  chan->loop_end << 12;
					ASSERT(chan->sample_cursor > 0);
					chan->sample_cursor -= 1;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
166
167
168
				}
				else
				{
169
					ASSERT(chan->sample_cursor >= (chan->loop_start << 12)); /* we should be positioned AT or BEYOND event cursor when responding to event */
170
					chan->sample_cursor -=  (chan->loop_start) << 12;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
171
					chan->sample_cursor  = -chan->sample_cursor;
172
					chan->sample_cursor +=  (chan->loop_start) << 12;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
173
174
					ASSERT(chan->sample_cursor > (chan->loop_start << 12));
					chan->sample_cursor -= 1;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
175
176
				}
				chan->sample_cursor_delta = -chan->sample_cursor_delta;
177
			}
Erik Faye-Lund's avatar
Erik Faye-Lund committed
178
179
			while ((chan->sample_cursor > (chan->loop_end << 12)) || (chan->sample_cursor < (chan->loop_start << 12)));
		break;
180
		default: ASSERT(FALSE); /* should never happen */
181
	}
182
	
Erik Faye-Lund's avatar
Erik Faye-Lund committed
183
	return TRUE;
184
185
}

Erik Faye-Lund's avatar
Erik Faye-Lund committed
186
void pimp_mixer_mix_channel(struct pimp_mixer_channel_state *chan, s32 *target, u32 samples)
Erik Faye-Lund's avatar
Erik Faye-Lund committed
187
{
188
	ASSERT(NULL != target);
189
190
	ASSERT(NULL != chan);
	
191
	while (samples > 0)
192
	{
193
		int safe_samples = pimp_mixer_detect_loop_event(chan, samples);
Erik Faye-Lund's avatar
Erik Faye-Lund committed
194
195
		int mix_samples = safe_samples;
		
196
197
198
199
		ASSERT(samples >= safe_samples);
		
		if (safe_samples < 0) mix_samples = samples;
		
200
		pimp_mixer_mix_samples(target, mix_samples, chan->sample_data, chan->volume, chan->sample_cursor, chan->sample_cursor_delta);
201
		chan->sample_cursor = chan->sample_cursor + chan->sample_cursor_delta * mix_samples;
Erik Faye-Lund's avatar
Erik Faye-Lund committed
202
		
203
		if (safe_samples < 0) break; /* done. */
204
		
205
206
		target  += safe_samples; /* move target pointer */
		samples -= safe_samples;
207
		
208
		if (FALSE == pimp_process_loop_event(chan))
209
		{
210
			/* the sample has stopped, we need to fill the rest of the buffer with the dc-offset, so it doesn't ruin our unsigned mixing-thing */
211
212
			while (samples--)
			{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
213
				*target++ += chan->volume * 128;
214
			}
215
			
216
			/* terminate sample */
Erik Faye-Lund's avatar
Erik Faye-Lund committed
217
			chan->sample_data = 0;
218
			
219
			return;
220
		}
221
222
223
		
		/* check that process_loop_event() didn't put us outside the sample */
		ASSERT((chan->sample_cursor >> 12) < chan->sample_length);
224
		ASSERT((chan->sample_cursor >> 12) >= 0);
225
226
227
	}
}

Erik Faye-Lund's avatar
Erik Faye-Lund committed
228
void pimp_mixer_mix(struct pimp_mixer *mixer, s8 *target, int samples)
229
{
230
	u32 c;
231
232
233
	int dc_offs;
	
	ASSERT(NULL != mixer);
Erik Faye-Lund's avatar
Erik Faye-Lund committed
234
	ASSERT(NULL != mixer->mix_buffer);
235
	ASSERT(NULL != target);
236
	ASSERT(samples >= 0);
237

238
	pimp_mixer_clear(mixer->mix_buffer, samples);
239
	
240
	dc_offs = 0;
241
	for (c = 0; c < PIMP_CHANNEL_COUNT; ++c)
242
	{
Erik Faye-Lund's avatar
Erik Faye-Lund committed
243
		struct pimp_mixer_channel_state *chan = &mixer->channels[c];
244
		if ((NULL != chan->sample_data) && (chan->volume > 0))
245
		{
246
			pimp_mixer_mix_channel(chan, mixer->mix_buffer, samples);
247
248
			dc_offs += chan->volume * 128;
		}
249
	}
250
	
251
252
	dc_offs >>= 8;
	
253
	pimp_mixer_clip_samples(target, mixer->mix_buffer, samples, dc_offs);
254
}