Click here to Skip to main content
Email Password   helpLost your password?

Screenshot - OggPlayer.jpg

Introduction

The TgPlayOgg project is a .NET C# library that allows you to play Ogg Vorbis files from your managed code. Decoding a given Ogg Vorbis file into usable sound data is done by TgPlayOgg which makes calls to an unmanaged C++ project TGPlayOgg_vorbisfile. TgPlayOgg also requires managed DirectX for sound output.

Background

At TrayGames, we needed to add support for playing sound files to the multi-player online game development SDK (TGSDK) provided to third party developers. We started out using the MP3 audio format, but we were concerned about licensing issues (fees kick in after you reach a certain level of sales). After comparing alternatives, we chose to use the Ogg Vorbis format. Ogg Vorbis is a completely open, patent-free, professional audio encoding and streaming technology with all the benefits of Open Source.

Using the Code

If you download the source, there is an "OggPlayer.sln" solution file under the "OggPlayer Sample" folder that will build all of the projects mentioned in this article. A sample test application has been provided in the "Test App" folder under the TgPlayOgg project. This application demonstrates how to use the library. The steps are as follows:

Let's take a look at the highlights of the test application. First we see it has a method that does the initialization and handles the PlayOggFile event. Note that we must call the OggPlay constructor in a try block, since it makes calls into DirectSound that may raise exceptions. Then it has another method that allows the user to choose an Ogg Vorbis sound file to open for playback.

using TG.Sound;

private void InitTestOfOggPlayer()
{
  try
  {
      oplay = new OggPlay(this, OggSampleSize.SixteenBits);
      oplay.PlayOggFileResult += new PlayOggFileEventHandler(PlayOggFileResult);

      textBox1.Text = "Initialization successful.\r\n";
  }
  catch(Exception e)
  {
      textBox1.Text = "Initialization failed: " + e.Message + "\r\n";
  }
}
  
private void Button1Click(object sender, System.EventArgs e)
{
  OggName = GetOggFileNameToOpen();
  
  if (OggName != null)
  {
    oplay.PlayOggFile(OggName, ++PlayId);
    textBox1.Text = "Playing " + OggName + " Id= " + PlayId.ToString() + "\r\n";
  }        
}

The Ogg Vorbis decoder may have encountered errors while decoding the Ogg Vorbis data. An Ogg Vorbis file may refuse to play if there is not enough data to stream through the initial buffers (the Ogg is too small) or if it simply can not read the file. There are two error counts that are for information purposes only, since if successful the created waveform data was played, but it may not have sounded as intended if either of these two counts are nonzero. The way we handle the PlayOggFile event is to display a status message indicating success or error (with the two error counts). We'll learn more about what these error counts mean later.

private static void PlayOggFileResult(object sender, PlayOggFileEventArgs e)
{
  if (e.Success)
  {
    MainForm.textBox1.Text += "PlayOggFile(" + e.PlayId + ") succeeded ("
      + "ErrorHoleCount: " + e.ErrorHoleCount + ", ErrorBadLinkCount: " 
      + e.ErrorBadLinkCount + ").\r\n";
  }
  else
  {
    MainForm.textBox1.Text += "PlayOggFile(" + e.PlayId + ") failed: '" 
      + e.ReasonForFailure + "'\r\n";
   }
   
   PlayId--;
}

Note that exiting a calling application does not kill the playback threads if one or more Ogg Vorbis files are still playing. These playback threads keep running although you can no longer hear them playing. The threads will finish playing whatever Ogg Vorbis files they were playing and then quit, unless the threads are specifically told to stop playback. So, when your application exits, it should probably kill any long playing Ogg Vorbis files that are still playing. This is why the test application handles the Form.Closing event by calling OggPlay.StopOggFile which you will learn more about later.

protected void Form1_Closing(object sender, 
                      System.ComponentModel.CancelEventArgs e)
{
  // Determine if any Ogg files are still playing by checking the PlayId member

  if (PlayId > 0)
  {
    // Display a MsgBox asking the user to save changes or abort

    if (MessageBox.Show("Ogg files are still playing," + 
        " are you sure you want to exit?", "TrayGames Ogg Player",
        MessageBoxButtons.YesNo) ==  DialogResult.No)
    {
      // Cancel the Closing event from closing the form

      e.Cancel = true;

      // Wait for files to finish playing...

    }
    else
    {
        // Kill all outstanding playbacks

      while (PlayId > 0)
        oplay.StopOggFile(PlayId--);  
    }
  }
}

