OggVorbis_File ogg; // OggVorbis file public unsafe virtual void Decode(SoundSample sample, int sampleOffset44k, int sampleCount44k, float *dest) { if (sample.objectInfo.wFormatTag != lastFormat || sample != lastSample) { ClearDecoder(); } lastDecodeTime = soundSystemLocal.CurrentSoundTime; if (failed) { UnsafeX.InitBlock(dest, 0, sampleCount44k * sizeof(float)); return; } // samples can be decoded both from the sound thread and the main thread for shakes ISystem.EnterCriticalSection(CRITICAL_SECTION.SECTION_ONE); var readSamples44k = sample.objectInfo.wFormatTag switch { WAVE_FORMAT_TAG.PCM => DecodePCM(sample, sampleOffset44k, sampleCount44k, dest), WAVE_FORMAT_TAG.OGG => DecodeOGG(sample, sampleOffset44k, sampleCount44k, dest), _ => 0, }; ISystem.LeaveCriticalSection(CRITICAL_SECTION.SECTION_ONE); if (readSamples44k < sampleCount44k) { UnsafeX.InitBlock(dest + readSamples44k, 0, (sampleCount44k - readSamples44k) * sizeof(float)); } }
public virtual void ClearDecoder() { ISystem.EnterCriticalSection(CRITICAL_SECTION.SECTION_ONE); switch (lastFormat) { case WAVE_FORMAT_TAG.PCM: break; case WAVE_FORMAT_TAG.OGG: ov_clear(ogg); ogg.memset(); break; } Clear(); ISystem.LeaveCriticalSection(CRITICAL_SECTION.SECTION_ONE); }
unsafe int OpenOGG(string strFileName, out WaveformatEx pwfx) { pwfx = default; mhmmio = fileSystem.OpenFileRead(strFileName); if (mhmmio == null) { return(-1); } ISystem.EnterCriticalSection(CRITICAL_SECTION.SECTION_ONE); var ov = new OggVorbis_File(); if (OggVorbis.ov_openFile(mhmmio, ov) < 0) { ISystem.LeaveCriticalSection(CRITICAL_SECTION.SECTION_ONE); fileSystem.CloseFile(mhmmio); mhmmio = null; return(-1); } mfileTime = mhmmio.Timestamp; var vi = ov_info(ov, -1); mpwfx.Format.nSamplesPerSec = (int)vi->rate; mpwfx.Format.nChannels = (short)vi->channels; mpwfx.Format.wBitsPerSample = sizeof(short) * 8; mdwSize = (int)(ov_pcm_total(ov, -1) * vi->channels); // pcm samples * num channels mbIsReadingFromMemory = false; if (SoundSystemLocal.s_realTimeDecoding.Bool) { ov_clear(ov); fileSystem.CloseFile(mhmmio); mhmmio = null; mpwfx.Format.wFormatTag = WAVE_FORMAT_TAG.OGG; mhmmio = fileSystem.OpenFileRead(strFileName); mMemSize = mhmmio.Length; } else { ogg = ov; mpwfx.Format.wFormatTag = WAVE_FORMAT_TAG.PCM; mMemSize = mdwSize * sizeof(short); } pwfx = mpwfx.Format; ISystem.LeaveCriticalSection(CRITICAL_SECTION.SECTION_ONE); isOgg = true; return(0); }
void Session_Hitch_f(CmdArgs args) { var sw = soundSystem.PlayingSoundWorld; if (sw != null) { soundSystem.SetMute(true); sw.Pause(); ISystem.EnterCriticalSection(); } SysW.Sleep(args.Count == 2 ? int.Parse(args[1]) : 100); if (sw != null) { ISystem.LeaveCriticalSection(); sw.UnPause(); soundSystem.SetMute(false); } }
// can pass SCHANNEL_ANY public virtual void StopSound(int channel) { int i; if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf($"StopSound({index},{channel})\n"); } if (soundWorld != null && soundWorld.writeDemo != null) { soundWorld.writeDemo.WriteInt((int)VFileDemo.DS.SOUND); soundWorld.writeDemo.WriteInt((int)SCMD.STOP); soundWorld.writeDemo.WriteInt(index); soundWorld.writeDemo.WriteInt(channel); } ISystem.EnterCriticalSection(); for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { var chan = channels[i]; if (!chan.triggerState) { continue; } if (channel != ISoundSystem.SCHANNEL_ANY && chan.triggerChannel != channel) { continue; } // stop it chan.Stop(); // free hardware resources chan.ALStop(); // if this was an onDemand sound, purge the sample now if (chan.leadinSample.onDemand) { chan.leadinSample.PurgeSoundSample(); } chan.leadinSample = null; chan.soundShader = null; } ISystem.LeaveCriticalSection(); }
unsafe int CloseOGG() { var ov = (OggVorbis_File)ogg; if (ov != null) { ISystem.EnterCriticalSection(CRITICAL_SECTION.SECTION_ONE); ov_clear(ov); ISystem.LeaveCriticalSection(CRITICAL_SECTION.SECTION_ONE); fileSystem.CloseFile(mhmmio); mhmmio = null; ogg = null; return(0); } return(-1); }
// returns the length of the started sound in msec public virtual int StartSound(ISoundShader shader, int channel, float diversity = 0, int shaderFlags = 0, bool allowSlow = true /* D3XP */) { int i; if (shader == null) { return(0); } var shader2 = (SoundShader)shader; if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf($"StartSound {soundWorld.gameMsec}ms ({index},{channel},{shader2.Name}) = "); } if (soundWorld != null && soundWorld.writeDemo != null) { soundWorld.writeDemo.WriteInt((int)VFileDemo.DS.SOUND); soundWorld.writeDemo.WriteInt((int)SCMD.START); soundWorld.writeDemo.WriteInt(index); soundWorld.writeDemo.WriteHashString(shader2.Name); soundWorld.writeDemo.WriteInt(channel); soundWorld.writeDemo.WriteFloat(diversity); soundWorld.writeDemo.WriteInt(shaderFlags); } // build the channel parameters by taking the shader parms and optionally overriding SoundShaderParms chanParms; chanParms = shader2.parms; OverrideParms(chanParms, this.parms, out chanParms); chanParms.soundShaderFlags |= shaderFlags; if (chanParms.shakes > 0f) { shader2.CheckShakesAndOgg(); } // this is the sample time it will be first mixed var start44kHz = soundWorld.fpa[0] != null ? soundWorld.lastAVI44kHz + Simd.MIXBUFFER_SAMPLES // if we are recording an AVI demo, don't use hardware time : soundSystemLocal.Current44kHzTime + Simd.MIXBUFFER_SAMPLES; // pick which sound to play from the shader if (shader2.numEntries == 0) { if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf("no samples in sound shader\n"); } return(0); // no sounds } // pick a sound from the list based on the passed diversity var choice = (int)(diversity * shader2.numEntries); if (choice < 0 || choice >= shader2.numEntries) { choice = 0; } // bump the choice if the exact sound was just played and we are NO_DUPS if ((chanParms.soundShaderFlags & ISoundSystem.SSF_NO_DUPS) != 0) { var sample = shader2.leadins[choice] ?? shader2.entries[choice]; for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { var chan2 = channels[i]; if (chan2.leadinSample == sample) { choice = (choice + 1) % shader2.numEntries; break; } } } // PLAY_ONCE sounds will never be restarted while they are running if ((chanParms.soundShaderFlags & ISoundSystem.SSF_PLAY_ONCE) != 0) { for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { var chan2 = channels[i]; if (chan2.triggerState && chan2.soundShader == shader2) { if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf("PLAY_ONCE not restarting\n"); } return(0); } } } // never play the same sound twice with the same starting time, even if they are on different channels for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { var chan2 = channels[i]; if (chan2.triggerState && chan2.soundShader == shader2 && chan2.trigger44kHzTime == start44kHz) { if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf("already started this frame\n"); } return(0); } } ISystem.EnterCriticalSection(); // kill any sound that is currently playing on this channel if (channel != ISoundSystem.SCHANNEL_ANY) { for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { var chan2 = channels[i]; if (chan2.triggerState && chan2.soundShader != null && chan2.triggerChannel == channel) { if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf($"(override {chan2.soundShader.base_.Name})"); } chan2.Stop(); // if this was an onDemand sound, purge the sample now if (chan2.leadinSample.onDemand) { chan2.ALStop(); chan2.leadinSample.PurgeSoundSample(); } break; } } } // find a free channel to play the sound on SoundChannel chan; for (i = 0; i < SoundSystemLocal.SOUND_MAX_CHANNELS; i++) { chan = channels[i]; if (!chan.triggerState) { break; } } if (i == SoundSystemLocal.SOUND_MAX_CHANNELS) { // we couldn't find a channel for it ISystem.LeaveCriticalSection(); if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf("no channels available\n"); } return(0); } chan = channels[i]; chan.leadinSample = shader2.leadins[choice] != null ? shader2.leadins[choice] : shader2.entries[choice]; // if the sample is onDemand (voice mails, etc), load it now if (chan.leadinSample.purged) { var start = SysW.Milliseconds; chan.leadinSample.Load(); var end = SysW.Milliseconds; session.TimeHitch(end - start); // recalculate start44kHz, because loading may have taken a fair amount of time if (soundWorld.fpa[0] == null) { start44kHz = soundSystemLocal.Current44kHzTime + Simd.MIXBUFFER_SAMPLES; } } if (SoundSystemLocal.s_showStartSound.Integer != 0) { common.Printf($"'{chan.leadinSample.name}'\n"); } chan.disallowSlow = SoundSystemLocal.s_skipHelltimeFX.Bool || !allowSlow; ResetSlowChannel(chan); // the sound will start mixing in the next async mix block chan.triggered = true; chan.openalStreamingOffset = 0; chan.trigger44kHzTime = start44kHz; chan.parms = chanParms; chan.triggerGame44kHzTime = soundWorld.game44kHz; chan.soundShader = shader2; chan.triggerChannel = channel; chan.stopped = false; chan.Start(); // we need to start updating the def and mixing it in playing = true; // spatialize it immediately, so it will start the next mix block // even if that happens before the next PlaceOrigin() Spatialize(soundWorld.listenerPos, soundWorld.listenerArea, soundWorld.rw); // return length of sound in milliseconds var length = chan.leadinSample.LengthIn44kHzSamples; if (chan.leadinSample.objectInfo.nChannels == 2) { length /= 2; // stereo samples } // adjust the start time based on diversity for looping sounds, so they don't all start at the same point if ((chan.parms.soundShaderFlags & ISoundSystem.SSF_LOOPING) != 0 && chan.leadinSample.LengthIn44kHzSamples == 0) { chan.trigger44kHzTime -= (int)(diversity * length); chan.trigger44kHzTime &= ~7; // so we don't have to worry about the 22kHz and 11kHz expansions // starting in fractional samples chan.triggerGame44kHzTime -= (int)(diversity * length); chan.triggerGame44kHzTime &= ~7; } length *= (int)(1000 / (float)SoundSystemLocal.PRIMARYFREQ); ISystem.LeaveCriticalSection(); return(length); }