Exemple #1
0
void Mixer::MixChannel(Channel& channel, uint8* data, int length)
{
	// Do not mix channel if channel is a sound and sound is disabled
	if (channel.group == MIXER_GROUP_SOUND && !gConfigSound.sound_enabled) {
		return;
	}

	if (channel.source && channel.source->Length() > 0 && !channel.done) {
		AudioFormat streamformat = channel.source->Format();
		int loaded = 0;
		SDL_AudioCVT cvt;
		cvt.len_ratio = 1;
		do {
			int samplesize = format.channels * format.BytesPerSample();
			int samples = length / samplesize;
			int samplesloaded = loaded / samplesize;
			double rate = 1;
			if (format.format == AUDIO_S16SYS) {
				rate = channel.rate;
			}
			int samplestoread = (int)((samples - samplesloaded) * rate);
			int lengthloaded = 0;
			if (channel.offset < channel.source->Length()) {
				bool mustconvert = false;
				if (MustConvert(*channel.source)) {
					if (SDL_BuildAudioCVT(&cvt, streamformat.format, streamformat.channels, streamformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) {
						break;
					}
					mustconvert = true;
				}

				const uint8* datastream = 0;
				int toread = (int)(samplestoread / cvt.len_ratio) * samplesize;
				int readfromstream = (channel.source->GetSome(channel.offset, &datastream, toread));
				if (readfromstream == 0) {
					break;
				}

				uint8* dataconverted = 0;
				const uint8* tomix = 0;

				if (mustconvert) {
					// tofix: there seems to be an issue with converting audio using SDL_ConvertAudio in the callback vs preconverted, can cause pops and static depending on sample rate and channels
					if (Convert(cvt, datastream, readfromstream, &dataconverted)) {
						tomix = dataconverted;
						lengthloaded = cvt.len_cvt;
					} else {
						break;
					}
				} else {
					tomix = datastream;
					lengthloaded = readfromstream;
				}

				bool effectbufferloaded = false;
				if (rate != 1 && format.format == AUDIO_S16SYS) {
					int in_len = (int)((double)lengthloaded / samplesize);
					int out_len = samples;
					if (!channel.resampler) {
						channel.resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0);
					}
					if (readfromstream == toread) {
						// use buffer lengths for conversion ratio so that it fits exactly
						speex_resampler_set_rate(channel.resampler, in_len, samples - samplesloaded);
					} else {
						// reached end of stream so we cant use buffer length as resampling ratio
						speex_resampler_set_rate(channel.resampler, format.freq, (int)(format.freq * (1 / rate)));
					}
					speex_resampler_process_interleaved_int(channel.resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len);
					effectbufferloaded = true;
					tomix = effectbuffer;
					lengthloaded = (out_len * samplesize);
				}

				if (channel.pan != 0.5f && format.channels == 2) {
					if (!effectbufferloaded) {
						memcpy(effectbuffer, tomix, lengthloaded);
						effectbufferloaded = true;
						tomix = effectbuffer;
					}
					switch (format.format) {
						case AUDIO_S16SYS:
							EffectPanS16(channel, (sint16*)effectbuffer, lengthloaded / samplesize);
							break;
						case AUDIO_U8:
							EffectPanU8(channel, (uint8*)effectbuffer, lengthloaded / samplesize);
							break;
					}
				}

				int mixlength = lengthloaded;
				if (loaded + mixlength > length) {
					mixlength = length - loaded;
				}

				float volumeadjust = volume;
				volumeadjust *= (gConfigSound.master_volume / 100.0f);
				switch (channel.group) {
				case MIXER_GROUP_SOUND:
					volumeadjust *= (gConfigSound.sound_volume / 100.0f);

					// Cap sound volume on title screen so music is more audible
					if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_TITLE_DEMO) {
						volumeadjust = Math::Min(volumeadjust, 0.75f);
					}
					break;
				case MIXER_GROUP_RIDE_MUSIC:
					volumeadjust *= (gConfigSound.ride_music_volume / 100.0f);
					break;
				}
				int startvolume = (int)(channel.oldvolume * volumeadjust);
				int endvolume = (int)(channel.volume * volumeadjust);
				if (channel.stopping) {
					endvolume = 0;
				}
				int mixvolume = (int)(channel.volume * volumeadjust);
				if (startvolume != endvolume) {
					// fade between volume levels to smooth out sound and minimize clicks from sudden volume changes
					if (!effectbufferloaded) {
						memcpy(effectbuffer, tomix, lengthloaded);
						effectbufferloaded = true;
						tomix = effectbuffer;
					}
					mixvolume = SDL_MIX_MAXVOLUME; // set to max since we are adjusting the volume ourselves
					int fadelength = mixlength / format.BytesPerSample();
					switch (format.format) {
						case AUDIO_S16SYS:
							EffectFadeS16((sint16*)effectbuffer, fadelength, startvolume, endvolume);
							break;
						case AUDIO_U8:
							EffectFadeU8((uint8*)effectbuffer, fadelength, startvolume, endvolume);
							break;
					}
				}

				SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, mixvolume);

				if (dataconverted) {
					delete[] dataconverted;
				}

				channel.offset += readfromstream;
			}

			loaded += lengthloaded;

			if (channel.loop != 0 && channel.offset >= channel.source->Length()) {
				if (channel.loop != -1) {
					channel.loop--;
				}
				channel.offset = 0;
			}
		} while(loaded < length && channel.loop != 0 && !channel.stopping);

		channel.oldvolume = channel.volume;
		channel.oldvolume_l = channel.volume_l;
		channel.oldvolume_r = channel.volume_r;
		if (channel.loop == 0 && channel.offset >= channel.source->Length()) {
			channel.done = true;
		}
	}
}
Exemple #2
0
void Mixer::MixChannel(Channel& channel, uint8* data, int length)
{
	if (channel.stream) {
		if (!channel.resampler) {
			channel.resampler = speex_resampler_init(format.channels, format.freq, format.freq, 0, 0);
		}
		AudioFormat channelformat = *channel.stream->Format();
		int loaded = 0;
		SDL_AudioCVT cvt;
		cvt.len_ratio = 1;
		do {
			int samplesize = format.channels * format.BytesPerSample();
			int samples = length / samplesize;
			int samplesloaded = loaded / samplesize;
			int samplestoread = (int)ceil((samples - samplesloaded) * channel.rate);
			int lengthloaded = 0;
			if (channel.offset < channel.stream->Length()) {
				bool mustconvert = false;
				if (MustConvert(*channel.stream)) {
					if (SDL_BuildAudioCVT(&cvt, channelformat.format, channelformat.channels, channelformat.freq, Mixer::format.format, Mixer::format.channels, Mixer::format.freq) == -1) {
						break;
					}
					mustconvert = true;
				}

				const uint8* datastream = 0;
				int readfromstream = (channel.stream->GetSome(channel.offset, &datastream, (int)(((samplestoread) * samplesize) / cvt.len_ratio)) / channelformat.BytesPerSample()) * channelformat.BytesPerSample();
				if (readfromstream == 0) {
					break;
				}

				int volume = channel.volume;
				uint8* dataconverted = 0;
				const uint8* tomix = 0;

				if (mustconvert) {
					if (Convert(cvt, datastream, readfromstream, &dataconverted)) {
						tomix = dataconverted;
						lengthloaded = (cvt.len_cvt / samplesize) * samplesize;
					} else {
						break;
					}
				} else {
					tomix = datastream;
					lengthloaded = readfromstream;
				}

				bool effectbufferloaded = false;

				if (channel.rate != 1 && format.format == AUDIO_S16SYS) {
					int in_len = (int)(ceil((double)lengthloaded / samplesize));
					int out_len = samples + 20; // needs some extra, otherwise resampler sometimes doesn't process all the input samples
					speex_resampler_set_rate(channel.resampler, format.freq, (int)(format.freq * (1 / channel.rate)));
					speex_resampler_process_interleaved_int(channel.resampler, (const spx_int16_t*)tomix, (spx_uint32_t*)&in_len, (spx_int16_t*)effectbuffer, (spx_uint32_t*)&out_len);
					effectbufferloaded = true;
					tomix = effectbuffer;
					lengthloaded = (out_len * samplesize);
				}

				if (channel.pan != 0.5f && format.channels == 2) {
					if (!effectbufferloaded) {
						memcpy(effectbuffer, tomix, lengthloaded);
						effectbufferloaded = true;
						tomix = effectbuffer;
					}
					switch (format.format) {
						case AUDIO_S16SYS:
							EffectPanS16(channel, (sint16*)effectbuffer, lengthloaded / samplesize);
							break;
						case AUDIO_U8:
							EffectPanU8(channel, (uint8*)effectbuffer, lengthloaded / samplesize);
						break;
					}
				}

				int mixlength = lengthloaded;
				if (loaded + mixlength > length) {
					mixlength = length - loaded;
				}

				SDL_MixAudioFormat(&data[loaded], tomix, format.format, mixlength, volume);

				if (dataconverted) {
					delete[] dataconverted;
				}

				channel.offset += readfromstream;

			}

			loaded += lengthloaded;

			if (channel.loop != 0 && channel.offset >= channel.stream->Length()) {
				if (channel.loop != -1) {
					channel.loop--;
				}
				channel.offset = 0;
			}
		} while(loaded < length && channel.loop != 0);
	}
}