static int TIMIDITY_GetSome(void *context, void *data, int bytes, SDL_bool *done) { TIMIDITY_Music *music = (TIMIDITY_Music *)context; int filled, amount, expected; if (music->stream) { filled = SDL_AudioStreamGet(music->stream, data, bytes); if (filled != 0) { return filled; } } if (!music->play_count) { /* All done */ *done = SDL_TRUE; return 0; } if (music->stream) { expected = music->buffer_size; amount = Timidity_PlaySome(music->song, music->buffer, music->buffer_size); if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) { return -1; } } else { expected = bytes; amount = Timidity_PlaySome(music->song, data, bytes); } if (amount < expected) { if (music->play_count == 1) { /* We didn't consume anything and we're done */ music->play_count = 0; } else { int play_count = -1; if (music->play_count > 0) { play_count = (music->play_count - 1); } if (TIMIDITY_Play(music, play_count) < 0) { return -1; } } } if (music->stream) { /* We'll pick it up from the stream next time around */ return 0; } else { /* We wrote output data */ return amount; } }
// stereo LRLRLR order void mixerCallback( void* userdata, Uint8* streamData, int len ) { memset( streamData, len, workingSilence ); memset( workingBuffer, 0, workingBufferSize ); if( workingBuffer == NULL ) { return; } int numSamples = ( len / WORKING_CHANNELS ) / ( ( SDL_AUDIO_MASK_BITSIZE & WORKING_FORMAT ) / 8 ); #if 0 int soundFreq = 440; // wave length float step = 1.0f / WORKING_RATE; // for testing audio output for( int s = 0; s < numSamples; ++s ) { int streamIdx = ( s * WORKING_CHANNELS ); testTimePassed += 1.0f / WORKING_RATE; float v = sinf( testTimePassed * soundFreq * M_TWO_PI_F ); workingBuffer[streamIdx] = v * 0.1f; workingBuffer[streamIdx+1] = v * 0.1f; } #else // advance each playing sound for( EntityID id = idSet_GetFirstValidID( &playingIDSet ); id != INVALID_ENTITY_ID; id = idSet_GetNextValidID( &playingIDSet, id ) ) { int i = idSet_GetIndex( id ); Sound* snd = &( playingSounds[i] ); Sample* sample = &( samples[snd->sample] ); // for right now lets assume the pitch will stay the same, when we get it working it'll just involve // changing the speed at which we move through the array bool soundDone = false; float volume = snd->volume * sbSoundGroups[snd->group].volume * masterVolume; for( int s = 0; ( s < numSamples ) && !soundDone; ++s ) { int streamIdx = ( s * WORKING_CHANNELS ); // we're assuming stereo output here if( sample->numChannels == 1 ) { float data = sample->data[(int)snd->pos] * volume; /* left */ workingBuffer[streamIdx] += data * inverseLerp( 1.0f, 0.0f, snd->pan ); /* right */ workingBuffer[streamIdx+1] += data * inverseLerp( -1.0f, 0.0f, snd->pan ); snd->pos += snd->pitch; } else { // if the sample is stereo then we ignore panning // NOTE: Pitch change doesn't work with stereo samples yet workingBuffer[streamIdx] += sample->data[(int)snd->pos] * volume; workingBuffer[streamIdx+1] += sample->data[(int)snd->pos+1] * volume; snd->pos += 2.0f; } if( snd->pos >= sample->numSamples ) { if( sample->loops ) { snd->pos -= (float)sample->numSamples; } else { soundDone = true; } } } if( soundDone ) { idSet_ReleaseID( &playingIDSet, id ); // this doesn't invalidate the id for the loop } } for( int i = 0; i < MAX_STREAMING_SOUNDS; ++i ) { if( !streamingSounds[i].playing ) continue; StreamingSound* stream = &( streamingSounds[i] ); bool soundDone = false; float volume = stream->volume * sbSoundGroups[stream->group].volume * masterVolume; // if the next buffer fill would be past what we have loaded then load some more // note: the SDL_AudioStreamAvailable return value is in bytes while( ( SDL_AudioStreamAvailable( stream->sdlStream ) < len ) && !( stream->readDone ) ) { int read = 0; int request = STREAM_READ_BUFFER_SIZE / sizeof( short ); size_t test = ARRAY_SIZE( streamReadBuffer ); // returns the number of samples stored per channel int samplesPerChannel = stb_vorbis_get_samples_short_interleaved( stream->access, stream->access->channels, streamReadBuffer, request ); read = samplesPerChannel * sizeof( short ); SDL_AudioStreamPut( stream->sdlStream, streamReadBuffer, samplesPerChannel * stream->channels * sizeof( short ) ); // reached the end of the file, are we looping? if( read != request ) { if( stream->loops ) { stb_vorbis_seek_start( stream->access ); } else { stream->readDone = true; SDL_AudioStreamFlush( stream->sdlStream ); } } } // read data from the audio stream until there is no more left or the end of the buffer to fill int bytesToStream = numSamples * stream->channels * sizeof( sbStreamWorkingBuffer[0] ); sb_Reserve( sbStreamWorkingBuffer, (size_t)bytesToStream ); int gotten = SDL_AudioStreamGet( stream->sdlStream, sbStreamWorkingBuffer, bytesToStream ); if( gotten < 0 ) { llog( LOG_ERROR, "Error reading from sdlStream: %s", SDL_GetError( ) ); stream->playing = false; continue; } else if( gotten == 0 ) { // end of stream stream->playing = false; } int samplesGotten = gotten / ( stream->channels * sizeof( sbStreamWorkingBuffer[0] ) ); for( int s = 0; s < samplesGotten; ++s ) { // then just mix those samples int streamIdx = ( s * WORKING_CHANNELS ); int workingIdx = ( s * stream->channels ); // we're assuming stereo output here if( stream->access->channels == 1 ) { float data = sbStreamWorkingBuffer[workingIdx] * volume; workingBuffer[streamIdx] += data * inverseLerp( 1.0f, 0.0f, stream->pan ); // left workingBuffer[streamIdx+1] += data * inverseLerp( -1.0f, 0.0f, stream->pan ); // right } else { // if the sample is stereo then we ignore panning workingBuffer[streamIdx] += sbStreamWorkingBuffer[workingIdx] * volume; workingBuffer[streamIdx+1] += sbStreamWorkingBuffer[workingIdx + 1] * volume; } } } #endif memcpy( streamData, workingBuffer, len ); }