Other times you might want to kill playback threads in midstream would be if your application has a pause capability, or if you want to reset sounds when the user switches away from your game.

The Ogg Vorbis Wrapper

Ogg Vorbis' high-level API, Vorbisfile, has only two input choices: either a C file pointer or a set of custom callback functions that do the reading of the input Ogg Vorbis data. The better and more portable of these choices is probably custom callbacks, but I wasn't aware that .NET 1.1 gave any control over the calling convention of its methods, and its standard calling convention is StdCall, while the Vorbisfile dynamic link libraries (DLLs) are compiled with the Cdecl calling convention. Thus, given C# and .NET 1.1, we decided to write some C/C++ code and compile it into a DLL, and this DLL includes the callbacks that Vorbisfile needs. This is why we created the TGPlayOgg_vorbis wrapper project.

I have since learned that you can use the DllImportAttribute class to provide the information needed to call a function exported from an unmanaged DLL. So you should be able to modify the source code of this library to eliminate the TGPlayOgg_vorbis wrapper project and make the Vorbisfile API calls directly from the TGPlayOgg project. The .NET Framework Base Class Library (BCL) provides for StdCall, Cdecl, ThisCall, and the WinApi calling conventions. WinApi selects the correct type automatically based on the platform (Windows or Windows CE). For example, to change the SomeFunction located in SomeLibrary.dll, you could use the following code:

[DllImport("SomeLibrary.DLL", EntryPoint="SomeFunction",  SetLastError=true,
CharSet=CharSet.Unicode, ExactSpelling=true,
CallingConvention=CallingConvention.Cdecl)]
public static extern bool SomeFunction(String param1, String param2);

For now though, the TGPlayOgg_vorbis project makes the calls into the Ogg Vorbis API for us. There are three wrapper functions: init_file_for_ogg_decode, ogg_decode_one_vorbis_packet, and ogg_final_cleanup. You will never call these methods directly, the C# library will make them so it can decode the file. If you want to add the definitions for these methods to your own managed application you would define a NativeMethods class (use any class name) and add the function prototypes to it. Having a separate class for your unmanaged DLL functions is advisable because consuming DLL functions can be prone to errors. Encapsulating the DLL declarations makes your job easier when it comes time to debug. The "Vorbisapi.cs" file in the TGPlayOgg_vorbis project already has such a class definition containing the declarations:

// External C functions in the TgPlayOgg_vorbisfile unmanaged DLL

[DllImport("TgPlayOgg_vorbisfile.dll", CharSet=CharSet.Unicode,
   CallingConvention=CallingConvention.Cdecl)]
public unsafe static extern int init_for_ogg_decode(
string fileName, void **vf_out);

[DllImport("TgPlayOgg_vorbisfile.dll", CallingConvention=CallingConvention.Cdecl)]
public unsafe static extern int ogg_decode_one_vorbis_packet(
  void *vf_ptr, void *buf_out, int buf_byte_size, 
int bits_per_sample, int *channels_cnt, int *sampling_rate, 
int *err_ov_hole_cnt, int *err_ov_ebadlink_cnt);

[DllImport("TgPlayOgg_vorbisfile.dll", CallingConvention=CallingConvention.Cdecl)]
public unsafe static extern int final_ogg_cleanup(void *vf_ptr);

What allows us to make these calls is the Platform Invoke (PInvoke) service. PInvoke will enable our managed code to call the unmanaged functions implemented in the DLL. It will locate and invoke the exported functions and marshal their parameters across the managed/unmanaged code boundary as needed. Note that PInvoke throws exceptions generated by the unmanaged function to the managed caller. Let's look at our unmanaged functions now.

The init_file_for_ogg_decode function will open and initialize the given Ogg Vorbis file for decoding. It sets up all the related decoding structures by calling the op_open API function. Also, you should be aware that ov_open, once successful, takes complete possession of the file resource. After you have opened a file using ov_open, you must close it using ov_clear, not fclose or any other function. Our wrapper functions take care of all of this, here's what our initialize function looks like:

int init_file_for_ogg_decode(wchar_t *filename, void **vf_out)
{
  // . . .

  
  int ov_ret = ov_open(file_ptr, static_cast<OggVorbis_File*>(vf_ptr), NULL, 0);

  if (ov_ret < 0)
  {
    // There was an error so cleanup now

    fclose(file_ptr);
    free(vf_ptr);

    // Return the ifod_err_ code

    return err_code;
  }
    
  // Copy the memory pointer to the caller

  *vf_out = vf_ptr;
  
  return 0;  // success

}

The ogg_decode_one_vorbis_packet function writes PCM (Pulse Code Modulation) data into the given buffer and returns the number of bytes written into that buffer. First it calls the ov_read which returns up to the specified number of bytes of decoded PCM audio in the requested endianness, signedness, and word size. If the audio is multichannel, the channels are interleaved in the output buffer. This function is used to decode a Vorbis file within a loop. Our C# application, which we'll see later, will be doing just that.

Next it calls ov_info which returns the vorbis_info struct for the specified bitstream. This allows us to return the number of channels in the bitstream, and the sampling rate of the bitstream to our C# application. There are basically two errors that can occur: OV_HOLE which indicates there was an interruption in the data, and OV_EBADLINK which indicates that an invalid stream section was supplied, or the requested link is corrupt.

The Ogg Vorbis format allows for multiple logical bitstreams to be combined (with restrictions) into a single physical bitstream. Note that the Vorbisfile API could more or less hide the multiple logical bitstream nature of chaining from an application but, when reading audio back, the application must be aware that multiple bitstream sections do not necessarily use the same number of channels or sampling rate. The Ogg Vorbis documentation provides more information on Ogg logical bitstream framing.

int ogg_decode_one_vorbis_packet(void *vf_ptr, 
            void *buf_out, int buf_byte_size, 
            int ogg_sample_size, 
            int *channels_cnt, int *sampling_rate, 
            int *err_ov_hole_cnt, int *err_ov_ebadlink_cnt)
{
  // . . .

  
  for (bytes_put_in_buf = 0;;)
  {
      long ov_ret = ov_read(static_cast<OggVorbis_File*>(vf_ptr), 
        static_cast<char*>(buf_out), buf_byte_size, 0, 
        word_size, want_signed, &bitstream);
      
      if (ov_ret == 0)  // at EOF

      {
         break;
      }
      else if (ov_ret < 0)
      {
          // An error occurred, bad ogg data of some kind

          if (ov_ret == OV_HOLE)
              ++(*err_ov_hole_cnt);
          else if (ov_ret == OV_EBADLINK)
              ++(*err_ov_ebadlink_cnt);
      }
      else 
      {
          assert(ov_ret <= buf_byte_size);
  
          vorbis_info* vi_ptr = ov_info(static_cast<OggVorbis_File*>(vf_ptr), 
                                                                      bitstream);
          if (vi_ptr != NULL)
          {
              // Number of channels in the bitstream

              *channels_cnt = vi_ptr->channels;

              // Sampling rate of the bitstream

              *sampling_rate = vi_ptr->rate;
          }
          
          bytes_put_in_buf = ov_ret;
          break;
      }
  }
  
  return bytes_put_in_buf;
}

After a bitstream has been opened using ov_open and decoding is complete, an application must call ov_clear to clear the decoder's buffers and close the file. The ogg_final_cleanup function does this by calling this function, it also frees the memory pointed to by vf_out. You can take a look at the Vorbisfile API documentation for more information on any of these functions.

int ogg_final_cleanup(void *vf_ptr)
{
  int ret = 0;
  
  if (vf_ptr != NULL)
  {
      ret = ov_clear(static_cast<OggVorbis_File*>(vf_ptr));
      // non-zero is failure

      free(vf_ptr);
  }
  
  return ret;
}

The .NET Ogg Vorbis Library

