Mixer loop optimization?

Anyone knows if these loops can be optimized?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#define C_SAMPLEPOS(channel) (channel->sound.position)
#define C_BUFFERSIZE(channel) (channel->sound.numsamples)
#define C_BUFFERINC(channel) (channel->bufferinc)
//Use precalculated sample positions!
//#define C_SAMPLERATE(channel,position) (convert_samplerate[soundchannels[(channel)].samplerate][(position)])
#define C_SAMPLERATE(channel,position) (uint_32)(channel->convert_samplerate*((float)(position)))
#define C_STEREO(channel) (channel->stereo)
#define C_GETSAMPLEPOS(channel,rchannel,time) (samplepos[C_STEREO(channel)][(((word)time)<<1)|(rchannel)])
#define C_VOLUMEPERCENT(channel) (channel->volume_percent)
#define C_SAMPLE(channel,samplepos) getsample(channel,samplepos)

uint_32 

static OPTINLINE void mixchannels(sample_stereo_t *res) //Mixes the channels with each other at a specific time!
{
	//Process multichannel!
	#ifdef DEBUG_SOUNDBUFFER
	byte buffering; //We're buffered?
	#endif
	int_32 result_l, result_r; //The result!
	uint_32 n,  relsample=0; //Current channel and relative sample!
	word channelsleft; //Reversed of above!
	//Channel specific data
	float volume;
	sample_t sample_l, sample_r;
	uint_32 currentpos, bufferinc; //Current sample pos, buffered samples!
	playing_p currentchannel; //Current sound channel!
	
	//First, initialise our variables!
	channelsleft = soundchannels_used; //Ammount of channels left!
	if (!channelsleft || __HW_DISABLED) //No channels to mix or nothing to process?
	{
		res->l = res->r = 0.0f; //Nothin to process!
		return; //Done!
	}
	#ifdef DEBUG_SOUNDBUFFER
	buffering = 0; //initialise buffering flag!
	#endif
	//Result and active counters!
	result_l = result_r = 0; //Init to standard sum!
	n = 0; //Current channel number counter and active channel counter!
	
	//Now process all channels!
	nextmixing: //Mix the next channel!
		currentchannel = &soundchannels[n]; //Current sound channel!
		if (currentchannel->soundhandler) //Active?
		{
			if (currentchannel->samplerate && memprotect(currentchannel->sound.samples,currentchannel->sound.length,"SW_Samples") && samplepos[currentchannel->stereo] /*&& convert_samplerate[currentchannel->samplerate]*/) //Allocated all neccesary channel data?
			{
				currentpos = C_SAMPLEPOS(currentchannel); //Load the current position!
				if (currentchannel->init) //First sample to run?
				{
					currentpos = 0; //Reset samplepos!
					relsample = 0; //Reset relative sample!
					#ifdef DEBUG_SOUNDBUFFER
					dolog("soundservice","Initialising sound buffer...");
					dolog("soundservice","Buffering %i @ 0/%i samples; extra data: %p; name: %s",n,C_BUFFERSIZE(currentchannel),currentchannel->extradata,currentchannel->name);
					#endif
					//Buffer and update buffer position!
					currentchannel->bufferflags = currentchannel->soundhandler(currentchannel->sound.samples,C_BUFFERSIZE(currentchannel),C_STEREO(currentchannel),currentchannel->extradata); // Request next sample for this channel, also give our channel extra information!
					currentchannel->init = 0; //Not initialised!
				}
				else //Not initialised? We're a running channel!
				{
					bufferinc = C_BUFFERINC(currentchannel); //Load buffer increase rate!
					relsample = C_SAMPLERATE(currentchannel,currentpos); //Get the sample position of the destination samplerate!
					rebuffer: //Rebuffer check!
					if (relsample>=C_BUFFERSIZE(currentchannel)) //Expired, we've reached the end of the buffer (sample overflow)?
					{
						#ifdef DEBUG_SOUNDBUFFER
						buffering = 1; //We're buffering!
						dolog("soundservice","Buffering %i @ %i/%i samples; extra data: %p; name: %s",n,relsample,C_BUFFERSIZE(currentchannel),currentchannel->extradata,currentchannel->name);
						#endif
						//Buffer and update buffer position!
						currentchannel->bufferflags = currentchannel->soundhandler(currentchannel->sound.samples,C_BUFFERSIZE(currentchannel),C_STEREO(currentchannel),currentchannel->extradata); // Request next sample for this channel, also give our channel extra information!
						currentpos -= bufferinc; //Reset position in the next frame!
						relsample = C_SAMPLERATE(currentchannel,currentpos); //Get the sample rate for the new buffer!
						goto rebuffer; //Rebuffer if needed!
					} //Don't buffer!
					#ifdef DEBUG_SOUNDBUFFER
					if (buffering) //We were buffering?
					{
						buffering = 0;
						dolog("soundservice","Buffer ready. Mixing...");
					}
					#endif
				}

				//Now process the buffered sound itself!
				if (currentchannel->bufferflags&1) //Do we even got something buffered?
				{
					volume = C_VOLUMEPERCENT(currentchannel); //Retrieve the current volume!
					sample_l = C_SAMPLE(currentchannel,C_GETSAMPLEPOS(currentchannel,0,relsample)); //The composed sample, based on the relative position!
					sample_r = C_SAMPLE(currentchannel,C_GETSAMPLEPOS(currentchannel,1,relsample)); //The composed sample, based on the relative position!
					
					//Apply the channel volume!
					sample_l *= volume;
					sample_r *= volume;
					
					//Now we have the correct left and right channel data on our native samplerate.
					
					//Next, add the data to the mixer!
					result_l += sample_l; //Mix the channels equally together based on volume!
					result_r += sample_r; //See above!
				}

				//Finish up: update the values to be updated!
				++currentpos; //Next position on each channel!
				C_SAMPLEPOS(currentchannel) = currentpos; //Store the current position for next usage!
			}
		}
		if (--channelsleft) //Channels left?
		{
			++n; //Next channel!
			goto nextmixing; //Go and mix the next channel!
		}
	//result_l/r=a+b+...
	//multi_l/r=a*b*...
	//z=a+b-(ab/USHRT_MAX) => z=result_l/r-(multi_l/r / USHRT_MAX)
	
	//Apply hard clipping!
	result_l = (result_l>SHRT_MAX)?SHRT_MAX:((result_l<SHRT_MIN)?SHRT_MIN:result_l);
	result_r = (result_r>SHRT_MAX)?SHRT_MAX:((result_r<SHRT_MIN)?SHRT_MIN:result_r);

	res->l = (sample_t)result_l; //Left channel!
	res->r = (sample_t)result_r; //Right channel!
}

