ga_SampleSource* gau_sample_source_create_stream(ga_StreamManager* in_mgr, ga_SampleSource* in_sampleSrc, gc_int32 in_bufferSamples) { gau_SampleSourceStream* ret = gcX_ops->allocFunc(sizeof(gau_SampleSourceStream)); gau_SampleSourceStreamContext* ctx = &ret->context; gc_int32 sampleSize; ga_BufferedStream* stream; ga_sample_source_init(&ret->sampleSrc); ga_sample_source_format(in_sampleSrc, &ret->sampleSrc.format); sampleSize = ga_format_sampleSize(&ret->sampleSrc.format); stream = ga_stream_create(in_mgr, in_sampleSrc, in_bufferSamples * sampleSize); if(stream) { ctx->stream = stream; ret->sampleSrc.flags = ga_stream_flags(stream); ret->sampleSrc.flags |= GA_FLAG_THREADSAFE; ret->sampleSrc.readFunc = &gauX_sample_source_stream_read; ret->sampleSrc.endFunc = &gauX_sample_source_stream_end; ret->sampleSrc.readyFunc = &gauX_sample_source_stream_ready; if(ret->sampleSrc.flags & GA_FLAG_SEEKABLE) { ret->sampleSrc.seekFunc = &gauX_sample_source_stream_seek; ret->sampleSrc.tellFunc = &gauX_sample_source_stream_tell; } ret->sampleSrc.closeFunc = &gauX_sample_source_stream_close; } else { gcX_ops->freeFunc(ret); ret = 0; } return (ga_SampleSource*)ret; }
gc_int32 gaX_read_samples_into_stream(ga_BufferedStream* in_stream, gc_CircBuffer* in_buffer, gc_int32 in_samples, ga_SampleSource* in_sampleSrc) { void* dataA; void* dataB; gc_uint32 sizeA = 0; gc_uint32 sizeB = 0; gc_int32 numBuffers; gc_int32 numWritten = 0; ga_Format fmt; gc_int32 sampleSize; gc_CircBuffer* b = in_buffer; ga_sample_source_format(in_sampleSrc, &fmt); sampleSize = ga_format_sampleSize(&fmt); numBuffers = gc_buffer_getFree(b, in_samples * sampleSize, &dataA, &sizeA, &dataB, &sizeB); if(numBuffers >= 1) { numWritten = ga_sample_source_read(in_sampleSrc, dataA, sizeA / sampleSize, &gaX_stream_onSeek, in_stream); if(numBuffers == 2 && numWritten == sizeA) numWritten += ga_sample_source_read(in_sampleSrc, dataB, sizeB / sampleSize, &gaX_stream_onSeek, in_stream); } gc_buffer_produce(b, numWritten * sampleSize); return numWritten; }
gau_SampleSourceLoop* gau_sample_source_create_loop(ga_SampleSource* in_sampleSrc) { gau_SampleSourceLoop* ret = gcX_ops->allocFunc(sizeof(gau_SampleSourceLoop)); gau_SampleSourceLoopContext* ctx = &ret->context; gc_int32 sampleSize; ga_sample_source_init(&ret->sampleSrc); ga_sample_source_acquire(in_sampleSrc); ga_sample_source_format(in_sampleSrc, &ret->sampleSrc.format); sampleSize = ga_format_sampleSize(&ret->sampleSrc.format); ctx->triggerSample = -1; ctx->targetSample = -1; ctx->loopCount = 0; ctx->loopMutex = gc_mutex_create(); ctx->innerSrc = in_sampleSrc; ctx->sampleSize = sampleSize; ret->sampleSrc.flags = ga_sample_source_flags(in_sampleSrc); ret->sampleSrc.flags |= GA_FLAG_THREADSAFE; assert(ret->sampleSrc.flags & GA_FLAG_SEEKABLE); ret->sampleSrc.readFunc = &gauX_sample_source_loop_read; ret->sampleSrc.endFunc = &gauX_sample_source_loop_end; ret->sampleSrc.readyFunc = &gauX_sample_source_loop_ready; ret->sampleSrc.seekFunc = &gauX_sample_source_loop_seek; ret->sampleSrc.tellFunc = &gauX_sample_source_loop_tell; ret->sampleSrc.closeFunc = &gauX_sample_source_loop_close; return ret; }
ga_Sound* ga_sound_create_sample_source(ga_SampleSource* in_sampleSrc) { ga_Sound* ret = 0; ga_Format format; gc_int32 dataSize; gc_int32 totalSamples; ga_SampleSource* sampleSrc = in_sampleSrc; gc_int32 sampleSize; ga_sample_source_format(sampleSrc, &format); sampleSize = ga_format_sampleSize(&format); ga_sample_source_tell(sampleSrc, &totalSamples); if(totalSamples > 0) { /* Known total samples*/ char* data; ga_Memory* memory; dataSize = sampleSize * totalSamples; data = gcX_ops->allocFunc(dataSize); ga_sample_source_read(sampleSrc, data, totalSamples, 0, 0); memory = gaX_memory_create(data, dataSize, 0); if(memory) { ret = ga_sound_create(memory, &format); if(!ret) ga_memory_release(memory); } else gcX_ops->freeFunc(data); } else { /* Unknown total samples */ gc_int32 BUFFER_SAMPLES = format.sampleRate * 2; char* data = 0; ga_Memory* memory; totalSamples = 0; while(!ga_sample_source_end(sampleSrc)) { gc_int32 numSamplesRead; data = gcX_ops->reallocFunc(data, (totalSamples + BUFFER_SAMPLES) * sampleSize); numSamplesRead = ga_sample_source_read(sampleSrc, data + (totalSamples * sampleSize), BUFFER_SAMPLES, 0, 0); if(numSamplesRead < BUFFER_SAMPLES) { data = gcX_ops->reallocFunc(data, (totalSamples + numSamplesRead) * sampleSize); } totalSamples += numSamplesRead; } memory = gaX_memory_create(data, totalSamples * sampleSize, 0); if(memory) { ret = ga_sound_create(memory, &format); if(!ret) ga_memory_release(memory); } else gcX_ops->freeFunc(data); } return ret; }
void gaX_stream_onSeek(gc_int32 in_sample, gc_int32 in_delta, void* in_seekContext) { ga_BufferedStream* s = (ga_BufferedStream*)in_seekContext; gc_int32 samplesAvail, sampleSize; ga_Format fmt; ga_sample_source_format(s->innerSrc, &fmt); sampleSize = ga_format_sampleSize(&fmt); gc_mutex_lock(s->readMutex); gc_mutex_lock(s->seekMutex); samplesAvail = gc_buffer_bytesAvail(s->buffer) / sampleSize; gauX_tell_jump_push(&s->tellJumps, samplesAvail + in_sample, in_delta); gc_mutex_unlock(s->seekMutex); gc_mutex_unlock(s->readMutex); }
/* Stream */ ga_BufferedStream* ga_stream_create(ga_StreamManager* in_mgr, ga_SampleSource* in_sampleSrc, gc_int32 in_bufferSize) { ga_BufferedStream* ret = gcX_ops->allocFunc(sizeof(ga_BufferedStream)); ret->refCount = 1; ret->refMutex = gc_mutex_create(); ga_sample_source_acquire(in_sampleSrc); ga_sample_source_format(in_sampleSrc, &ret->format); gc_list_head(&ret->tellJumps); ret->innerSrc = in_sampleSrc; ret->nextSample = 0; ret->seek = 0; ret->tell = 0; ret->end = 0; ret->bufferSize = in_bufferSize; ret->flags = ga_sample_source_flags(in_sampleSrc); assert(ret->flags & GA_FLAG_THREADSAFE); ret->produceMutex = gc_mutex_create(); ret->seekMutex = gc_mutex_create(); ret->readMutex = gc_mutex_create(); ret->buffer = gc_buffer_create(in_bufferSize); ret->streamLink = (gc_Link*)gaX_stream_manager_add(in_mgr, ret); return ret; }
void gaX_mixer_mix_handle(ga_Mixer* in_mixer, ga_Handle* in_handle, gc_int32 in_numSamples) { ga_Handle* h = in_handle; ga_Mixer* m = in_mixer; ga_SampleSource* ss = h->sampleSrc; if(ga_sample_source_end(ss)) { /* Stream is finished! */ gc_mutex_lock(h->handleMutex); if(h->state < GA_HANDLE_STATE_FINISHED) h->state = GA_HANDLE_STATE_FINISHED; gc_mutex_unlock(h->handleMutex); return; } else { if(h->state == GA_HANDLE_STATE_PLAYING) { ga_Format handleFormat; ga_sample_source_format(ss, &handleFormat); { /* Check if we have enough samples to stream a full buffer */ gc_int32 srcSampleSize = ga_format_sampleSize(&handleFormat); gc_int32 dstSampleSize = ga_format_sampleSize(&m->format); gc_float32 oldPitch = h->pitch; gc_float32 dstToSrc = handleFormat.sampleRate / (gc_float32)m->format.sampleRate * oldPitch; gc_int32 requested = (gc_int32)(in_numSamples * dstToSrc); requested = requested / dstToSrc < in_numSamples ? requested + 1 : requested; if(requested > 0 && ga_sample_source_ready(ss, requested)) { gc_float32 gain, pan, pitch; gc_int32* dstBuffer; gc_int32 dstSamples; gc_mutex_lock(h->handleMutex); gain = h->gain; pan = h->pan; pitch = h->pitch; gc_mutex_unlock(h->handleMutex); /* We avoided a mutex lock by using pitch to check if buffer has enough dst samples */ /* If it has changed since then, we re-test to make sure we still have enough samples */ if(oldPitch != pitch) { dstToSrc = handleFormat.sampleRate / (gc_float32)m->format.sampleRate * pitch; requested = (gc_int32)(in_numSamples * dstToSrc); requested = requested / dstToSrc < in_numSamples ? requested + 1 : requested; if(!(requested > 0 && ga_sample_source_ready(ss, requested))) return; } dstBuffer = &m->mixBuffer[0]; dstSamples = in_numSamples; { /* TODO: To optimize, we can refactor the _read() interface to be _mix(), avoiding this malloc/copy */ gc_int32 bufferSize = requested * srcSampleSize; void* src = gcX_ops->allocFunc(bufferSize); gc_int32 dstBytes = dstSamples * dstSampleSize; gc_int32 numRead = 0; numRead = ga_sample_source_read(ss, src, requested, 0, 0); gaX_mixer_mix_buffer(in_mixer, src, numRead, &handleFormat, dstBuffer, dstSamples, &m->format, gain, pan, pitch); gcX_ops->freeFunc(src); } } } } } }
void ga_handle_format(ga_Handle* in_handle, ga_Format* out_format) { ga_sample_source_format(in_handle->sampleSrc, out_format); }