The Microsoft .NET 1.1 Framework has no sound playing classes, so to play the waveform data constructed from the decoded Ogg Vorbis file data, there are basically two choices. The first is to write the waveform data out as a WAV file, and then use quartz.dll (on Win98 and later) to play that WAV file. The disadvantage of this choice is that WAV files can be very large (e.g. a 5.5 MB Ogg Vorbis file was tested and resulted in a 67 MB WAV file), and playback can't begin until after the entire WAV file has been written out (e.g. decoding that 5.5 MB Ogg Vorbis file and writing out a WAV file takes more than 20 seconds on a 1.6 GHz P4 PC). The other choice is to use methods in managed DirectX which means there's no need to write out any WAV file, and we can play the waveform data as it is generated, so playback can begin much quicker than the first approach. The TrayGames client already ensures that the managed DirectX APIs are installed on target computers so this was not an issue for us and it's the choice we went with.

The OggPlay class is the main class that your application will be using. Its constructor creates a new DirectX Sound device, sets the cooperative level and sample size of the Ogg Vorbis file.

public OggPlay(Control owner, OggSampleSize wantedOggSampleSize)
{
  // Set DirectSoundDevice

  DirectSoundDevice = new Device();

  // NOTE: The DirectSound documentation recommends

  // CooperativeLevel.Priority for games

  DirectSoundDevice.SetCooperativeLevel(owner, 
                    CooperativeLevel.Priority);

  // Set OggSampleSize

  OggFileSampleSize = wantedOggSampleSize;
}

