public override void Perform(AudioData audio, PreviewImageQuery query) { int desiredWidth = query.DesiredWidth; int desiredHeight = query.DesiredHeight; int oggHash = audio.OggVorbisData.GetCombinedHashCode(); int oggLen = audio.OggVorbisData.Length; PcmData pcm = OggVorbis.LoadChunkFromMemory(audio.OggVorbisData, 500000); short[] sdata = pcm.data; short maxVal = 1; for (int i = 0; i < pcm.dataLength; i++) { maxVal = Math.Max(maxVal, Math.Abs(pcm.data[i])); } Bitmap result = new Bitmap(desiredWidth, desiredHeight); int channelLength = pcm.dataLength / pcm.channelCount; int yMid = result.Height / 2; int stepWidth = (channelLength / (2 * result.Width)) - 1; const int samples = 10; using (Graphics g = Graphics.FromImage(result)) { Color baseColor = ExtMethodsColor.ColorFromHSV( (float)(oggHash % 90) * (float)(oggLen % 4) / 360.0f, 0.5f, 1f); Pen linePen = new Pen(Color.FromArgb(MathF.RoundToInt(255.0f / MathF.Pow((float)samples, 0.65f)), baseColor)); g.Clear(Color.Transparent); for (int x = 0; x < result.Width; x++) { float invMaxVal = 2.0f / ((float)maxVal + (float)short.MaxValue); float timePercentage = (float)x / (float)result.Width; int i = MathF.RoundToInt(timePercentage * channelLength); float left; float right; short channel1; short channel2; for (int s = 0; s <= samples; s++) { int offset = stepWidth * s / samples; channel1 = sdata[(i + offset) * pcm.channelCount + 0]; channel2 = sdata[(i + offset) * pcm.channelCount + 1]; left = (float)Math.Abs(channel1) * invMaxVal; right = (float)Math.Abs(channel2) * invMaxVal; g.DrawLine(linePen, x, yMid, x, yMid + MathF.RoundToInt(left * yMid)); g.DrawLine(linePen, x, yMid, x, yMid - MathF.RoundToInt(right * yMid)); } } } query.Result = result; }
private void PerformStreamingUpdate(AudioData audioDataRes) { int num; AL.GetSource(this.alSource, ALGetSourcei.BuffersProcessed, out num); while (num > 0) { num--; int unqueued; unqueued = AL.SourceUnqueueBuffer(this.alSource); if (OggVorbis.IsStreamValid(this.strOvStr)) { PcmData pcm; bool eof = !OggVorbis.StreamChunk(this.strOvStr, out pcm); if (eof) { OggVorbis.EndStream(ref this.strOvStr); if (this.looped) { OggVorbis.BeginStreamFromMemory(audioDataRes.OggVorbisData, out this.strOvStr); if (pcm.dataLength == 0) { eof = !OggVorbis.StreamChunk(this.strOvStr, out pcm); } else { eof = false; } } } if (pcm.dataLength > 0) { AL.BufferData( unqueued, pcm.channelCount == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, pcm.data, pcm.dataLength * PcmData.SizeOfDataElement, pcm.sampleRate); AL.SourceQueueBuffer(this.alSource, unqueued); } if (pcm.dataLength == 0 || eof) { this.strStopReq = StopRequest.EndOfStream; break; } } } }
private void OnDisposed(bool manually) { if (manually) { if (this.isStreamed) { lock (this.strLock) { OggVorbis.EndStream(ref this.strOvStr); } this.strStopReq = StopRequest.Immediately; } this.attachedTo = null; this.curPriority = -1; lock (this.strLock) { if (this.alSource > AlSource_NotAvailable) { this.CleanupAlSource(); DualityApp.Sound.FreeAlSource(this.alSource); this.alSource = AlSource_NotAvailable; } if (this.strAlBuffers != null) { for (int i = 0; i < this.strAlBuffers.Length; i++) { if (!AL.IsBuffer(this.strAlBuffers[i])) { return; } AL.DeleteBuffer(this.strAlBuffers[i]); } this.strAlBuffers = null; } this.UnregisterPlaying(); } } }
private void OnDisposed(bool manually) { if (manually) { if (this.strWorker != null && this.strWorker.IsAlive) { lock (this.strLock) { OggVorbis.EndStream(ref this.strOvStr); } } this.strWorker = null; this.attachedTo = null; this.curPriority = -1; lock (this.strLock) { if (this.alSource > AlSource_NotAvailable) { this.CleanupAlSource(); DualityApp.Sound.FreeAlSource(this.alSource); this.alSource = AlSource_NotAvailable; } if (this.strAlBuffers != null) { for (int i = 0; i < this.strAlBuffers.Length; i++) { if (!AL.IsBuffer(this.strAlBuffers[i])) { continue; } AL.DeleteBuffer(this.strAlBuffers[i]); } this.strAlBuffers = null; } this.UnregisterPlaying(); } } }
private void PerformStreamingBegin(AudioData audioDataRes) { // Generate streaming buffers this.strAlBuffers = new int[3]; for (int i = 0; i < this.strAlBuffers.Length; ++i) { AL.GenBuffers(1, out this.strAlBuffers[i]); } // Begin streaming OggVorbis.BeginStreamFromMemory(audioDataRes.OggVorbisData, out this.strOvStr); // Initially, completely fill all buffers for (int i = 0; i < this.strAlBuffers.Length; ++i) { PcmData pcm; bool eof = !OggVorbis.StreamChunk(this.strOvStr, out pcm); if (pcm.dataLength > 0) { AL.BufferData( this.strAlBuffers[i], pcm.channelCount == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, pcm.data, pcm.dataLength * PcmData.SizeOfDataElement, pcm.sampleRate); AL.SourceQueueBuffer(this.alSource, this.strAlBuffers[i]); if (eof) { break; } } else { break; } } }
public unsafe int DecodeOGG(SoundSample sample, int sampleOffset44k, int sampleCount44k, float *dest) { int readSamples, totalSamples; var shift = 22050 / sample.objectInfo.nSamplesPerSec; var sampleOffset = sampleOffset44k >> shift; var sampleCount = sampleCount44k >> shift; // open OGG file if not yet opened if (lastSample == null) { // make sure there is enough space for another decoder if (ISampleDecoder.decoderMemoryAllocator.FreeBlockMemory < ISampleDecoder.MIN_OGGVORBIS_MEMORY) { return(0); } if (sample.nonCacheData == null) { Debug.Assert(false); failed = true; return(0); } // this should never happen file.SetData(sample.nonCacheData, sample.objectMemSize); if (OggVorbis.ov_openFile(file, ogg) < 0) { failed = true; return(0); } lastFormat = WAVE_FORMAT_TAG.OGG; lastSample = sample; } // seek to the right offset if necessary if (sampleOffset != lastSampleOffset && ov_pcm_seek(ogg, sampleOffset / sample.objectInfo.nChannels) != 0) { failed = true; return(0); } lastSampleOffset = sampleOffset; // decode OGG samples totalSamples = sampleCount; readSamples = 0; do { float **samples; var ret = (int)ov_read_float(ogg, &samples, totalSamples / sample.objectInfo.nChannels, null); if (ret == 0) { failed = true; break; } if (ret < 0) { failed = true; return(0); } ret *= sample.objectInfo.nChannels; Simd.UpSampleOGGTo44kHz(dest + (readSamples << shift), samples, ret, sample.objectInfo.nSamplesPerSec, sample.objectInfo.nChannels); readSamples += ret; totalSamples -= ret; } while (totalSamples > 0); lastSampleOffset += readSamples; return(readSamples << shift); }
private static void ThreadStreamFunc(object param) { SoundInstance sndInst = (SoundInstance)param; while (true) { lock (sndInst.strLock) { if (sndInst.Disposed) { return; } if (!DualityApp.Sound.IsAvailable) { return; } ALSourceState stateTemp = ALSourceState.Stopped; if (sndInst.alSource > AlSource_NotAvailable) { stateTemp = AL.GetSourceState(sndInst.alSource); } if (stateTemp == ALSourceState.Stopped && sndInst.strStopReq != StopRequest.None) { // Stopped due to regular EOF. If strStopReq is NOT set, // the source stopped playing because it reached the end of the buffer // but in fact only because we were too slow inserting new data. return; } else if (sndInst.strStopReq == StopRequest.Immediately) { // Stopped intentionally due to Stop() if (sndInst.alSource > AlSource_NotAvailable) { AL.SourceStop(sndInst.alSource); } return; } Sound soundRes = sndInst.sound.Res; AudioData audioDataRes = sndInst.audioData.Res; if (soundRes == null || audioDataRes == null) { sndInst.Dispose(); return; } if (stateTemp == ALSourceState.Initial) { // Generate streaming buffers sndInst.strAlBuffers = new int[3]; for (int i = 0; i < sndInst.strAlBuffers.Length; ++i) { AL.GenBuffers(1, out sndInst.strAlBuffers[i]); } // Begin streaming OggVorbis.BeginStreamFromMemory(audioDataRes.OggVorbisData, out sndInst.strOvStr); // Initially, completely fill all buffers for (int i = 0; i < sndInst.strAlBuffers.Length; ++i) { PcmData pcm; bool eof = !OggVorbis.StreamChunk(sndInst.strOvStr, out pcm); if (pcm.dataLength > 0) { AL.BufferData( sndInst.strAlBuffers[i], pcm.channelCount == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, pcm.data, pcm.dataLength * PcmData.SizeOfDataElement, pcm.sampleRate); AL.SourceQueueBuffer(sndInst.alSource, sndInst.strAlBuffers[i]); if (eof) { break; } } else { break; } } // Initially play source AL.SourcePlay(sndInst.alSource); stateTemp = AL.GetSourceState(sndInst.alSource); } else { int num; AL.GetSource(sndInst.alSource, ALGetSourcei.BuffersProcessed, out num); while (num > 0) { num--; int unqueued; unqueued = AL.SourceUnqueueBuffer(sndInst.alSource); if (OggVorbis.IsStreamValid(sndInst.strOvStr)) { PcmData pcm; bool eof = !OggVorbis.StreamChunk(sndInst.strOvStr, out pcm); if (eof) { OggVorbis.EndStream(ref sndInst.strOvStr); if (sndInst.looped) { OggVorbis.BeginStreamFromMemory(audioDataRes.OggVorbisData, out sndInst.strOvStr); if (pcm.dataLength == 0) { eof = !OggVorbis.StreamChunk(sndInst.strOvStr, out pcm); } } } if (pcm.dataLength > 0) { AL.BufferData( unqueued, pcm.channelCount == 1 ? ALFormat.Mono16 : ALFormat.Stereo16, pcm.data, pcm.dataLength * PcmData.SizeOfDataElement, pcm.sampleRate); AL.SourceQueueBuffer(sndInst.alSource, unqueued); } if (pcm.dataLength == 0 || eof) { sndInst.strStopReq = StopRequest.EndOfStream; break; } } } } if (stateTemp == ALSourceState.Stopped && sndInst.strStopReq == StopRequest.None) { // If the source stopped unintentionally, restart it. (See above) AL.SourcePlay(sndInst.alSource); } } Thread.Sleep(16); } }