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_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; }
gc_result gaX_device_queue_openAl(ga_DeviceImpl_OpenAl* in_device, void* in_buffer) { gc_int32 formatOal; gc_int32 sampleSize; ALint state; gc_int32 bps = in_device->format.bitsPerSample; ga_DeviceImpl_OpenAl* d = in_device; if(in_device->format.numChannels == 1) formatOal = (gc_int32)(bps == 16 ? AL_FORMAT_MONO16 : AL_FORMAT_MONO8); else formatOal = (gc_int32)(bps == 16 ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO8); sampleSize = ga_format_sampleSize(&in_device->format); alBufferData(d->hwBuffers[d->nextBuffer], formatOal, in_buffer, (ALsizei)in_device->numSamples * sampleSize, in_device->format.sampleRate); CHECK_AL_ERROR; if(AUDIO_ERROR != AL_NO_ERROR) return GC_ERROR_GENERIC; alSourceQueueBuffers(d->hwSource, 1, &d->hwBuffers[d->nextBuffer]); CHECK_AL_ERROR; if(AUDIO_ERROR != AL_NO_ERROR) return GC_ERROR_GENERIC; d->nextBuffer = (d->nextBuffer + 1) % d->numBuffers; --d->emptyBuffers; alGetSourcei(d->hwSource, AL_SOURCE_STATE, &state); CHECK_AL_ERROR; if(state != AL_PLAYING) { /* NOTE: calling this, even as a 'noop', can cause a clicking sound. */ alSourcePlay(d->hwSource); } CHECK_AL_ERROR; return GC_SUCCESS; }
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; }
int main(int argc, char** argv) { ga_Format fmt; ga_Device* dev; gc_int16* buf; gc_int32 numSamples; gc_int32 sampleSize; gc_int32 numToQueue; gc_int32 i; gc_int16 sample; gc_float32 pan = 1.0f; gc_float32 t = 0.0f; /* Initialize library + device */ gc_initialize(0); memset(&fmt, 0, sizeof(ga_Format)); fmt.bitsPerSample = 16; fmt.numChannels = 2; fmt.sampleRate = 44100; numSamples = 2048; sampleSize = ga_format_sampleSize(&fmt); dev = ga_device_open(GA_DEVICE_TYPE_DEFAULT, 2, 2048, &fmt); if(!dev) return 1; /* Allocate buffer */ buf = (gc_int16*)malloc(numSamples * sampleSize); /* Infinite mix loop */ while(1) { numToQueue = ga_device_check(dev); while(numToQueue--) { for(i = 0; i < numSamples * 2; i = i + 2) { sample = (gc_int16)(sin(t) * 32768); sample = (sample > -32768 ? (sample < 32767 ? sample : 32767) : -32768); pan = (gc_float32)sin(t / 300) / 2.0f + 0.5f; buf[i] = (gc_int16)(sample * pan); buf[i + 1] = (gc_int16)(sample * (1.0f - pan)); t = t + 0.03f; if(t > 3.14159265f) t -= 3.14159265f; } ga_device_queue(dev, (char*)buf); } } /* Clean up device + library */ ga_device_close(dev); gc_shutdown(); /* Free buffer */ free(buf); return 0; }
/* Sound Functions */ ga_Sound* ga_sound_create(ga_Memory* in_memory, ga_Format* in_format) { ga_Sound* ret = gcX_ops->allocFunc(sizeof(ga_Sound)); ret->numSamples = ga_memory_size(in_memory) / ga_format_sampleSize(in_format); memcpy(&ret->format, in_format, sizeof(ga_Format)); ga_memory_acquire(in_memory); ret->memory = in_memory; ret->refMutex = gc_mutex_create(); ret->refCount = 1; return (ga_Sound*)ret; }
gc_result ga_mixer_mix(ga_Mixer* in_mixer, void* out_buffer) { gc_int32 i; ga_Mixer* m = in_mixer; gc_Link* link; gc_int32 end = m->numSamples * m->format.numChannels; ga_Format* fmt = &m->format; gc_int32 mixSampleSize = ga_format_sampleSize(&m->mixFormat); memset(&m->mixBuffer[0], 0, m->numSamples * mixSampleSize); link = m->mixList.next; while(link != &m->mixList) { ga_Handle* h = (ga_Handle*)link->data; gc_Link* oldLink = link; link = link->next; gaX_mixer_mix_handle(m, (ga_Handle*)h, m->numSamples); if(ga_handle_finished(h)) { gc_mutex_lock(m->mixMutex); gc_list_unlink(oldLink); gc_mutex_unlock(m->mixMutex); } } switch(fmt->bitsPerSample) /* mixBuffer will already be correct bps */ { case 8: { gc_int8* mix = (gc_int8*)out_buffer; for(i = 0; i < end; ++i) { gc_int32 sample = m->mixBuffer[i]; mix[i] = (gc_int8)(sample > -128 ? (sample < 127 ? sample : 127) : -128); } break; } case 16: { gc_int16* mix = (gc_int16*)out_buffer; for(i = 0; i < end; ++i) { gc_int32 sample = m->mixBuffer[i]; mix[i] = (gc_int16)(sample > -32768 ? (sample < 32767 ? sample : 32767) : -32768); } break; } } return GC_SUCCESS; }
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); }
static gc_int32 gauX_mixThreadFunc(void* in_context) { gau_Manager* ctx = (gau_Manager*)in_context; ga_Mixer* m = ctx->mixer; gc_int32 sampleSize = ga_format_sampleSize(&ctx->format); while(!ctx->killThreads) { gc_int32 numToQueue = ga_device_check(ctx->device); while(numToQueue--) { ga_mixer_mix(m, ctx->mixBuffer); ga_device_queue(ctx->device, ctx->mixBuffer); } gc_thread_sleep(5); } return 0; }
/* Mixer Functions */ ga_Mixer* ga_mixer_create(ga_Format* in_format, gc_int32 in_numSamples) { ga_Mixer* ret = gcX_ops->allocFunc(sizeof(ga_Mixer)); gc_int32 mixSampleSize; gc_list_head(&ret->dispatchList); gc_list_head(&ret->mixList); ret->numSamples = in_numSamples; memcpy(&ret->format, in_format, sizeof(ga_Format)); ret->mixFormat.bitsPerSample = 32; ret->mixFormat.numChannels = in_format->numChannels; ret->mixFormat.sampleRate = in_format->sampleRate; mixSampleSize = ga_format_sampleSize(&ret->mixFormat); ret->mixBuffer = (gc_int32*)gcX_ops->allocFunc(in_numSamples * mixSampleSize); ret->dispatchMutex = gc_mutex_create(); ret->mixMutex = gc_mutex_create(); return ret; }
void gaX_mixer_mix_buffer(ga_Mixer* in_mixer, void* in_srcBuffer, gc_int32 in_srcSamples, ga_Format* in_srcFmt, gc_int32* in_dstBuffer, gc_int32 in_dstSamples, ga_Format* in_dstFmt, gc_float32 in_gain, gc_float32 in_pan, gc_float32 in_pitch) { ga_Format* mixFmt = in_dstFmt; gc_int32 mixerChannels = mixFmt->numChannels; gc_int32 srcChannels = in_srcFmt->numChannels; gc_float32 sampleScale = in_srcFmt->sampleRate / (gc_float32)mixFmt->sampleRate * in_pitch; gc_int32* dst = in_dstBuffer; gc_int32 numToFill = in_dstSamples; gc_float32 fj = 0.0f; gc_int32 j = 0; gc_int32 i = 0; gc_float32 pan = in_pan; gc_float32 gain = in_gain; gc_float32 srcSamplesRead = 0.0f; gc_int32 sampleSize = ga_format_sampleSize(in_srcFmt); pan = (pan + 1.0f) / 2.0f; pan = pan > 1.0f ? 1.0f : pan; pan = pan < 0.0f ? 0.0f : pan; /* TODO: Support 8-bit/16-bit mono/stereo mixer format */ switch(in_srcFmt->bitsPerSample) { case 16: { gc_int32 srcBytes = in_srcSamples * sampleSize; const gc_int16* src = (const gc_int16*)in_srcBuffer; while(i < numToFill * (gc_int32)mixerChannels && srcBytes >= 2 * srcChannels) { gc_int32 newJ, deltaSrcBytes; dst[i] += (gc_int32)((gc_int32)src[j] * gain * (1.0f - pan) * 2); dst[i + 1] += (gc_int32)((gc_int32)src[j + ((srcChannels == 1) ? 0 : 1)] * gain * pan * 2); i += mixerChannels; fj += sampleScale * srcChannels; srcSamplesRead += sampleScale * srcChannels; newJ = (gc_uint32)fj & (srcChannels == 1 ? ~0 : ~0x1); deltaSrcBytes = (newJ - j) * 2; j = newJ; srcBytes -= deltaSrcBytes; } break; } } }
gc_int32 ga_stream_read(ga_BufferedStream* in_stream, void* in_dst, gc_int32 in_numSamples) { ga_BufferedStream* s = in_stream; gc_CircBuffer* b = s->buffer; gc_int32 delta; /* Read the samples */ gc_int32 samplesConsumed = 0; gc_mutex_lock(s->readMutex); { void* dataA; void* dataB; gc_uint32 sizeA, sizeB; gc_int32 totalBytes = 0; gc_int32 sampleSize = ga_format_sampleSize(&s->format); gc_int32 dstBytes = in_numSamples * sampleSize; gc_int32 avail = gc_buffer_bytesAvail(b); dstBytes = dstBytes > avail ? avail : dstBytes; if(gc_buffer_getAvail(b, dstBytes, &dataA, &sizeA, &dataB, &sizeB) >= 1) { gc_int32 bytesToRead = dstBytes < (gc_int32)sizeA ? dstBytes : (gc_int32)sizeA; memcpy(in_dst, dataA, bytesToRead); totalBytes += bytesToRead; if(dstBytes > 0 && dataB) { gc_int32 dstBytesLeft = dstBytes - bytesToRead; bytesToRead = dstBytesLeft < (gc_int32)sizeB ? dstBytesLeft : (gc_int32)sizeB; memcpy((char*)in_dst + totalBytes, dataB, bytesToRead); totalBytes += bytesToRead; } } samplesConsumed = totalBytes / sampleSize; gc_buffer_consume(b, totalBytes); } /* Update the tell pos */ gc_mutex_lock(s->seekMutex); s->tell += samplesConsumed; delta = gauX_tell_jump_process(&s->tellJumps, samplesConsumed); s->tell += delta; gc_mutex_unlock(s->seekMutex); gc_mutex_unlock(s->readMutex); return samplesConsumed; }
gau_Manager* gau_manager_create_custom(gc_int32 in_devType, gc_int32 in_threadPolicy, gc_int32 in_numBuffers, gc_int32 in_bufferSamples) { gau_Manager* ret = gcX_ops->allocFunc(sizeof(gau_Manager)); assert(in_threadPolicy == GAU_THREAD_POLICY_SINGLE || in_threadPolicy == GAU_THREAD_POLICY_MULTI); assert(in_bufferSamples > 128); assert(in_numBuffers >= 2); /* Open device */ memset(&ret->format, 0, sizeof(ga_Format)); ret->format.bitsPerSample = 16; ret->format.numChannels = 2; ret->format.sampleRate = 44100; ret->device = ga_device_open(in_devType, in_numBuffers, in_bufferSamples, &ret->format); assert(ret->device); /* Initialize mixer */ ret->mixer = ga_mixer_create(&ret->format, in_bufferSamples); ret->streamMgr = ga_stream_manager_create(); ret->sampleSize = ga_format_sampleSize(&ret->format); ret->mixBuffer = (gc_int16*)gcX_ops->allocFunc(ret->mixer->numSamples * ret->sampleSize); /* Create and run mixer and stream threads */ ret->threadPolicy = in_threadPolicy; ret->killThreads = 0; if(ret->threadPolicy == GAU_THREAD_POLICY_MULTI) { ret->mixThread = gc_thread_create(gauX_mixThreadFunc, ret, GC_THREAD_PRIORITY_HIGH, 64 * 1024); ret->streamThread = gc_thread_create(gauX_streamThreadFunc, ret, GC_THREAD_PRIORITY_HIGH, 64 * 1024); gc_thread_run(ret->mixThread); gc_thread_run(ret->streamThread); } else { ret->mixThread = 0; ret->streamThread = 0; } return ret; }
void ga_stream_produce(ga_BufferedStream* in_stream) { ga_BufferedStream* s = in_stream; gc_CircBuffer* b = s->buffer; gc_int32 sampleSize = ga_format_sampleSize(&s->format); gc_int32 bytesFree = gc_buffer_bytesFree(b); if(s->seek >= 0) { gc_int32 samplePos; gc_mutex_lock(s->readMutex); gc_mutex_lock(s->seekMutex); if(s->seek >= 0) /* Check again now that we're mutexed */ { samplePos = s->seek; s->tell = samplePos; s->seek = -1; s->nextSample = samplePos; ga_sample_source_seek(s->innerSrc, samplePos); gc_buffer_consume(s->buffer, gc_buffer_bytesAvail(s->buffer)); /* Clear buffer */ gauX_tell_jump_clear(&s->tellJumps); /* Clear tell-jump list */ } gc_mutex_unlock(s->seekMutex); gc_mutex_unlock(s->readMutex); } while(bytesFree) { gc_int32 samplesWritten = 0; gc_int32 bytesWritten = 0; gc_int32 bytesToWrite = bytesFree; samplesWritten = gaX_read_samples_into_stream(s, b, bytesToWrite / sampleSize, s->innerSrc); bytesWritten = samplesWritten * sampleSize; bytesFree -= bytesWritten; s->nextSample += samplesWritten; if(bytesWritten < bytesToWrite && ga_sample_source_end(s->innerSrc)) { s->end = 1; break; } } }
ga_SampleSource* gau_sample_source_create_sound(ga_Sound* in_sound) { gau_SampleSourceSound* ret = gcX_ops->allocFunc(sizeof(gau_SampleSourceSound)); gau_SampleSourceSoundContext* ctx = &ret->context; gc_int32 sampleSize; ga_sample_source_init(&ret->sampleSrc); ga_sound_acquire(in_sound); ga_sound_format(in_sound, &ret->sampleSrc.format); sampleSize = ga_format_sampleSize(&ret->sampleSrc.format); ctx->posMutex = gc_mutex_create(); ctx->sound = in_sound; ctx->sampleSize = sampleSize; ctx->numSamples = ga_sound_numSamples(in_sound); ctx->pos = 0; ret->sampleSrc.flags = GA_FLAG_THREADSAFE | GA_FLAG_SEEKABLE; ret->sampleSrc.readFunc = &gauX_sample_source_sound_read; ret->sampleSrc.endFunc = &gauX_sample_source_sound_end; ret->sampleSrc.seekFunc = &gauX_sample_source_sound_seek; ret->sampleSrc.tellFunc = &gauX_sample_source_sound_tell; ret->sampleSrc.closeFunc = &gauX_sample_source_sound_close; return (ga_SampleSource*)ret; }
ga_SampleSource* gau_sample_source_create_wav(ga_DataSource* in_dataSrc) { gc_result validHeader; gau_SampleSourceWav* ret = gcX_ops->allocFunc(sizeof(gau_SampleSourceWav)); gau_SampleSourceWavContext* ctx = &ret->context; gc_int32 seekable = ga_data_source_flags(in_dataSrc) & GA_FLAG_SEEKABLE ? 1 : 0; ga_sample_source_init(&ret->sampleSrc); ret->sampleSrc.flags = GA_FLAG_THREADSAFE; if(seekable) ret->sampleSrc.flags |= GA_FLAG_SEEKABLE; ret->sampleSrc.readFunc = &gauX_sample_source_wav_read; ret->sampleSrc.endFunc = &gauX_sample_source_wav_end; if(seekable) { ret->sampleSrc.seekFunc = &gauX_sample_source_wav_seek; ret->sampleSrc.tellFunc = &gauX_sample_source_wav_tell; } ret->sampleSrc.closeFunc = &gauX_sample_source_wav_close; ctx->pos = 0; ga_data_source_acquire(in_dataSrc); ctx->dataSrc = in_dataSrc; validHeader = gauX_sample_source_wav_load_header(in_dataSrc, &ctx->wavHeader); if(validHeader == GC_SUCCESS) { ctx->posMutex = gc_mutex_create(); ret->sampleSrc.format.numChannels = ctx->wavHeader.channels; ret->sampleSrc.format.bitsPerSample = ctx->wavHeader.bitsPerSample; ret->sampleSrc.format.sampleRate = ctx->wavHeader.sampleRate; ctx->sampleSize = ga_format_sampleSize(&ret->sampleSrc.format); } else { ga_data_source_release(in_dataSrc); gcX_ops->freeFunc(ret); ret = 0; } return (ga_SampleSource*)ret; }
ga_DeviceImpl_XAudio2* gaX_device_open_xaudio2(gc_int32 in_numBuffers, gc_int32 in_numSamples, ga_Format* in_format) { ga_DeviceImpl_XAudio2* ret = gcX_ops->allocFunc(sizeof(ga_DeviceImpl_XAudio2)); HRESULT result; WAVEFORMATEX fmt; gc_int32 i; ret->devType = GA_DEVICE_TYPE_XAUDIO2; ret->numBuffers = in_numBuffers; ret->numSamples = in_numSamples; memcpy(&ret->format, in_format, sizeof(ga_Format)); ret->sampleSize = ga_format_sampleSize(in_format); ret->nextBuffer = 0; ret->xa = 0; ret->master = 0; CoInitializeEx(NULL, COINIT_MULTITHREADED); result = XAudio2Create(&ret->xa, 0, XAUDIO2_DEFAULT_PROCESSOR); if(FAILED(result)) goto cleanup; result = IXAudio2_CreateMasteringVoice(ret->xa, &ret->master, 2, 44100, 0, 0, 0); if(FAILED(result)) goto cleanup; fmt.cbSize = sizeof(WAVEFORMATEX); ZeroMemory(&fmt, sizeof(WAVEFORMATEX)); fmt.cbSize = sizeof(WAVEFORMATEX); fmt.wFormatTag = WAVE_FORMAT_PCM; fmt.nChannels = 2; fmt.wBitsPerSample = 16; fmt.nSamplesPerSec = 44100; fmt.nBlockAlign = fmt.nChannels * (fmt.wBitsPerSample / 8); fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; result = IXAudio2_CreateSourceVoice(ret->xa, &ret->source, &fmt, XAUDIO2_VOICE_NOPITCH, XAUDIO2_DEFAULT_FREQ_RATIO, 0, 0, 0); if(FAILED(result)) goto cleanup; result =IXAudio2_StartEngine(ret->xa); if(FAILED(result)) goto cleanup; result = IXAudio2SourceVoice_Start(ret->source, 0, XAUDIO2_COMMIT_NOW); if(FAILED(result)) goto cleanup; ret->buffers = gcX_ops->allocFunc(ret->numBuffers * sizeof(void*)); for(i = 0; i < ret->numBuffers; ++i) ret->buffers[i] = gcX_ops->allocFunc(ret->numSamples * ret->sampleSize); return ret; cleanup: if(ret->source) { IXAudio2SourceVoice_Stop(ret->source, 0, XAUDIO2_COMMIT_NOW); IXAudio2SourceVoice_FlushSourceBuffers(ret->source); IXAudio2SourceVoice_DestroyVoice(ret->source); } if(ret->xa) IXAudio2_StopEngine(ret->xa); if(ret->master) IXAudio2MasteringVoice_DestroyVoice(ret->master); if(ret->xa) IXAudio2_Release(ret->xa); CoUninitialize(); gcX_ops->freeFunc(ret); return 0; }
gc_int32 ga_sound_numSamples(ga_Sound* in_sound) { return ga_memory_size(in_sound->memory) / ga_format_sampleSize(&in_sound->format); }
gc_int32 ga_stream_ready(ga_BufferedStream* in_stream, gc_int32 in_numSamples) { ga_BufferedStream* s = in_stream; gc_int32 avail = gc_buffer_bytesAvail(s->buffer); return s->end || avail >= in_numSamples * ga_format_sampleSize(&s->format) && avail > s->bufferSize / 2.0f; }
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); } } } } } }