The owner parameter is used by the DirectSound SetCooperativeLevel method, which defines its owner parameter as "The System.Windows.Forms.Control of the application that is using the Device object". This should probably be your application's main window. The wantedOggSampleSize parameter is either 8 bits or 16 bits. 8-bit sample size has lower quality but is faster and takes less memory than 16-bit sample size. If your application's Ogg Vorbis files are encoded with 8-bit sample size, then choose 8 (you can also choose 16, but it's wasteful and gains nothing if the Ogg Vorbis sources are only 8-bit). If your application's Ogg Vorbis files are encoded with 16-bit sample size, then choose 16 to get the full sound quality during playback, or choose 8, or give the user the option of choosing 8, if you want to minimize playback resource requirements. If your application's Ogg Vorbis files are a mixture (some are encoded with 8-bit sample size and others are encoded with 16-bit sample size), then choose whichever you think is best (either setting, 8 or 16 bits, will play all the Ogg Vorbis files).

The TgPlayOgg library declares two events with delegates and an event argument class (which defines data for both events) for playing and stopping Ogg Vorbis files. The PlayOggFileResult event (PlayOggFileEventHandler delegate) is used for event notification when the PlayOggFile method completes, while the StopOggFileNow event (StopOggFileEventHandler delegate) is used when the client wishes to interrupt playback prematurely. Here's a look at the data members of the event argument class.

public sealed class PlayOggFileEventArgs : EventArgs
{
  private bool success;
  // If !Success then this is the explanation for the failure

  private string reasonForFailure;
  // The value of the playID parameter when PlayOggFile() was called

  private int playId;
      
  public int ErrorHoleCount,
      // Count of encountered OV_HOLE errors during decoding

      // indicates there was an interruption in the data.

    ErrorBadLinkCount;
      // Count of encountered OV_EBADLINK errors during decoding

      // indicates that an invalid stream

      // section was supplied to libvorbisfile, 

  // . . .

}

OggPlay provides two simple methods PlayOggFile and StopOggFile. PlayOggFile plays the Ogg Vorbis file specified by the fileName parameter. The playId parameter is an arbitrary value determined by the user, and it is returned in the raised PlayOggFileResult event. This event is raised by PlayOggFileThreadProc. In your event-handler code, you can use the returned playID to know which specific PlayOggFile call resulted in that handled event. This is why your application should attach to the PlayOggFileEventHandler delegate.

public void PlayOggFile(string fileName, int playId)
{
  PlayOggFileEventArgs EventArgs = new PlayOggFileEventArgs(playId);
  
  // Decode the ogg file in a separate thread

  PlayOggFileThreadInfo pofInfo = new PlayOggFileThreadInfo(
    EventArgs, fileName, 
    OggFileSampleSize == OggSampleSize.EightBits ? 8 : 16,
    DirectSoundDevice, this);
  
  Thread PlaybackThread = new Thread(new 
         ThreadStart(pofInfo.PlayOggFileThreadProc));
  PlaybackThread.Start();
  Thread.Sleep(0);
}

StopOggFile raises the StopOggFileNow event. This event will be handled by the PlayOggFileThreadProc method. Your application does not need to attach to the StopOggFileEventHandler delegate, but PlayOggFileThreadProc does of course.

public void StopOggFile(int playId)
{
  PlayOggFileEventArgs EventArgs = new PlayOggFileEventArgs(playId);
  StopOggFileNow(this, EventArgs);  
}

The OggPlay class contains the PlayOggFileThreadInfo class which is used as the thread class for the playback thread created in the PlayOggFile method of the OggPlay class. In a way, this class sits between the managed and unmanaged environments. It does work on behalf of OggPlay by making calls into the unmanaged Ogg Vorbis wrapper described above. The main method in this class is PlayOggFileThreadProc and we will look at some of the parts of this method now.

The first thing that PlayOggFileThreadProc does is initialize the Ogg Vorbis file for decoding by calling into the Ogg Vorbis wrapper. If an error is encountered during initialization, it's returned via the PlayOggFileEventHandler (see below). Note that the filename, sample rate, and DirectSound device are all passed to this class through its constructor. The constructor also registers the class' InterruptOggFilePlayback method to handle the StopOggFileNow.

  int ErrorCode = NativeMethods.init_file_for_ogg_decode(FileName, &vf);
  
  if (ErrorCode != 0)
  {
    // . . .

      
    oplay.PlayOggFileResult(this, EventArgs);
    return;
  }

Next PlayOggFileThreadProc creates the PCM byte array and passes it to the ogg_decode_one_vorbis_packet function. This function will pass back the first chunk of decoded Ogg Vorbis data and its size.

  // Get next chunk of PCM data, pin these so GC can't relocate them

  fixed(byte *buf = &PcmBuffer[0])
  {
    fixed(int *HoleCount = &EventArgs.ErrorHoleCount)
    {
      fixed(int *BadLinkCount = &EventArgs.ErrorBadLinkCount)
      {
        // NOTE: The sample size of the returned PCM data -- either 8-bit 

        //     or 16-bit samples -- is set by BitsPerSample

        PcmBytes = NativeMethods.ogg_decode_one_vorbis_packet(
          vf, buf, PcmBuffer.Length,
          BitsPerSample,
          &ChannelsCount, &SamplingRate,
          HoleCount, BadLinkCount);
      }
    }
  }

The first time we return from the ogg_decode_one_vorbis_packet function, we create DirectSound WaveFormat, BufferDescription, SecondaryBuffer, and Notify objects. WaveFormat is used to hold the format of the waveform audio data after it's been decoded. BufferDescription will describe the characteristics of the new buffer object, including the WaveFormat. The SecondaryBuffer has methods and properties used to manage the sound buffer. Notify allows us to set up notification triggers at different points during playback.

  int HoldThisManySamples = 
    (int)(SamplingRate * SecBufHoldThisManySeconds);
  
  // Set the format

  MyWaveFormat.AverageBytesPerSecond = AverageBytesPerSecond;
  MyWaveFormat.BitsPerSample = (short)BitsPerSample;
  MyWaveFormat.BlockAlign = (short)BlockAlign;
  MyWaveFormat.Channels = (short)ChannelsCount;
  MyWaveFormat.SamplesPerSecond = SamplingRate;
  MyWaveFormat.FormatTag = WaveFormatTag.Pcm;
  
  // Set BufferDescription

  MyDescription = new BufferDescription();
  
  MyDescription.Format = MyWaveFormat;
  MyDescription.BufferBytes = 
  SecBufByteSize = HoldThisManySamples * BlockAlign;
  MyDescription.CanGetCurrentPosition = true;
  MyDescription.ControlPositionNotify = true;
  
  // Create the buffer

  SecBuf = new SecondaryBuffer(MyDescription, DirectSoundDevice);
  
  // Set 3 notification points, at 0, 1/3, and 2/3 SecBuf size

  MyNotify = new Notify(SecBuf);
  
  BufferPositionNotify[] MyBufferPositions = new BufferPositionNotify[3];

  MyBufferPositions[0].Offset = 0;
  MyBufferPositions[0].EventNotifyHandle = 
            SecBufNotifyAtBegin.Handle;
  MyBufferPositions[1].Offset = 
            (HoldThisManySamples / 3) * BlockAlign;
  MyBufferPositions[1].EventNotifyHandle = 
            SecBufNotifyAtOneThird.Handle;
  MyBufferPositions[2].Offset = 
            ((HoldThisManySamples * 2) / 3) * BlockAlign;
  MyBufferPositions[2].EventNotifyHandle = 
            SecBufNotifyAtTwoThirds.Handle;
  
  MyNotify.SetNotificationPositions(MyBufferPositions);

After these objects are prepared, we load the decoded PCM data into a MemoryStream object. This stream is written into the DirectSound buffer object and then played using the asynchronous Play method. This process is repeated until we reach the end of the Ogg Vorbis file. We must be aware that multiple bitstream sections do not necessarily use the same number of channels or sampling rate (we refer to this as its format). While we can handle a different format at the start of a new Ogg Vorbis file, we can't handle a format change during the playback of a file. Besides reaching the end of file, or an error, this is another reason the library will stop playback.

  // Copy the new PCM data into PCM memory stream

  PcmStream.SetLength(0);
  PcmStream.Write(PcmBuffer, 0, PcmBytes);
  PcmStream.Position = 0;
  PcmStreamNextConsumPcmPosition = 0;
  
  // Initial load of secondary buffer

  if (SecBufInitialLoad)
  {
    int WriteCount = (int)Math.Min(
      PcmStream.Length,
      SecBufByteSize - SecBufNextWritePosition);
    
    if (WriteCount > 0)
    {
      SecBuf.Write(
        SecBufNextWritePosition,
        PcmStream,
        WriteCount,
        LockFlag.None);
    
      SecBufNextWritePosition += WriteCount;
      PcmStreamNextConsumPcmPosition += WriteCount;
    }
    
    if (SecBufByteSize == SecBufNextWritePosition)
    {
      // Done filling the buffer

      SecBufInitialLoad = false;
      SecBufNextWritePosition = 0;

      // So start the playback

      // NOTE: Play does the playing in its own thread

      SecBuf.Play(0, BufferPlayFlags.Looping);
      Thread.Sleep(0);
      //yield rest of timeslice 

      //so playback can start right away

    }
    else
    {
      continue;  // Get more PCM data

    }
  }

Points of Interest

Those are pretty much the highlights of the sample, TgPlayOgg, and TgPlayOgg_vorbisfile projects. These projects are interesting if you want to learn about decoding Ogg Vorbis audio files or as an example of how to call unmanaged code from the managed .NET environment. If you are interested in checking out the full TGSDK for producing your own multi-player online games, you can get it at the TrayGames web site. You may also want to check out the Ogg Vorbis web site to learn more about their encoding format and the many tools for manipulating it.

Revision History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
Generalinternet stream
dfss
0:47 2 May '09  
How i can play a internet stream ?
GeneralUsing OpenAL instead of DirectX
Member 3197220
16:46 11 Sep '08  
It would pretty cool to see this demo powered by OpenAL instead of DirectX for sound output Big Grin
In fact, I will try to play around with the code a bit to see if I can pull it off.
If I am successful, and if anyone is interested, I can put the code somewhere where people can grab it.
Good job with this tutorial!
GeneralRe: Using OpenAL instead of DirectX
ConHulio
3:05 27 Jul '09  
I have already done this.

I am currently using OGG with OpenAL.

I have managed to re-create the decoding part for OGG, and also implemented it for OpenAL.

soon i will post it into CodeProject.
GeneralLooping an ogg file
munawar0009
12:43 23 Jun '08  
Hi,
I enjoy your library quite a bit, but have one thing I can't figure out. I handle the PlayOggFileResult event when the ogg is done playing so that I can loop it. However, there is a significant gap between the last bit of the track and when the tracks tarts playing again. IE: there is a pause before the track starts to play again. The way I'm looping it is like this:
in event handler method:
OPM.PlayOggFile("track to loop.ogg", 1, 0, 0, SampleSize.SixteenBits)
How can I get the file to loop mor seemlessly?
Thanks for any help!
GeneralRe: Looping an ogg file
doyouwantkarate
17:10 9 Jul '08  
Are you sure your file doesn't have some "quiet time" at the beginning or end of the file?

If there is a second of empty air encoded at the end of your file, that'd do it...
GeneralMemory Leak
ConHulio
1:13 21 Apr '08  
Hi Perry,

Im having some trouble with your latest code from April 07.

Im using it to play songs one after each other. Its playing from a MemoryStream.

Problem is - it seems as though there is a memory leak.


I use the normal InitialiseOggPlayer at the start of form creation, then i use this snippet to play the song.

item.MediaBuffer.Position = 0;
oggPlay.PlayOggFile(item.MediaBuffer.GetBuffer(), item.ID, 0, 0);


at the end of each song played, when it calls PlayOggFileResult, i call this to try and recover the memory.

GC.Collect()


but it doesnt seem to do anything?, do you have any ideas? is the buffers cleanup at the end of the Ogg play in the OggPlayer code ?


thanks
Richard
QuestionIs it free for commercial use?
Member 1776092
1:32 16 Apr '08  
Could you please let us know if it can be used for free in commercial project?
Generallicence
jury_mart
6:50 10 Dec '07  
Hi! Can i use it for free in my commercial product?
Questionhow can i play mp3 file??
Ibrahim Dwaikat
10:19 7 Nov '07  
Hi


I need to play MP3 file as u did with ogg... how can i do that using DirectSound (C#)??? or at least from where can i get a sample code for that???Confused Confused Confused

Visit Me

www.engibrahim.tk

AnswerRe: how can i play mp3 file??
Perry Marchant
10:47 7 Nov '07  
Sorry I don't have an answer for you. The point of this project was to *avoid* playing .mp3 files.

Perry

GeneralRe: how can i play mp3 file??
canozurdo
4:42 27 Jun '08  
What if you have the mp3 byte array and want to convert to ogg? i mean... this project is excellent, but there are still many mp3 files around, so i want to convert the mp3 byte array to ogg format... do you know how can i achieve this? thanks in advance

Bye

QuestionPlayOGGfile on .Net compact framework?
AudreyBG
0:34 23 Aug '07  
Hi,

Basic question is: Can I use your project with the .Net compact framework? I am developping an application on Windows Mobile 5.0 and I need a way to play OGG files within my application.

Here's the context: I've been using Microsoft's Speech SDK to generate .wav files from text files, with their text-to-speech module. Those files were replayed later on on a pocket PC, using the PlaySound function (from coredll).
Since I need to transfer those files on a pocket PC so that they can be replayed later on, I've soon discovered a memory issue: .Wav files were way too big for my pocket PC.

So at the moment, I'm investigating another solution, which would consist on converting my .wav files to .ogg files. (I still need to figure out how I'm going to convert them. If someone happens to have a solution, please let me know)

Then I would transfer those ogg files to my pocket PC, and I would need the correct DLL to ensure I can play back those files through my application that will be running on my pocket PC.

Thanks very much for your help!


GeneralCan I place these sources on my page?
anb0674
5:22 17 Jun '07  
Hi,
I'm interested in handling Ogg Vorbis on the .NET platform too. Can I place your sources on my Ogg Vorbis related Web page, keeping your copyright and any info you might wish to provide along with it?
Andrei Borovsky
GeneralRe: Can I place these sources on my page?
Perry Marchant
12:38 22 Jun '07  
Yes you can place the source on your web page, as long as you include my name and a link to my web site www.traygames.com. Thanks.

Perry

Generalwaveform api for C# and .NET
AdamSlosarski
11:50 6 Feb '07  
waveform audio interface you can hear in your .net apps[^]

Testing your code against performance will keep you running with good scalability and maintenance for years.
NTime.exe - the free tool for real developers of high scalability applications!

QuestionReading the Comment Data in Vorbis ogg file
Justin Hull
7:30 24 Dec '06  
Mr. Merchant,

The article and code examples are great. Though not stroung in c# I can follow much of the process. My question (comment) relates not to the playing of the ogg file, but to reading the header (comment) data. As a side project i would like to develop a small application that will read all my mjusic files and develop a database with which I can edit tags, create playlists, etc. I know there are programs on the market, this is just for fun.

I can read MP3 formats, but have gotten mired in the vorbis structures. WIth your experience in using vorbis libraries:

1 - Is it difficult to use the vorbis DLLs just to read the comment structure?
2 - Is it difficult to ise the vorbis DLLs in vb.net? (for expediencey I'd rather code in vb.net)
3 - Are there any basic examples of reading/writing "tag" information for ogg(vorbis files)?

I am going to download the source code of your project and slog throuh, but any help will be appreciated. In the end I think it would be great to provide a .net wrapper class that provided easier access to vobis processing, but that is for down the road.

Justin
Questionplaying audio files
t4urean
6:29 7 Aug '06  
HI all

My project has a method that continuously reads a block of PCM data from an external radio receiver connected to the computer through USB.

Is there and way to listen to that signal through C#. IS PCM just like audio signal … if not how do I convert it into audio.

o O º(`'·.,(`'·., ☆,.·''),.·'')º O o°
»·'"`»* *☆ t4ure4n ☆* *«·'"`«
°o O º(,.·''(,.·'' ☆`'·.,)`'·.,)º O o°

GeneralCan this code be distributed again?
yusk
5:30 21 Apr '06  
I am not good at English.
As for this code, is the re-distribution possible?
At that time, may I open the code of this Ogg Vorbis Player to the public though it wants to open the project thing and the program that I made to the public to the site?

GeneralSound stops playing when window looses focus
pietdejong
1:22 4 Mar '06  
I have downloaded your demo application.
When the application looses focus ( ie I minimize it ) it stops playing the sound. This seems to be a directX issue, any ideas ?

Many thanks
Piet

GeneralRe: Sound stops playing when window looses focus
Perry Marchant
6:25 4 Mar '06  
It's not an issue with DirectX, but an option. Add the following code at the end of the block of code that instaniates the BufferDescription object in the playback thread:

// Tells the primary buffer to continue play this buffer,
// even if the app loses focus.
MyDescription.GlobalFocus = wantGlobalFocus;

Where wantGlobalFocus is a boolean type. Set it to True if you want the ogg file to keep playing on loss of focus, False otherwise.

I've added this as an option to my higher level class and converted the libary to .NET 2.0. For now these enhancements are only available in the TGSDK version of the library, so the download here can still support .NET 1.1.

Perry

-- modified at 11:27 Saturday 4th March, 2006
GeneralUsing this code
titan_pi8
9:10 2 Mar '06  
Hello,

Is it allowed to use (part of) this code in a commercial application?
GeneralRe: Using this code
Perry Marchant
11:11 2 Mar '06  
Yes you may use this code as part of a commercial application. If you use the update code (found only in the TGSDK) then I only ask that you leave the comments at the top of the file in tact.

Perry
GeneralDirectShow / DMO
Jonas Beckeman
22:05 20 Dec '05  
It seems a bit strange to implement a decoder this way, when there are Windows standards for audio decoding - but maybe DirectShow is too slow in managed code..?

Have you considered of writing it as a DirectShow decoder filter and play back sound through a DS filter graph?
GeneralRe: DirectShow / DMO
Perry Marchant
12:38 21 Dec '05  
If I understand your thought... I should have probably mentioned in the introduction to my article that one of the goals for this library was to have as much of it as possible done in managed code. This is why I chose to make only the low level Vorbis API calls in unmanaged C++ code, and do everything else (including the decoding) in C#. The results are actually quite nice, especially if you are using it from the managed environment.

Perry
GeneralRe: DirectShow / DMO
Jonas Beckeman
13:48 21 Dec '05  
It should be possible to write a managed ogg DMO decoder though, which allows you to harness the power of filter graphs (switch decoder on the fly, apply filters etc). The DirectShowLib wrapper could be one way to go.

The optimal solution however would be to write it as module in a cross-platform signal processing system, but I don't know if there is anything like that (AFAIK DirectShow has no equivalent in mono). ASIO is audio only I believe.

Sorry for wandering OT a bit, I'm investigating these things for a future project, trying to get a feel for which direction people are going with managed DSP.


Last Updated 4 Apr 2007 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010