/// <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 void ResetFile() { try { // Grab a fresh instance of the file m_CSVorbisFileInstance = m_CSVorbisFile.makeInstance(); m_TagLibFile = null; m_TagLibFile = TagLib.File.Create(m_Filename); } catch (Exception ex) { throw new Exception("Unable to reload OggFile [" + m_Filename + "]", ex); } }
private TagLib.File m_TagLibFile; // TagLibSharp file object #endregion Fields #region Constructors /// <summary> /// Constructor /// </summary> /// <param name="Filename"> /// A <see cref="System.String"/> containing the path to the Ogg Vorbis file this instance represents /// </param> public OggFile(string Filename) { // Check that the file exists if (!(System.IO.File.Exists(Filename))) { throw new OggFileReadException("File not found", Filename); } // Load the relevant objects m_Filename = Filename; try { m_CSVorbisFile = new VorbisFile(m_Filename); } catch (Exception ex) { throw new OggFileReadException("Unable to open file for data reading\n" + ex.Message, Filename); } try { m_TagLibFile = TagLib.File.Create(m_Filename); } catch (TagLib.UnsupportedFormatException ex) { throw new OggFileReadException("Unsupported format (not an ogg?)\n" + ex.Message, Filename); } catch (TagLib.CorruptFileException ex) { throw new OggFileCorruptException(ex.Message, Filename, "Tags"); } // 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", 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", 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> /// Retrive the source info from a vorbis file /// </summary> /// <param name="source">the source file to retrieve data from</param> /// <returns></returns> public Info RetrieveSourceInfo(VorbisFileInstance source) { Info[] formatInfo = source.vorbisFile.getInfo(); if (formatInfo.Length < 1 || formatInfo[0] == null) throw new ArgumentException("NO CLIP INFORMATION"); Info sourceInfo = formatInfo[0]; return sourceInfo; }
public void StopFile(VorbisFileInstance audioFile) { foreach(AudioSource source in AudioSources) { try { source.PauseSource(audioFile); source.Dispose(); return; } catch (Exception e) { Console.WriteLine("Cannot remove source"); } } }
public int bisect_forward_serialno(long begin, long searched, long end, int currentno, int m, VorbisFileInstance instance) { long endsearched=end; long next=end; Page page=new Page(); int ret; while(searched<endsearched) { long bisect; if(endsearched-searched<CHUNKSIZE) { bisect=searched; } else { bisect=(searched+endsearched)/2; } seek_helper(bisect, instance); ret = get_next_page(page, -1, instance); if(ret==OV_EREAD) return OV_EREAD; if(ret<0 || page.serialno()!=currentno) { endsearched=bisect; if(ret>=0)next=ret; } else { searched=ret+page.header_len+page.body_len; } } seek_helper(next, instance); ret = get_next_page(page, -1, instance); if(ret==OV_EREAD) return OV_EREAD; if(searched>=end || ret==-1) { links=m+1; offsets=new long[m+2]; offsets[m+1]=searched; } else { ret=bisect_forward_serialno(next, offset, end, page.serialno(), m+1, instance); if(ret==OV_EREAD)return OV_EREAD; } offsets[m]=begin; return 0; }
// last step of the OggVorbis_File initialization; get all the // vorbis_info structs and PCM positions. Only called by the seekable // initialization (local stream storage is hacked slightly; pay // attention to how that's done) public void prefetch_all_headers(Info first_i,Comment first_c, int dataoffset, VorbisFileInstance instance) { Page og=new Page(); int ret; vi=new Info[links]; vc=new Comment[links]; dataoffsets=new long[links]; pcmlengths=new long[links]; serialnos=new int[links]; for(int i=0;i<links;i++) { if(first_i!=null && first_c!=null && i==0) { // we already grabbed the initial header earlier. This just // saves the waste of grabbing it again // !!!!!!!!!!!!! vi[i]=first_i; //memcpy(vf->vi+i,first_i,sizeof(vorbis_info)); vc[i]=first_c; //memcpy(vf->vc+i,first_c,sizeof(vorbis_comment)); dataoffsets[i]=dataoffset; } else { // seek to the location of the initial header seek_helper(offsets[i], instance); //!!! if (fetch_headers(vi[i], vc[i], null, null, instance) == -1) { Console.Error.WriteLine("Error opening logical bitstream #"+(i+1)+"\n"); dataoffsets[i]=-1; } else { dataoffsets[i]=offset; instance.os.clear(); } } // get the serial number and PCM length of this link. To do this, // get the last page of the stream long end=offsets[i+1]; //!!! seek_helper(end, instance); while(true) { ret=get_prev_page(og, instance); if(ret==-1) { // this should not be possible Console.Error.WriteLine("Could not find last page of logical "+ "bitstream #"+(i)+"\n"); vi[i].clear(); vc[i].clear(); break; } if(og.granulepos()!=-1) { serialnos[i]=og.serialno(); pcmlengths[i]=og.granulepos(); break; } } } }
/// <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); }
public int get_next_page(Page page, long boundary, VorbisFileInstance instance) { if(boundary>0) boundary+=offset; while(true) { int more; if(boundary>0 && offset>=boundary)return OV_FALSE; more = instance.oy.pageseek(page); if(more<0){offset-=more;} else { if(more==0) { if(boundary==0)return OV_FALSE; // if(get_data()<=0)return -1; int ret=get_data(instance); if(ret==0) return OV_EOF; if(ret<0) return OV_EREAD; } else { int ret=(int)offset; //!!! offset+=more; return ret; } } } }
/// <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; }
// DETERMINES THE ALFORMAT FOR A GIVEN SOURCE public ALFormat DetermineSourceFormat(VorbisFileInstance source) { Info sourceInfo = RetrieveSourceInfo(source); if (sourceInfo.channels == 1) { return ALFormat.Mono16; } else if (sourceInfo.channels == 2) { return ALFormat.Stereo16; } else { throw new NotImplementedException("Only mono and stereo are implemented. Audio has too many channels."); } }
// PAUSE public void PauseSource(VorbisFileInstance source) { // TO DO AL.SourcePause(Source); }
// PLAY public void PlaySource(VorbisFileInstance source) { // Remove any empty buffers RemoveEmptyBuffers(); // Set up all the source parameters AudioFormat = DetermineSourceFormat(source); AudioRate = DetermineSourceRate(source); // Initialize the source to play SourceFile = source; FileHasEnded = false; // Start buffer int processedBuffers = 0; for (int i = 0; i < BufferCount; i++) { int bytesRead = source.read(SegmentBuffer, SegmentBuffer.Length, _BIGENDIANREADMODE, _WORDREADMODE, _SGNEDREADMODE, null); if (bytesRead > 0) { AL.BufferData(Buffers[i], AudioFormat, SegmentBuffer, bytesRead, AudioRate); processedBuffers++; } else if (bytesRead == 0) { break; } else { throw new System.IO.IOException("Unable to open OGG File"); } } // Play buffered clip AL.SourceQueueBuffers(Source, processedBuffers, Buffers); AL.SourcePlay(Source); }
public int get_data(VorbisFileInstance instance) { int index=instance.oy.buffer(CHUNKSIZE); byte[] buffer = instance.oy.data; // int bytes=callbacks.read_func(buffer, index, 1, CHUNKSIZE, datasource); int bytes=0; try { bytes=datasource.Read(buffer, index, CHUNKSIZE); } catch(Exception e) { Console.Error.WriteLine(e.Message); return OV_EREAD; } instance.oy.wrote(bytes); if(bytes==-1) { bytes=0; } return bytes; }
/// <summary> /// Plays the audio clip on the first free channel. /// </summary> /// <param name="clip">The audio clip to play.</param> public void 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; } } catch (Exception e) { #if DEBUG Debug.Print(e.StackTrace); #endif } } }
public void seek_helper(long offst, VorbisFileInstance instance) { //callbacks.seek_func(datasource, offst, SEEK_SET); fseek(datasource, offst, SEEK_SET); this.offset=offst; instance.oy.reset(); }
// Play Call public void PlayFile(VorbisFileInstance audioFile) { foreach (AudioSource source in AudioSources) { try { if (source.IsFree) { source.PlaySource(audioFile); return; } } catch (Exception e) { //Console.WriteLine("AudioManager threw exception!"); //Console.WriteLine(e.StackTrace); } } }
private int get_prev_page(Page page, VorbisFileInstance instance) { long begin=offset; //!!! int ret; int offst=-1; while(offst==-1) { begin-=CHUNKSIZE; if(begin<0) begin=0; seek_helper(begin, instance); while(offset<begin+CHUNKSIZE) { ret=get_next_page(page, begin+CHUNKSIZE-offset, instance); if(ret==OV_EREAD){ return OV_EREAD; } if(ret<0){ break; } else{ offst=ret; } } } seek_helper(offst, instance); //!!! ret=get_next_page(page, CHUNKSIZE, instance); if(ret<0) { //System.err.println("Missed page fencepost at end of logical bitstream Exiting"); //System.exit(1); return OV_EFAULT; } return offst; }
public void PlayFile(VorbisFileInstance audioFile, out AudioSource currentSource) { currentSource = null; foreach (AudioSource source in AudioSources) { try { if (source.IsFree) { Thread.Sleep(500); source.PlaySource(audioFile); currentSource = source; return; } } catch (Exception e) { //Console.WriteLine("AudioManager threw exception!"); //Console.WriteLine(e.StackTrace); //currentSource = null; } } }
// uses the local ogg_stream storage in vf; this is important for // non-streaming input sources public int fetch_headers(Info vi, Comment vc, int[] serialno, Page og_ptr, VorbisFileInstance instance) { //System.err.println("fetch_headers"); Page og=new Page(); Packet op=new Packet(); int ret; if(og_ptr==null) { ret = get_next_page(og, CHUNKSIZE, instance); if(ret==OV_EREAD)return OV_EREAD; if(ret<0) return OV_ENOTVORBIS; og_ptr=og; } if(serialno!=null)serialno[0]=og_ptr.serialno(); instance.os.init(og_ptr.serialno()); // extract the initial header from the first page and verify that the // Ogg bitstream is in fact Vorbis data vi.init(); vc.init(); int i=0; while(i<3) { instance.os.pagein(og_ptr); while(i<3) { int result = instance.os.packetout(op); if(result==0)break; if(result==-1) { Console.Error.WriteLine("Corrupt header in logical bitstream."); //goto bail_header; vi.clear(); vc.clear(); instance.os.clear(); return -1; } if(vi.synthesis_headerin(vc, op)!=0) { Console.Error.WriteLine("Illegal header in logical bitstream."); //goto bail_header; vi.clear(); vc.clear(); instance.os.clear(); return -1; } i++; } if(i<3) if(get_next_page(og_ptr, 1, instance)<0) { Console.Error.WriteLine("Missing header in logical bitstream."); //goto bail_header; vi.clear(); vc.clear(); instance.os.clear(); return -1; } } return 0; }
public void PauseFile(VorbisFileInstance audioFile) { foreach (AudioSource source in AudioSources) { try { source.PauseSource(audioFile); return; } catch (Exception e) { Console.WriteLine("issue with {0}", e); } } }
public VorbisFileInstance makeInstance() { VorbisFileInstance instance = new VorbisFileInstance(this); return instance; }
// DETERMINE THE RATE OF A GIVEN SOURCE public int DetermineSourceRate(VorbisFileInstance source) { Info sourceInfo = RetrieveSourceInfo(source); return sourceInfo.rate; }