0
0

Hi guys,

It is not immediately obvious how to loop netstreams. I came up with the following, which works. Is this the best way to do it ? I would have expected it to loop internally due to the FMOD_LOOP_NORMAL flag, but this does not currently appear to be the case …

Apologies if I’ve missed something blindingly obvious.

Best regards,
Steve Williams
Director
Advance Software.

Netstream main.cpp with modifications to support looping ..

[code:wdlbtxoz]

/*===============================================================================================
NetStream Example
Copyright (c), Firelight Technologies Pty, Ltd 2004-2008.

This example shows how to play streaming audio from the internet.

Update: Now with optional looping.

===============================================================================================*/

include <windows.h>

include <stdio.h>

include <conio.h>

include "../../api/inc/fmod.hpp"

include "../../api/inc/fmod_errors.h"

void ERRCHECK(FMOD_RESULT result)
{
if (result != FMOD_OK)
{
printf("FMOD error! (%d) %s\n", result, FMOD_ErrorString(result));
//exit(-1);
}
}

int main(int argc, char *argv[])
{
FMOD::System *system = NULL;
FMOD::Sound *sound = NULL;
FMOD::Channel *channel = NULL;
FMOD_RESULT result;
int key;
unsigned int version;
bool looping = true;

printf(&quot;===================================================================\n&quot;);
printf(&quot;NetStream Example.  Copyright (c) Firelight Technologies 2004-2008.\n&quot;);
printf(&quot;===================================================================\n\n&quot;);

if (argc &lt; 2)
{
    printf(&quot;Usage:   netstream &lt;url&gt;\n&quot;);
    printf(&quot;Example: netstream http://www.fmod.org/stream.mp3\n\n&quot;);
    return -1;
}

/*
    Create a System object and initialize.
*/
result = FMOD::System_Create(&amp;system);
ERRCHECK(result);

result = system-&gt;getVersion(&amp;version);
ERRCHECK(result);

if (version &lt; FMOD_VERSION)
{
    printf(&quot;Error!  You are using an old version of FMOD %08x.  This program requires %08x\n&quot;, version, FMOD_VERSION);
    return 0;
}

result = system-&gt;init(1, FMOD_INIT_NORMAL, 0);
ERRCHECK(result);

/*
    Bump up the file buffer size a little bit for netstreams (to account for lag).
*/
result = system-&gt;setStreamBufferSize(64*1024, FMOD_TIMEUNIT_RAWBYTES);
ERRCHECK(result);

printf(&quot;Press space to pause, Esc to quit\n\n&quot;);

/*
Playback loop
*/
do
{
unsigned int ms = 0, percent = 0;
bool playing = false;
bool paused = false;
bool starving = false;
FMOD_OPENSTATE openstate;

    if (kbhit())
    {
       key = getch();

       switch (key)
       {
           case ' ' :
           {
              if (channel)
              {
                  bool paused;
                  channel-&gt;getPaused(&amp;paused);
                  channel-&gt;setPaused(!paused);
              }
              break;
           }

           case 27:
              looping = false;
              break;
        }
   }

   if (!sound)
   {
      result = system-&gt;createSound(argv[1], FMOD_HARDWARE | FMOD_2D | FMOD_CREATESTREAM | FMOD_NONBLOCKING | FMOD_LOOP_NORMAL,  0, &amp;sound);
      ERRCHECK(result);
   }

   if (!channel)
   {
       result = system-&gt;playSound(FMOD_CHANNEL_FREE, sound, false, &amp;channel);
   }

   system-&gt;update();

   for (;;)
   {
        FMOD_TAG tag;
        if (sound-&gt;getTag(0, -1, &amp;tag) != FMOD_OK)
        {
           break;
        }
        if (tag.datatype == FMOD_TAGDATATYPE_STRING)
        {
           printf(&quot;%s = %s (%d bytes)       \n&quot;, tag.name, tag.data, tag.datalen);
        }
   }

   result = sound-&gt;getOpenState(&amp;openstate, &amp;percent, &amp;starving);

   if (result != FMOD_OK)
   {
       result  = sound-&gt;release();
       sound   = NULL;
       channel = NULL;
   }

   if (channel)
   {
        result = channel-&gt;getPaused(&amp;paused);
        ERRCHECK(result);
        result = channel-&gt;isPlaying(&amp;playing);
        ERRCHECK(result);
        result = channel-&gt;getPosition(&amp;ms, FMOD_TIMEUNIT_MS);
        ERRCHECK(result);
        result = channel-&gt;setMute(starving);
        ERRCHECK(result);
   }

   printf(&quot;Time %02d:%02d:%02d : %s : (%3d%%%) %s     \r&quot;, ms / 1000 / 60, ms / 1000 % 60, ms / 10 % 100, openstate == FMOD_OPENSTATE_BUFFERING ? &quot;Buffering...&quot; : openstate == FMOD_OPENSTATE_CONNECTING ? &quot;Connecting...&quot; : paused ? &quot;Paused       &quot; : playing ? &quot;Playing      &quot; : &quot;Stopped      &quot;, percent, starving ? &quot;STARVING&quot; : &quot;        &quot;);

   Sleep(10);

} while (looping);


printf(&quot;\n&quot;);

printf(&quot;Shutting down.\n&quot;);


/*
   If we pressed escape before it is ready, wait for it to finish opening before we release it.
*/
if (sound)
{
   do
  {
     FMOD_OPENSTATE openstate;

     result = sound-&gt;getOpenState(&amp;openstate, 0, 0);
     ERRCHECK(result);

     if ( (openstate == FMOD_OPENSTATE_READY) || (openstate == FMOD_OPENSTATE_STREAMING))
     {
         break;
     }

     printf(&quot;Waiting for sound to finish opening before trying to release it....\r&quot;);

     Sleep(10);

  } while (1);
}

if (channel)
{
    result = channel-&gt;stop();
    ERRCHECK(result);
}

if (sound)
  result = sound-&gt;release();

result = system-&gt;close();
ERRCHECK(result);
result = system-&gt;release();
ERRCHECK(result);

return 0;

}

[/code:wdlbtxoz]

  • You must to post comments
0
0

netstreams dont support seeking so as a side effect of that, they won’t loop.
You could possibly do it using 2 streams, some time before the 1st one ends, open the 2nd one, buffer it, then start it when the 1st one ends.
You could make it sample accurate using Channel::setDelay so that the 2nd one starts exactly when the first one ends.
You would then repeat the process.

  • You must to post comments
0
0

Hi Brett,

Thanks for the response, that’s almost a solution.

The missing part is when the stream turns out to be short enough to fit into one memory buffer. When this occurs, you’d want to keep the entire stream in memory and loop it as normal. Otherwise, you’ll keep reloading something unnecessarily and would be wasting bandwidth.

The problem is that it is not always possible to tell ahead of time how long a sample is. Ideally the API would handle all this internally, with the caller specifying a maximum buffer size to cache..

Until then, I’ll figure out a temporary solution.

Cheers,
Steve.

  • You must to post comments
Showing 2 results
Your Answer

Please first to submit.