void AliveRenderAudio(float * AudioStream, int StreamLength)
{
	static float tick = 0;
	static int note = 0;

	AliveAudioSoundbank * currentSoundbank = AliveAudio::m_CurrentSoundbank;

	AliveAudio::voiceListMutex.lock();
	int voiceCount = AliveAudio::m_Voices.size();
	AliveAudioVoice ** rawPointer = AliveAudio::m_Voices.data(); // Real nice speed boost here.

	for (int i = 0; i < StreamLength; i += 2)
	{
		for (int v = 0; v < voiceCount; v++)
		{
			AliveAudioVoice * voice = rawPointer[v]; // Raw pointer skips all that vector bottleneck crap

			voice->f_TrackDelay--;

			if (voice->m_UsesNoteOffDelay)
				voice->f_NoteOffDelay--;

			if (voice->m_UsesNoteOffDelay && voice->f_NoteOffDelay <= 0 && voice->b_NoteOn == true)
			{
				voice->b_NoteOn = false;
				//printf("off");
			}

			if (voice->b_Dead || voice->f_TrackDelay > 0)
				continue;

			float centerPan = voice->m_Tone->f_Pan;
			float leftPan = 1.0f;
			float rightPan = 1.0f;

			if (centerPan > 0)
			{
				leftPan = 1.0f - abs(centerPan);
			}
			if (centerPan < 0)
			{
				rightPan = 1.0f - abs(centerPan);
			}

			float s = voice->GetSample();

			float leftSample = s * leftPan;
			float rightSample = s * rightPan;

			SDL_MixAudioFormat((Uint8 *)(AudioStream + i), (const Uint8*)&leftSample, AUDIO_F32, sizeof(float), 37); // Left Channel
			SDL_MixAudioFormat((Uint8 *)(AudioStream + i + 1), (const Uint8*)&rightSample, AUDIO_F32, sizeof(float), 37); // Right Channel
		}

		AliveAudio::currentSampleIndex++;
	}
	AliveAudio::voiceListMutex.unlock();


	CleanVoices();
}
Exemple #2
0
static void AUD_Callback(void* data, Uint8* stream, int len) {
    AUD_Sound* sound = data;
    if (sound->curlen <= 0) {
        return;
    }
    int real_len = (sound->curlen > len ? len : sound->curlen);

    memset(stream, 0, len);
    SDL_MixAudioFormat(stream, sound->cur, sound->spec.format, real_len, sound->volume);
    if (real_len == len) {
        sound->cur += real_len;
        sound->curlen -= real_len;
    }
    else {
        stream += real_len;
        len -= real_len;
        if (sound->loop) {
            sound->cur = sound->buf;
            sound->curlen = sound->buflen;

            SDL_MixAudioFormat(stream, sound->cur, sound->spec.format, len, sound->volume);

            sound->cur += len;
            sound->curlen -= len;
        }
        else sound->curlen = 0;
    }
}
Exemple #3
0
/* Play some of a stream previously started with OGG_play() */
int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
{
    int mixable;

    while((len > 0) && music->playing)
    {
        if(!music->len_available)
            OGG_getsome(music);
        mixable = len;
        if(mixable > music->len_available)
            mixable = music->len_available;
        if(music->volume == MIX_MAX_VOLUME)
            SDL_memcpy(snd, music->snd_available, (size_t)mixable);
        else
        {
            SDL_MixAudioFormat(snd, music->snd_available, mixer.format,
                               (Uint32)mixable, music->volume);
        }
        music->len_available -= mixable;
        music->snd_available += mixable;
        len -= mixable;
        snd += mixable;
    }

    return len;
}
Exemple #4
0
/* Play some of a stream previously started with GME_play() */
int GME_playAudio(struct MUSIC_GME *music, Uint8 *stream, int len)
{
    if(music==NULL) return 1;
    if(music->game_emu==NULL) return 1;
    if(music->playing==-1) return 1;
    if( len<0 ) return 0;
    int srgArraySize = len/music->cvt.len_ratio;
    short buf[srgArraySize];
    int srcLen = (int)((double)(len/2)/music->cvt.len_ratio);

    char *err = (char*)gme_play( music->game_emu, srcLen, buf );
    if( err != NULL)
    {
        Mix_SetError("GAME-EMU: %s", err);
        return 0;
    }
    int dest_len = srcLen*2;

    if( music->cvt.needed ) {
        music->cvt.len = dest_len;
        music->cvt.buf = (Uint8*)buf;
        SDL_ConvertAudio(&music->cvt);
        dest_len = music->cvt.len_cvt;
    }

    if ( music->volume == MIX_MAX_VOLUME )
    {
        SDL_memcpy(stream, (Uint8*)buf, dest_len);
    } else {
        SDL_MixAudioFormat(stream, (Uint8*)buf, mixer.format, dest_len, music->volume);
    }
    return len-dest_len;
}
Exemple #5
0
static void Audio_mixer (void* udata, Uint8* buf, int size)
{
	(void)udata;
	Zero(buf, size, Uint8);
	if (!_audio.buf) {
		_audio.buf = New(size, Uint8);
		_audio.spec.samples = size;
	}

	MusicResource* music = _audio.music;
	SmackerResource* smacker = SDL_AtomicGetPtr((void**)&_audio.smacker);

	if (music) {
		MusicResource_play(music, _audio.buf, size);

		SDL_MixAudioFormat(buf, _audio.buf, AUDIO_S16LSB, size, _audio.volume);
	}
	if (smacker) {
		SmackerResource_play_audio(smacker, _audio.buf, size);

		SDL_MixAudioFormat(buf, _audio.buf, AUDIO_S16LSB, size, _audio.volume);
	}
}
Exemple #6
0
/* callback for SDL audio - calls all our other callbacks and mixes them */
static void channel_mixer_cb(void *extra, Uint8 *stream, int len)
{
    extern SDL_AudioFormat deviceFormat; // TODO: get this
    Uint8 mix[len];

	memset(stream, 0, len); /* fill base with silence */

	/* mix in every channel */
	for (i = 0; i < channel_count; i++) {
		int volume = channel_volume[i] * SDL_MIX_MAXVOLUME;
		if (volume <= 0)
			continue;
		channel_callback[i](extra, mix, len);
		SDL_MixAudioFormat(stream, mix, deviceFormat, len, volume);
	}
}
Exemple #7
0
void sdl_audio_callback (void *userdata, Uint8 * stream, int len)
{
    int i;
    int copy_amt;
    SDL_memset(stream, mixer.outputSpec.silence, len);  /* initialize buffer to silence */
    /* for each channel, mix in whatever is playing on that channel */
    for (i = 0; i < NumChannels; i++)
    {
        if (mixer.channels[i].position == NULL)
        {
            /* if no sound is playing on this channel */
            continue;           /* nothing to do for this channel */
        }

        /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */
        copy_amt = mixer.channels[i].remaining < len ? mixer.channels[i].remaining : len;

        /* mix this sound effect with the output */
        SDL_MixAudioFormat(stream, mixer.channels[i].position, mixer.outputSpec.format, copy_amt, sfx_volume * mixer.channels[i].volume);

        /* update buffer position in sound effect and the number of bytes left */
        mixer.channels[i].position += copy_amt;
        mixer.channels[i].remaining -= copy_amt;

        /* did we finish playing the sound effect ? */
        if (mixer.channels[i].remaining == 0)
        {
            if (mixer.channels[i].loops != 0)
            {
                mixer.channels[i].position = mixer.channels[i].s->buffer.get();
                mixer.channels[i].remaining = mixer.channels[i].s->length;
                if (mixer.channels[i].loops != -1) mixer.channels[i].loops--;
            } else {
                sdl_stop_channel(i);
            }
        }
    }
}
Exemple #8
0
/*
    Called from SDL's audio system.  Supplies sound input with data by mixing together all
    currently playing sound effects.
*/
void
audioCallback(void *userdata, Uint8 * stream, int len)
{
    int i;
    int copy_amt;
    SDL_memset(stream, mixer.outputSpec.silence, len);  /* initialize buffer to silence */
    /* for each channel, mix in whatever is playing on that channel */
    for (i = 0; i < NUM_CHANNELS; i++) {
        if (mixer.channels[i].position == NULL) {
            /* if no sound is playing on this channel */
            continue;           /* nothing to do for this channel */
        }

        /* copy len bytes to the buffer, unless we have fewer than len bytes remaining */
        copy_amt =
            mixer.channels[i].remaining <
            len ? mixer.channels[i].remaining : len;

        /* mix this sound effect with the output */
        SDL_MixAudioFormat(stream, mixer.channels[i].position,
                           mixer.outputSpec.format, copy_amt, 150);

        /* update buffer position in sound effect and the number of bytes left */
        mixer.channels[i].position += copy_amt;
        mixer.channels[i].remaining -= copy_amt;

        /* did we finish playing the sound effect ? */
        if (mixer.channels[i].remaining == 0) {
            mixer.channels[i].position = NULL;  /* indicates no sound playing on channel anymore */
            mixer.numSoundsPlaying--;
            if (mixer.numSoundsPlaying == 0) {
                /* if no sounds left playing, pause audio callback */
                SDL_PauseAudio(1);
            }
        }
    }
}
Exemple #9
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 #10
0
/* Mixing function */
static void mix_channels(void *udata, Uint8 *stream, int len)
{
    Uint8 *mix_input;
    int i, mixable, volume = SDL_MIX_MAXVOLUME;
    Uint32 sdl_ticks;

#if SDL_VERSION_ATLEAST(1, 3, 0)
    /* Need to initialize the stream in SDL 1.3+ */
    SDL_memset(stream, mixer.silence, len);
#endif

    /* Mix the music (must be done before the channels are added) */
    if ( music_active || (mix_music != music_mixer) ) {
        mix_music(music_data, stream, len);
    }

    /* Mix any playing channels... */
    sdl_ticks = SDL_GetTicks();
    for ( i=0; i<num_channels; ++i ) {
        if ( !mix_channel[i].paused ) {
            if ( mix_channel[i].expire > 0 && mix_channel[i].expire < sdl_ticks ) {
                /* Expiration delay for that channel is reached */
                mix_channel[i].playing = 0;
                mix_channel[i].looping = 0;
                mix_channel[i].fading = MIX_NO_FADING;
                mix_channel[i].expire = 0;
                _Mix_channel_done_playing(i);
            } else if ( mix_channel[i].fading != MIX_NO_FADING ) {
                Uint32 ticks = sdl_ticks - mix_channel[i].ticks_fade;
                if ( ticks >= mix_channel[i].fade_length ) {
                    Mix_Volume(i, mix_channel[i].fade_volume_reset); /* Restore the volume */
                    if( mix_channel[i].fading == MIX_FADING_OUT ) {
                        mix_channel[i].playing = 0;
                        mix_channel[i].looping = 0;
                        mix_channel[i].expire = 0;
                        _Mix_channel_done_playing(i);
                    }
                    mix_channel[i].fading = MIX_NO_FADING;
                } else {
                    if ( mix_channel[i].fading == MIX_FADING_OUT ) {
                        Mix_Volume(i, (mix_channel[i].fade_volume * (mix_channel[i].fade_length-ticks))
                                   / mix_channel[i].fade_length );
                    } else {
                        Mix_Volume(i, (mix_channel[i].fade_volume * ticks) / mix_channel[i].fade_length );
                    }
                }
            }
            if ( mix_channel[i].playing > 0 ) {
                int index = 0;
                int remaining = len;
                while (mix_channel[i].playing > 0 && index < len) {
                    remaining = len - index;
                    volume = (mix_channel[i].volume*mix_channel[i].chunk->volume) / MIX_MAX_VOLUME;
                    mixable = mix_channel[i].playing;
                    if ( mixable > remaining ) {
                        mixable = remaining;
                    }

                    mix_input = Mix_DoEffects(i, mix_channel[i].samples, mixable);
                    SDL_MixAudioFormat(stream+index,mix_input,mixer.format,mixable,volume);
                    if (mix_input != mix_channel[i].samples)
                        SDL_free(mix_input);

                    mix_channel[i].samples += mixable;
                    mix_channel[i].playing -= mixable;
                    index += mixable;

                    /* rcg06072001 Alert app if channel is done playing. */
                    if (!mix_channel[i].playing && !mix_channel[i].looping) {
                        _Mix_channel_done_playing(i);
                    }
                }

                /* If looping the sample and we are at its end, make sure
                   we will still return a full buffer */
                while ( mix_channel[i].looping && index < len ) {
                    int alen = mix_channel[i].chunk->alen;
                    remaining = len - index;
                    if (remaining > alen) {
                        remaining = alen;
                    }

                    mix_input = Mix_DoEffects(i, mix_channel[i].chunk->abuf, remaining);
                    SDL_MixAudioFormat(stream+index, mix_input, mixer.format, remaining, volume);
                    if (mix_input != mix_channel[i].chunk->abuf)
                        SDL_free(mix_input);

                    if (mix_channel[i].looping > 0) {
                        --mix_channel[i].looping;
                    }
                    mix_channel[i].samples = mix_channel[i].chunk->abuf + remaining;
                    mix_channel[i].playing = mix_channel[i].chunk->alen - remaining;
                    index += remaining;
                }
                if ( ! mix_channel[i].playing && mix_channel[i].looping ) {
                    if (mix_channel[i].looping > 0) {
                        --mix_channel[i].looping;
                    }
                    mix_channel[i].samples = mix_channel[i].chunk->abuf;
                    mix_channel[i].playing = mix_channel[i].chunk->alen;
                }
            }
        }
    }

    /* rcg06122001 run posteffects... */
    Mix_DoEffects(MIX_CHANNEL_POST, stream, len);

    if ( mix_postmix ) {
        mix_postmix(mix_postmix_data, stream, len);
    }
}
Exemple #11
0
void Audio::load_wav(const char* filename)
{
    if (sound_enabled)
    {
        clear_wav();

        // Load Wav File
        SDL_AudioSpec wave;
    
        uint8_t *data;
        uint32_t length;

        pause_audio();

        if( SDL_LoadWAV(filename, &wave, &data, &length) == NULL)
        {
            wavfile.loaded = 0;
            resume_audio();
            std::cout << "Could not load wav: " << filename << std::endl;
            return;
        }
        
        SDL_LockAudio();

        // Halve Volume Of Wav File
        uint8_t* data_vol = new uint8_t[length];
	SDL_MixAudioFormat(data_vol, data, wave.format, length, SDL_MIX_MAXVOLUME / 2);

        // WAV File Needs Conversion To Target Format
        if (wave.format != AUDIO_S16 || wave.channels != 2 || wave.freq != FREQ)
        {
            SDL_AudioCVT cvt;
            SDL_BuildAudioCVT(&cvt, wave.format, wave.channels, wave.freq,
                                    AUDIO_S16,   CHANNELS,      FREQ);

            cvt.buf = (uint8_t*) malloc(length*cvt.len_mult);
            memcpy(cvt.buf, data_vol, length);
            cvt.len = length;
            SDL_ConvertAudio(&cvt);
            SDL_FreeWAV(data);
            delete[] data_vol;

            wavfile.data = (int16_t*) cvt.buf;
            wavfile.length = cvt.len_cvt / 2;
            wavfile.pos = 0;
            wavfile.loaded = 1;
        }
        // No Conversion Needed
        else
        {
            SDL_FreeWAV(data);
            wavfile.data = (int16_t*) data_vol;
            wavfile.length = length / 2;
            wavfile.pos = 0;
            wavfile.loaded = 2;
        }

        resume_audio();
        SDL_UnlockAudio();
    }
}
Exemple #12
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);
	}
}