void SDL_AudioCallback(void *user_data, Uint8 *audio, int length)
{
	if (__HW_DISABLED) return; //Disabled?
	/* Clear the audio buffer so we can mix samples into it. */

	//Now, mix all channels!
	sample_stereo_t* ubuf = (sample_stereo_t*) audio; //Buffer!
	int i = 0;
	word reallength = length/sizeof(ubuf[0]); //Total length!
	for (;;) { //Process all samples needed!
		mixchannels(&ubuf[i]); //Fill both channels!
		if (!--reallength) break; //Next while left!
		++i; //Next item!
	}
	uint_64 mspassed = getmspassed(&ticks); //Load the time passed!
	totaltime_audio += mspassed; //Total time!
	++totaltimes_audio; //Total times increase!
	totaltime_audio_avg = (uint_32)SAFEDIV(totaltime_audio,totaltimes_audio); //Recalculate AVG audio time!
}


Should I change the inner loop (checking active channels and if they're valid) to the outer loop?

So the outer loop checks if the channel is valid for use,
The inner loop checks and increases the current relsample
The inner loop adds the sample to the mixer.

When the outer loop finishes:
The outer loop clips all samples.

Would this be faster than the current method? (so instead of a:
for (sample=0;sample<4096;sample++){for (channel=0;channel<66;channel++){/* Process channel here */}}

We get:
for (channel=0;channel<66;channel++){for (sample=0;sample<4096;sample++){/* Process sample */}})
I've worked on it a bit:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//Simple macros for checking samples!
//Precalcs handling!
#define C_SAMPLEPOS(channel) (channel->sound.position)
#define C_BUFFERSIZE(channel) (channel->sound.numsamples)
#define C_BUFFERINC(channel) (channel->bufferinc)
//Use precalculated sample positions!
//#define C_SAMPLERATE(channel,position) (convert_samplerate[soundchannels[(channel)].samplerate][(position)])
#define C_SAMPLERATE(channel,position) (uint_32)(channel->convert_samplerate*((float)(position)))
#define C_STEREO(channel) (channel->stereo)
#define C_GETSAMPLEPOS(channel,rchannel,time) (samplepos[C_STEREO(channel)][(((word)time)<<1)|(rchannel)])
#define C_VOLUMEPERCENT(channel) (channel->volume_percent)
#define C_SAMPLE(channel,samplepos) getsample(channel,samplepos)

static OPTINLINE void mixchannel(playing_p currentchannel, int_32 *result_l, int_32 *result_r) //Mixes the channels with each other at a specific time!
{
	//Process multichannel!
	uint_32 relsample; //Current channel and relative sample!
	//Channel specific data
	uint_32 currentpos, bufferinc; //Current sample pos, buffered samples!
	
	//First, initialise our variables!
	#ifdef DEBUG_SOUNDBUFFER
	buffering = 0; //initialise buffering flag!
	#endif
	
	//First step: buffering if needed and keep our buffer!
	currentpos = C_SAMPLEPOS(currentchannel); //Load the current position!
	if (currentchannel->init) //First sample to run?
	{
		currentpos = 0; //Reset samplepos!
		relsample = 0; //Reset relative sample!
		#ifdef DEBUG_SOUNDBUFFER
		dolog("soundservice","Initialising sound buffer...");
		dolog("soundservice","Buffering %i @ 0/%i samples; extra data: %p; name: %s",n,C_BUFFERSIZE(currentchannel),currentchannel->extradata,currentchannel->name);
		#endif
		//Buffer and update buffer position!
		currentchannel->bufferflags = currentchannel->soundhandler(currentchannel->sound.samples,C_BUFFERSIZE(currentchannel),C_STEREO(currentchannel),currentchannel->extradata); // Request next sample for this channel, also give our channel extra information!
		currentchannel->init = 0; //Not initialised!
	}
	else //Not initialised? We're a running channel!
	{
		#ifdef DEBUG_SOUNDBUFFER
		byte buffering = 0; //We're buffered?
		#endif
		bufferinc = C_BUFFERINC(currentchannel); //Load buffer increase rate!
		relsample = C_SAMPLERATE(currentchannel,currentpos); //Get the sample position of the destination samplerate!
		rebuffer: //Rebuffer check!
		if (relsample>=C_BUFFERSIZE(currentchannel)) //Expired, we've reached the end of the buffer (sample overflow)?
		{
			#ifdef DEBUG_SOUNDBUFFER
			buffering = 1; //We're buffering!
			dolog("soundservice","Buffering %i @ %i/%i samples; extra data: %p; name: %s",n,relsample,C_BUFFERSIZE(currentchannel),currentchannel->extradata,currentchannel->name);
			#endif
			//Buffer and update buffer position!
			currentchannel->bufferflags = currentchannel->soundhandler(currentchannel->sound.samples,C_BUFFERSIZE(currentchannel),C_STEREO(currentchannel),currentchannel->extradata); // Request next sample for this channel, also give our channel extra information!
			currentpos -= bufferinc; //Reset position in the next frame!
			relsample = C_SAMPLERATE(currentchannel,currentpos); //Get the sample rate for the new buffer!
			goto rebuffer; //Rebuffer if needed!
		} //Don't buffer!
		#ifdef DEBUG_SOUNDBUFFER
		if (buffering) //We were buffering?
		{
			buffering = 0;
			dolog("soundservice","Buffer ready. Mixing...");
		}
		#endif
	}

	//Second step: Process the buffered sound itself!
	if (currentchannel->bufferflags&1) //Do we even got something buffered?
	{
		float volume = C_VOLUMEPERCENT(currentchannel); //Retrieve the current volume!
		int_32 sample_l = C_SAMPLE(currentchannel,C_GETSAMPLEPOS(currentchannel,0,relsample)); //The composed sample, based on the relative position!
		int_32 sample_r = C_SAMPLE(currentchannel,C_GETSAMPLEPOS(currentchannel,1,relsample)); //The composed sample, based on the relative position!
		
		//Apply the channel volume!
		sample_l *= volume;
		sample_r *= volume;
		
		//Now we have the correct left and right channel data on our native samplerate.
		
		//Next, add the data to the mixer!
		*result_l += sample_l; //Mix the channels equally together based on volume!
		*result_r += sample_r; //See above!
	} //Mix!

	//Finish up: update the values to be updated!
	++currentpos; //Next position on each channel!
	C_SAMPLEPOS(currentchannel) = currentpos; //Store the current position for next usage!
}

int_32 mixedsamples[SAMPLESIZE*2]; //All mixed samples buffer!

static OPTINLINE void mixaudio(sample_stereo_p buffer, uint_32 length) //Mix audio channels to buffer!
{
	if (!length) return; //Abort without length!
	//Current data numbers
	uint_32 currentsample;
	uint_32 channelsleft = soundchannels_used; //The ammount of channels to mix!
	//Active data
	playing_p activechannel; //Current channel!
	int_32 *firstactivesample;
	int_32 *activesample;
	memset(&mixedsamples,0,sizeof(mixedsamples)); //Init mixed samples, stereo!
	if (channelsleft)
	{
		activechannel = &soundchannels[0]; //Lookup the first channel!
		for (;;) //Mix the next channel!
		{
			if (activechannel->soundhandler) //Active?
			{
				if (activechannel->samplerate &&
					memprotect(activechannel->sound.samples,activechannel->sound.length,"SW_Samples")) //Allocated all neccesary channel data?
				{
					currentsample = 0;
					activesample = &mixedsamples[0]; //Init active sample to the first sample!
					for (;currentsample<length;) //Process all samples!
					{
						firstactivesample = activesample; //First channel sample!
						++activesample; //Next sample!
						mixchannel(activechannel,firstactivesample,activesample); //L&R channel!
						++currentsample; //Next sample!
						++activesample; //Next sample in our buffer!
					}
				}
			}
			if (!--channelsleft) break; //Stop when no channels left!
			++activechannel; //Next channel!
		}
	} //Got channels?

	 //Process all generated samples to output!
	currentsample = 0; //Init sample!
	int_32 result_l, result_r; //Sample buffer!
	activesample = &mixedsamples[0]; //Initialise the mixed samples position!
	for (;;)
	{
		result_l = *activesample; //L channel!
		++activesample; //Next sample!
		result_r = *activesample; //R channel!
		++activesample; //Next sample!
		if (result_l>SHRT_MAX) result_l = SHRT_MAX;
		if (result_l<SHRT_MIN) result_l = SHRT_MIN;
		if (result_r>SHRT_MAX) result_r = SHRT_MAX;
		if (result_r<SHRT_MIN) result_r = SHRT_MIN;

		buffer->l = (sample_t)result_l; //Left channel!
		buffer->r = (sample_t)result_r; //Right channel!
		if (++currentsample>length) break; //Finished!
		++buffer; //Next sample in the result!
	}
}


Also I seem to notice that a minimum of 64 samples buffered in SDL is required? Anyone knows if this is correct? (So the audio callback needs to buffer at least 64 samples each call to work correctly)
Topic archived. No new replies allowed.