private void LoadOggFile(IOggFileSource source) { m_Source = source; m_CSVorbisFile = source.VorbisFile; m_TagLibFile = source.TagLibFile; // Populate some other info shizzle and do a little bit of sanity checking m_Streams = m_CSVorbisFile.streams(); if (m_Streams <= 0) { throw new OggFileReadException("File doesn't contain any logical bitstreams", source.FileName); } // Assuming <0 is for whole file and >=0 is for specific logical bitstreams m_Bitrate = m_CSVorbisFile.bitrate(-1); m_LengthTime = (int)m_CSVorbisFile.time_total(-1); // Figure out the ALFormat of the stream m_Info = m_CSVorbisFile.getInfo(); // Get the info of the first stream, assuming all streams are the same? Dunno if this is safe tbh if (m_Info[0] == null) { throw new OggFileReadException("Unable to determine Format{FileInfo.Channels} for first bitstream", source.FileName); } if (m_TagLibFile.Properties.AudioBitrate == 16) { m_Format = (m_Info[0].channels) == 1 ? ALFormat.Mono16 : ALFormat.Stereo16; // This looks like a fudge, but I've seen it a couple of times (what about the other formats I wonder?) } else { m_Format = (m_Info[0].channels) == 1 ? ALFormat.Mono8 : ALFormat.Stereo8; } // A grab our first instance of the file so we're ready to play m_CSVorbisFileInstance = m_CSVorbisFile.makeInstance(); }
/// <summary> /// Determines the format of the given audio clip. /// </summary> /// <param name="clip">The clip to determine the format of.</param> /// <returns>The audio format.</returns> public static ALFormat DetermineFormat(VorbisFileInstance clip) { // TODO: Should probably do more than just check the format of the first stream. Info[] clipInfo = clip.vorbisFile.getInfo(); if (clipInfo.Length < 1 || clipInfo[0] == null) { throw new ArgumentException("Audio clip does not have track information"); } Info info = clipInfo[0]; // The number of channels is determined by the clip. The bit depth // however is the choice of the player. If desired, 8-bit audio // could be supported here. if (info.channels == 1) { return(ALFormat.Mono16); } else if (info.channels == 2) { return(ALFormat.Stereo16); } else { throw new NotImplementedException("Only mono and stereo are implemented. Audio has too many channels."); } }
/// <summary> /// Determines the rate of the given audio clip. /// </summary> /// <param name="clip">The clip to determine the rate of.</param> /// <returns>The audio rate.</returns> public int DetermineRate(VorbisFileInstance clip) { // TODO: Should probably do more than just check the format of the first stream. Info[] clipInfo = clip.vorbisFile.getInfo(); if (clipInfo.Length < 1 || clipInfo[0] == null) { throw new ArgumentException("Audio clip does not have track information"); } Info info = clipInfo[0]; return(info.rate); }
/// <summary> /// Begins playing the given clip. /// </summary> /// <param name="file">The clip to play.</param> public void Play(VorbisFileInstance clip) { DequeuUsedBuffers(); CurrentFormat = DetermineFormat(clip); CurrentRate = DetermineRate(clip); CurrentClip = clip; eof = false; // Buffer initial audio int usedBuffers = 0; for (int i = 0; i < BufferCount; i++) { int bytesRead = clip.read(SegmentBuffer, SegmentBuffer.Length, _BIGENDIANREADMODE, _WORDREADMODE, _SGNEDREADMODE, null); if (bytesRead > 0) { // Buffer the segment AL.BufferData(Buffers[i], CurrentFormat, SegmentBuffer, bytesRead, CurrentRate); usedBuffers++; } else if (bytesRead == 0) { // Clip is too small to fill the initial buffer, so stop // buffering. break; } else { // TODO: There was an error reading the file throw new System.IO.IOException("Error reading or processing OGG file"); } } // Start playing the clip AL.SourceQueueBuffers(Source, usedBuffers, Buffers); AL.SourcePlay(Source); }
/// <summary> /// Caches the given number of bytes by reading them in and discarding /// them. This is useful so that when the sound if first played, /// there's not a delay. /// </summary> /// <param name="bytes">Then number of PCM bytes to read.</param> protected void Cache(int bytes) { VorbisFileInstance instance = rawClip.makeInstance(); int totalBytes = 0; byte[] buffer = new byte[4096]; while (totalBytes < bytes) { int bytesRead = instance.read(buffer, buffer.Length, 0, 2, 1, null); if (bytesRead <= 0) { break; } totalBytes += bytesRead; } }
//FIXED BY ALEXEY MIKHAYLOV!!!! /// <summary> /// Reset the OggFile (reload from disk). /// Useful if tags have changed externally, or to reset the internal position pointer to replay the file from the beginning /// SeekToTime(0) is the preferred method of moving the internal pointer to the beginning however however /// </summary> public bool ResetFile() { try { // Grab a fresh instance of the file if (m_CSVorbisFile == null) { return(false); } m_CSVorbisFileInstance = m_CSVorbisFile.makeInstance(); m_TagLibFile = null; m_TagLibFile = m_Source.TagLibFile; return(true); } catch (Exception ex) { //throw new Exception("Unable to reload OggFile [" + m_Source.FileName + "]", ex); return(false); } }
/// <summary> /// Plays the audio clip on the first free channel. /// </summary> /// <param name="clip">The audio clip to play.</param> public int PlayClip(VorbisFileInstance clip) { // TODO: If all channels are busy, the clip will be ignored. There must be a more elegant way. foreach (AudioChannel channel in Channels) { try { if (channel.IsFree) { channel.Play(clip); return(channel.Source); } } catch (Exception e) { #if DEBUG //Debug.Print(e.StackTrace); #endif } } return(-1); }