Continuous Memory Player

JB
Jonathan Brockerville
Fri, Sep 20, 2019 6:44 PM

Hi,

I'm trying to create a continuous memory player (for lack of a better
description). Here's what I'm doing. Create a memory player with looping
enabled. When it gets to EOF the callback method is invoked. I update the
memory with new audio data and return true. This basically works, however,
I keep getting a little snippet of the previous audio data. It's as if the
callback is invoked slightly after the rewind happens. I assumed that
execution would be: play memory, EOF callback, rewind memory, play memory,
etc. That doesn't seem to be the case. What am I getting wrong here?

I also tried disabling looping, but I couldn't figure out how to manually
rewind the memory and transmit again. Destroying the memory player,
re-creating it, and pointing at the same but updated buffer seems heavy
handed.

I've essentially copied and pasted the AudioMediaPlayer class and used the
memory player methods under the hood. That is,
https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm

Should I be approaching this differently or...?

Thanks,
-brock

Hi, I'm trying to create a continuous memory player (for lack of a better description). Here's what I'm doing. Create a memory player with looping enabled. When it gets to EOF the callback method is invoked. I update the memory with new audio data and return true. This basically works, however, I keep getting a little snippet of the previous audio data. It's as if the callback is invoked slightly after the rewind happens. I assumed that execution would be: play memory, EOF callback, rewind memory, play memory, etc. That doesn't seem to be the case. What am I getting wrong here? I also tried disabling looping, but I couldn't figure out how to manually rewind the memory and transmit again. Destroying the memory player, re-creating it, and pointing at the same but updated buffer seems heavy handed. I've essentially copied and pasted the AudioMediaPlayer class and used the memory player methods under the hood. That is, https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm Should I be approaching this differently or...? Thanks, -brock
MA
Michael A. Leonetti
Fri, Sep 20, 2019 7:12 PM

Maybe not helpful, but if it were me I'd std::cout the heck out of the
code to see in what sequence everything occurs. I would, do this:

  1. Use some serious print lines as many places as you can, especially
    on the EOF function, and the functions where you're writing your
    audio source also with memory locations.
  2. Create output files with whatever the same data is that you write to
    the media buffers, write to the files (name them each differently
    each time EOF occurs close the file and reopen). Afterwards open the
    files with audacity or something (especially if they're raw PCM) and
    hear what they sound like.

This is how I debug stuff like this. If the output files are also weird,
then that might tell you something about what's going on.

Here's a quick and dirty (and ugly) library I wrote for just such
occasions that you can easily turn on and off.

// Intelligent debug COUT code to stop code duplication

#ifndef DEBUGCOUTS_H
#define DEBUGCOUTS_H

#if defined( DEBUG )
#include <iomanip>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#endif

// Define the couts
#define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' << LINE <<
' ' << PRETTY_FUNCTION << "]: "
#define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' << LINE <<
' ' << PRETTY_FUNCTION << "]: "
#define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' << LINE
<< ' ' << PRETTY_FUNCTION << "]: "

// Quick class for differentiating when we don't have a string
class DebugCouts_NoStringType : public std::string {}; // Make it a
string type

#endif

/**
 * Prefix name of the logger functions you want.
 * For example DEBUG_COUTS( MyClassFile, true )
 * will make the log functions COUT_LOG_INFO, COUT_LOG_WARN,
COUT_LOG_ERROR
 * For use like
 * COUT_LOG_INFO( MyClassFile ) << "This will get thinged?";
 * Or just
 * COUT_LOG_INFO( MyClassFile );
 * Setting the second parameter ACTIVE to "false" causes everything to
be compiled out except for the iostream code which adds some bloat
 */
#if defined( DEBUG )

#define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED>
class PREFIX##_Log
{
    public:
    template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log( STRING_TYPE level=STRING_TYPE() )
    {
        if( ENABLED and
!std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
    {
        auto now = std::chrono::system_clock::now();
        auto seconds =
std::chrono::time_point_caststd::chrono::seconds( now );
        auto microseconds =
std::chrono::duration_caststd::chrono::microseconds( now-seconds );
        auto t = std::chrono::system_clock::to_time_t( now );
            char b[ 64 ];
            if( !std::strftime( b, sizeof( b ), "%F, %T",
std::localtime( &t ) ) )
            {
                b[ 0 ] = 0;
            }
        std::cout << "["#PREFIX" " << level << ' ' << b << '.' <<
microseconds.count() << "] (" << std::hex <<
std::this_thread::get_id() << std::dec << ") ";
    }
    }
    template<class T>
        PREFIX##_Log &operator <<( const T &v )
        {
            if( ENABLED )
                std::cout << v;
            return( *this );
        }
    ~PREFIX##_Log()
    {
        if( ENABLED )
        std::cout << std::endl;
    }
};
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() )
{
    if( !ACTIVE )
        return( PREFIX##_Log<ACTIVE>() );
    PREFIX##_Log<ACTIVE> log( "ERROR" );
    if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
        log << message << ": ";
    return( log );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() )
{
    if( !ACTIVE )
        return( PREFIX##_Log<ACTIVE>() );
    PREFIX##_Log<ACTIVE> log( "WARN" );
    if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
        log << message << ": ";
    return( log );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() )
{
    if( !ACTIVE )
        return( PREFIX##_Log<ACTIVE>() );
    PREFIX##_Log<ACTIVE> log( "INFO" );
    if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
        log << message << ": ";
    return( log );
}

#else

// Blank ones
#define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED>
class PREFIX##_Log
{
    public:
    template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log( STRING_TYPE level=STRING_TYPE() )
    {
    }
    template<class T>
        PREFIX##_Log &operator <<( const T &v )
        {
            return( *this );
        }
    ~PREFIX##_Log()
    {
    }
};
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() )
{
    return( PREFIX##_Log<ACTIVE>() );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() )
{
    return( PREFIX##_Log<ACTIVE>() );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() )
{
    return( PREFIX##_Log<ACTIVE>() );
}

#endif

Michael A. Leonetti
As warm as green tea

On 9/20/19 2:44 PM, Jonathan Brockerville wrote:

Hi,

I'm trying to create a continuous memory player (for lack of a better
description). Here's what I'm doing. Create a memory player with
looping enabled. When it gets to EOF the callback method is invoked. I
update the memory with new audio data and return true. This basically
works, however, I keep getting a little snippet of the previous audio
data. It's as if the callback is invoked slightly after the rewind
happens. I assumed that execution would be: play memory, EOF callback,
rewind memory, play memory, etc. That doesn't seem to be the case.
What am I getting wrong here?

I also tried disabling looping, but I couldn't figure out how to
manually rewind the memory and transmit again. Destroying the memory
player, re-creating it, and pointing at the same but updated buffer
seems heavy handed.

I've essentially copied and pasted the AudioMediaPlayer class and used
the memory player methods under the hood. That is,
https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm

Should I be approaching this differently or...?

Thanks,
-brock


Visit our blog: http://blog.pjsip.org

pjsip mailing list
pjsip@lists.pjsip.org
http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

Maybe not helpful, but if it were me I'd std::cout the heck out of the code to see in what sequence everything occurs. I would, do this: 1. Use some serious print lines as many places as you can, especially on the EOF function, and the functions where you're writing your audio source also with memory locations. 2. Create output files with whatever the same data is that you write to the media buffers, write to the files (name them each differently each time EOF occurs close the file and reopen). Afterwards open the files with audacity or something (especially if they're raw PCM) and hear what they sound like. This is how I debug stuff like this. If the output files are also weird, then that might tell you something about what's going on. Here's a quick and dirty (and ugly) library I wrote for just such occasions that you can easily turn on and off. > // Intelligent debug COUT code to stop code duplication > > #ifndef DEBUGCOUTS_H > #define DEBUGCOUTS_H > > #if defined( DEBUG ) > #include <iomanip> > #include <iostream> > #include <string> > #include <thread> > #include <chrono> > #endif > > // Define the couts > #define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' << __LINE__ << > ' ' << __PRETTY_FUNCTION__ << "]: " > #define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' << __LINE__ << > ' ' << __PRETTY_FUNCTION__ << "]: " > #define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' << __LINE__ > << ' ' << __PRETTY_FUNCTION__ << "]: " > > // Quick class for differentiating when we don't have a string > class DebugCouts_NoStringType : public std::string {}; // Make it a > string type > > #endif > > /** >  * Prefix name of the logger functions you want. >  * For example DEBUG_COUTS( MyClassFile, true ) >  * will make the log functions COUT_LOG_INFO, COUT_LOG_WARN, > COUT_LOG_ERROR >  * For use like >  * COUT_LOG_INFO( MyClassFile ) << "This will get thinged?"; >  * Or just >  * COUT_LOG_INFO( MyClassFile ); >  * Setting the second parameter ACTIVE to "false" causes everything to > be compiled out except for the iostream code which adds some bloat >  */ > #if defined( DEBUG ) > > #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ > class PREFIX##_Log \ > { \ >     public: \ >     template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \ >     { \ >         if( ENABLED and > !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >     { \ >         auto now = std::chrono::system_clock::now(); \ >         auto seconds = > std::chrono::time_point_cast<std::chrono::seconds>( now ); \ >         auto microseconds = > std::chrono::duration_cast<std::chrono::microseconds>( now-seconds ); \ >         auto t = std::chrono::system_clock::to_time_t( now ); \ >             char b[ 64 ]; \ >             if( !std::strftime( b, sizeof( b ), "%F, %T", > std::localtime( &t ) ) ) \ >             { \ >                 b[ 0 ] = 0; \ >             } \ >         std::cout << "["#PREFIX" " << level << ' ' << b << '.' << > microseconds.count() << "] (" << std::hex << > std::this_thread::get_id() << std::dec << ") "; \ >     } \ >     } \ >     template<class T> \ >         PREFIX##_Log &operator <<( const T &v ) \ >         { \ >             if( ENABLED ) \ >                 std::cout << v; \ >             return( *this ); \ >         } \ >     ~PREFIX##_Log() \ >     { \ >         if( ENABLED ) \ >         std::cout << std::endl; \ >     } \ > }; \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     if( !ACTIVE ) \ >         return( PREFIX##_Log<ACTIVE>() ); \ >     PREFIX##_Log<ACTIVE> log( "ERROR" ); \ >     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >         log << message << ": "; \ >     return( log ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     if( !ACTIVE ) \ >         return( PREFIX##_Log<ACTIVE>() ); \ >     PREFIX##_Log<ACTIVE> log( "WARN" ); \ >     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >         log << message << ": "; \ >     return( log ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     if( !ACTIVE ) \ >         return( PREFIX##_Log<ACTIVE>() ); \ >     PREFIX##_Log<ACTIVE> log( "INFO" ); \ >     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >         log << message << ": "; \ >     return( log ); \ > } > > #else > > // Blank ones > #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ > class PREFIX##_Log \ > { \ >     public: \ >     template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \ >     { \ >     } \ >     template<class T> \ >         PREFIX##_Log &operator <<( const T &v ) \ >         { \ >             return( *this ); \ >         } \ >     ~PREFIX##_Log() \ >     { \ >     } \ > }; \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     return( PREFIX##_Log<ACTIVE>() ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     return( PREFIX##_Log<ACTIVE>() ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() ) \ > { \ >     return( PREFIX##_Log<ACTIVE>() ); \ > } > > #endif Michael A. Leonetti As warm as green tea On 9/20/19 2:44 PM, Jonathan Brockerville wrote: > Hi, > > I'm trying to create a continuous memory player (for lack of a better > description). Here's what I'm doing. Create a memory player with > looping enabled. When it gets to EOF the callback method is invoked. I > update the memory with new audio data and return true. This basically > works, however, I keep getting a little snippet of the previous audio > data. It's as if the callback is invoked slightly after the rewind > happens. I assumed that execution would be: play memory, EOF callback, > rewind memory, play memory, etc. That doesn't seem to be the case. > What am I getting wrong here? > > I also tried disabling looping, but I couldn't figure out how to > manually rewind the memory and transmit again. Destroying the memory > player, re-creating it, and pointing at the same but updated buffer > seems heavy handed. > > I've essentially copied and pasted the AudioMediaPlayer class and used > the memory player methods under the hood. That is, > https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm > > Should I be approaching this differently or...? > > Thanks, > -brock > > > > _______________________________________________ > Visit our blog: http://blog.pjsip.org > > pjsip mailing list > pjsip@lists.pjsip.org > http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org
JB
Jonathan Brockerville
Mon, Sep 23, 2019 2:34 AM

Thanks, Michael. That is some serious console debugging code.  :)

Turns out I may have fluked into solving it. I can't explain it just yet,
but it feels right. I was trying buffers with different sample rates, byte
rates, and number of channels. One test didn't do the repeating thing. It
was a buffer size of double the byte rate. I changed all my tests to
allocate a buffer of double the byte rate and they all worked. We'll see if
that holds true with more rigorous testing though.  :P

On Fri, 20 Sep 2019 at 15:13, Michael A. Leonetti michael@theleonetti.com
wrote:

Maybe not helpful, but if it were me I'd std::cout the heck out of the
code to see in what sequence everything occurs. I would, do this:

1. Use some serious print lines as many places as you can, especially
on the EOF function, and the functions where you're writing your audio
source also with memory locations.
2. Create output files with whatever the same data is that you write
to the media buffers, write to the files (name them each differently each
time EOF occurs close the file and reopen). Afterwards open the files with
audacity or something (especially if they're raw PCM) and hear what they
sound like.

This is how I debug stuff like this. If the output files are also weird,
then that might tell you something about what's going on.

Here's a quick and dirty (and ugly) library I wrote for just such
occasions that you can easily turn on and off.

// Intelligent debug COUT code to stop code duplication

#ifndef DEBUGCOUTS_H
#define DEBUGCOUTS_H

#if defined( DEBUG )
#include <iomanip>
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#endif

// Define the couts
#define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' << LINE << ' '
<< PRETTY_FUNCTION << "]: "
#define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' << LINE << ' '
<< PRETTY_FUNCTION << "]: "
#define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' << LINE << '
' << PRETTY_FUNCTION << "]: "

// Quick class for differentiating when we don't have a string
class DebugCouts_NoStringType : public std::string {}; // Make it a string
type

#endif

/**

  • Prefix name of the logger functions you want.
  • For example DEBUG_COUTS( MyClassFile, true )
  • will make the log functions COUT_LOG_INFO, COUT_LOG_WARN, COUT_LOG_ERROR
  • For use like
  • COUT_LOG_INFO( MyClassFile ) << "This will get thinged?";
  • Or just
  • COUT_LOG_INFO( MyClassFile );
  • Setting the second parameter ACTIVE to "false" causes everything to be
    compiled out except for the iostream code which adds some bloat
    */
    #if defined( DEBUG )

#define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED>
class PREFIX##_Log
{
public:
template<typename STRING_TYPE=DebugCouts_NoStringType> PREFIX##_Log(
STRING_TYPE level=STRING_TYPE() )
{
if( ENABLED and
!std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
{
auto now = std::chrono::system_clock::now();
auto seconds = std::chrono::time_point_caststd::chrono::seconds(
now );
auto microseconds =
std::chrono::duration_caststd::chrono::microseconds( now-seconds );
auto t = std::chrono::system_clock::to_time_t( now );
char b[ 64 ];
if( !std::strftime( b, sizeof( b ), "%F, %T", std::localtime(
&t ) ) )
{
b[ 0 ] = 0;
}
std::cout << "["#PREFIX" " << level << ' ' << b << '.' <<
microseconds.count() << "] (" << std::hex << std::this_thread::get_id() <<
std::dec << ") ";
}
}
template<class T>
PREFIX##_Log &operator <<( const T &v )
{
if( ENABLED )
std::cout << v;
return( *this );
}
~PREFIX##_Log()
{
if( ENABLED )
std::cout << std::endl;
}
};
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() )
{
if( !ACTIVE )
return( PREFIX##_Log<ACTIVE>() );
PREFIX##_Log<ACTIVE> log( "ERROR" );
if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
log << message << ": ";
return( log );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() )
{
if( !ACTIVE )
return( PREFIX##_Log<ACTIVE>() );
PREFIX##_Log<ACTIVE> log( "WARN" );
if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
log << message << ": ";
return( log );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() )
{
if( !ACTIVE )
return( PREFIX##_Log<ACTIVE>() );
PREFIX##_Log<ACTIVE> log( "INFO" );
if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value )
log << message << ": ";
return( log );
}

#else

// Blank ones
#define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED>
class PREFIX##_Log
{
public:
template<typename STRING_TYPE=DebugCouts_NoStringType> PREFIX##_Log(
STRING_TYPE level=STRING_TYPE() )
{
}
template<class T>
PREFIX##_Log &operator <<( const T &v )
{
return( *this );
}
~PREFIX##_Log()
{
}
};
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() )
{
return( PREFIX##_Log<ACTIVE>() );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() )
{
return( PREFIX##_Log<ACTIVE>() );
}
template<typename STRING_TYPE=DebugCouts_NoStringType>
PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() )
{
return( PREFIX##_Log<ACTIVE>() );
}

#endif

Michael A. Leonetti
As warm as green tea

On 9/20/19 2:44 PM, Jonathan Brockerville wrote:

Hi,

I'm trying to create a continuous memory player (for lack of a better
description). Here's what I'm doing. Create a memory player with looping
enabled. When it gets to EOF the callback method is invoked. I update the
memory with new audio data and return true. This basically works, however,
I keep getting a little snippet of the previous audio data. It's as if the
callback is invoked slightly after the rewind happens. I assumed that
execution would be: play memory, EOF callback, rewind memory, play memory,
etc. That doesn't seem to be the case. What am I getting wrong here?

I also tried disabling looping, but I couldn't figure out how to manually
rewind the memory and transmit again. Destroying the memory player,
re-creating it, and pointing at the same but updated buffer seems heavy
handed.

I've essentially copied and pasted the AudioMediaPlayer class and used the
memory player methods under the hood. That is,
https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm

Should I be approaching this differently or...?

Thanks,
-brock


Visit our blog: http://blog.pjsip.org

pjsip mailing listpjsip@lists.pjsip.orghttp://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org


Visit our blog: http://blog.pjsip.org

pjsip mailing list
pjsip@lists.pjsip.org
http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

Thanks, Michael. That is some serious console debugging code. :) Turns out I may have fluked into solving it. I can't explain it just yet, but it feels right. I was trying buffers with different sample rates, byte rates, and number of channels. One test didn't do the repeating thing. It was a buffer size of double the byte rate. I changed all my tests to allocate a buffer of double the byte rate and they all worked. We'll see if that holds true with more rigorous testing though. :P On Fri, 20 Sep 2019 at 15:13, Michael A. Leonetti <michael@theleonetti.com> wrote: > Maybe not helpful, but if it were me I'd std::cout the heck out of the > code to see in what sequence everything occurs. I would, do this: > > 1. Use some serious print lines as many places as you can, especially > on the EOF function, and the functions where you're writing your audio > source also with memory locations. > 2. Create output files with whatever the same data is that you write > to the media buffers, write to the files (name them each differently each > time EOF occurs close the file and reopen). Afterwards open the files with > audacity or something (especially if they're raw PCM) and hear what they > sound like. > > This is how I debug stuff like this. If the output files are also weird, > then that might tell you something about what's going on. > > Here's a quick and dirty (and ugly) library I wrote for just such > occasions that you can easily turn on and off. > > // Intelligent debug COUT code to stop code duplication > > #ifndef DEBUGCOUTS_H > #define DEBUGCOUTS_H > > #if defined( DEBUG ) > #include <iomanip> > #include <iostream> > #include <string> > #include <thread> > #include <chrono> > #endif > > // Define the couts > #define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' << __LINE__ << ' ' > << __PRETTY_FUNCTION__ << "]: " > #define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' << __LINE__ << ' ' > << __PRETTY_FUNCTION__ << "]: " > #define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' << __LINE__ << ' > ' << __PRETTY_FUNCTION__ << "]: " > > // Quick class for differentiating when we don't have a string > class DebugCouts_NoStringType : public std::string {}; // Make it a string > type > > #endif > > /** > * Prefix name of the logger functions you want. > * For example DEBUG_COUTS( MyClassFile, true ) > * will make the log functions COUT_LOG_INFO, COUT_LOG_WARN, COUT_LOG_ERROR > * For use like > * COUT_LOG_INFO( MyClassFile ) << "This will get thinged?"; > * Or just > * COUT_LOG_INFO( MyClassFile ); > * Setting the second parameter ACTIVE to "false" causes everything to be > compiled out except for the iostream code which adds some bloat > */ > #if defined( DEBUG ) > > #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ > class PREFIX##_Log \ > { \ > public: \ > template<typename STRING_TYPE=DebugCouts_NoStringType> PREFIX##_Log( > STRING_TYPE level=STRING_TYPE() ) \ > { \ > if( ENABLED and > !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ > { \ > auto now = std::chrono::system_clock::now(); \ > auto seconds = std::chrono::time_point_cast<std::chrono::seconds>( > now ); \ > auto microseconds = > std::chrono::duration_cast<std::chrono::microseconds>( now-seconds ); \ > auto t = std::chrono::system_clock::to_time_t( now ); \ > char b[ 64 ]; \ > if( !std::strftime( b, sizeof( b ), "%F, %T", std::localtime( > &t ) ) ) \ > { \ > b[ 0 ] = 0; \ > } \ > std::cout << "["#PREFIX" " << level << ' ' << b << '.' << > microseconds.count() << "] (" << std::hex << std::this_thread::get_id() << > std::dec << ") "; \ > } \ > } \ > template<class T> \ > PREFIX##_Log &operator <<( const T &v ) \ > { \ > if( ENABLED ) \ > std::cout << v; \ > return( *this ); \ > } \ > ~PREFIX##_Log() \ > { \ > if( ENABLED ) \ > std::cout << std::endl; \ > } \ > }; \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() ) \ > { \ > if( !ACTIVE ) \ > return( PREFIX##_Log<ACTIVE>() ); \ > PREFIX##_Log<ACTIVE> log( "ERROR" ); \ > if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ > log << message << ": "; \ > return( log ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() ) \ > { \ > if( !ACTIVE ) \ > return( PREFIX##_Log<ACTIVE>() ); \ > PREFIX##_Log<ACTIVE> log( "WARN" ); \ > if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ > log << message << ": "; \ > return( log ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() ) \ > { \ > if( !ACTIVE ) \ > return( PREFIX##_Log<ACTIVE>() ); \ > PREFIX##_Log<ACTIVE> log( "INFO" ); \ > if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ > log << message << ": "; \ > return( log ); \ > } > > #else > > // Blank ones > #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ > class PREFIX##_Log \ > { \ > public: \ > template<typename STRING_TYPE=DebugCouts_NoStringType> PREFIX##_Log( > STRING_TYPE level=STRING_TYPE() ) \ > { \ > } \ > template<class T> \ > PREFIX##_Log &operator <<( const T &v ) \ > { \ > return( *this ); \ > } \ > ~PREFIX##_Log() \ > { \ > } \ > }; \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE message=STRING_TYPE() ) \ > { \ > return( PREFIX##_Log<ACTIVE>() ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE message=STRING_TYPE() ) \ > { \ > return( PREFIX##_Log<ACTIVE>() ); \ > } \ > template<typename STRING_TYPE=DebugCouts_NoStringType> > PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE message=STRING_TYPE() ) \ > { \ > return( PREFIX##_Log<ACTIVE>() ); \ > } > > #endif > > > > Michael A. Leonetti > As warm as green tea > > On 9/20/19 2:44 PM, Jonathan Brockerville wrote: > > Hi, > > I'm trying to create a continuous memory player (for lack of a better > description). Here's what I'm doing. Create a memory player with looping > enabled. When it gets to EOF the callback method is invoked. I update the > memory with new audio data and return true. This basically works, however, > I keep getting a little snippet of the previous audio data. It's as if the > callback is invoked slightly after the rewind happens. I assumed that > execution would be: play memory, EOF callback, rewind memory, play memory, > etc. That doesn't seem to be the case. What am I getting wrong here? > > I also tried disabling looping, but I couldn't figure out how to manually > rewind the memory and transmit again. Destroying the memory player, > re-creating it, and pointing at the same but updated buffer seems heavy > handed. > > I've essentially copied and pasted the AudioMediaPlayer class and used the > memory player methods under the hood. That is, > https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm > > Should I be approaching this differently or...? > > Thanks, > -brock > > > > _______________________________________________ > Visit our blog: http://blog.pjsip.org > > pjsip mailing listpjsip@lists.pjsip.orghttp://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org > > _______________________________________________ > Visit our blog: http://blog.pjsip.org > > pjsip mailing list > pjsip@lists.pjsip.org > http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org >
MA
Michael A. Leonetti
Mon, Sep 23, 2019 2:54 PM

Glad you solved it. I figured it might be not of much help.

It helps me a bunch though. I'm always getting into messes like that
where I even just need to print what the buffer sizes are resolving to.
I get mixed up a bunch with simple things in C/C++ like order of
operations and floating point/non-floating point maths.

For example, to calculate the buffer frames required for a given period
I use this:

                template<typename Rep,typename Period>
                static size_t calculateBufferFrames( const
std::chrono::duration<Rep,Period> &period, size_t samplerate )
                {
                    // Now how much period is in a second (because the
samplerate is a full second)
                    auto per_second =
std::chrono::duration_cast<std::chrono::duration<Rep,Period>>(
std::chrono::seconds( 1 ) );

                    // Now do the math
                    return(
period.count()*samplerate/per_second.count() );
                }

I used to have ( period.count()/per_second.count() )*samplerate which
works fine if you do it with a calculator, but will produce 0 if done
here. That caused me a bit of grief with the buffers until I did some
print lines.

Maybe I'm just inexperienced :D.

Michael A. Leonetti
As warm as green tea

On 9/22/19 10:34 PM, Jonathan Brockerville wrote:

Thanks, Michael. That is some serious console debugging code.  :)

Turns out I may have fluked into solving it. I can't explain it just
yet, but it feels right. I was trying buffers with different sample
rates, byte rates, and number of channels. One test didn't do the
repeating thing. It was a buffer size of double the byte rate. I
changed all my tests to allocate a buffer of double the byte rate and
they all worked. We'll see if that holds true with more
rigorous testing though.  :P

On Fri, 20 Sep 2019 at 15:13, Michael A. Leonetti
<michael@theleonetti.com mailto:michael@theleonetti.com> wrote:

 Maybe not helpful, but if it were me I'd std::cout the heck out of
 the code to see in what sequence everything occurs. I would, do this:

  1. Use some serious print lines as many places as you can,
     especially on the EOF function, and the functions where you're
     writing your audio source also with memory locations.
  2. Create output files with whatever the same data is that you
     write to the media buffers, write to the files (name them each
     differently each time EOF occurs close the file and reopen).
     Afterwards open the files with audacity or something
     (especially if they're raw PCM) and hear what they sound like.

 This is how I debug stuff like this. If the output files are also
 weird, then that might tell you something about what's going on.

 Here's a quick and dirty (and ugly) library I wrote for just such
 occasions that you can easily turn on and off.
 // Intelligent debug COUT code to stop code duplication

 #ifndef DEBUGCOUTS_H
 #define DEBUGCOUTS_H

 #if defined( DEBUG )
 #include <iomanip>
 #include <iostream>
 #include <string>
 #include <thread>
 #include <chrono>
 #endif

 // Define the couts
 #define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' <<
 __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: "
 #define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' <<
 __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: "
 #define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' <<
 __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: "

 // Quick class for differentiating when we don't have a string
 class DebugCouts_NoStringType : public std::string {}; // Make it
 a string type

 #endif

 /**
  * Prefix name of the logger functions you want.
  * For example DEBUG_COUTS( MyClassFile, true )
  * will make the log functions COUT_LOG_INFO, COUT_LOG_WARN,
 COUT_LOG_ERROR
  * For use like
  * COUT_LOG_INFO( MyClassFile ) << "This will get thinged?";
  * Or just
  * COUT_LOG_INFO( MyClassFile );
  * Setting the second parameter ACTIVE to "false" causes
 everything to be compiled out except for the iostream code which
 adds some bloat
  */
 #if defined( DEBUG )

 #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \
 class PREFIX##_Log \
 { \
     public: \
     template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \
     { \
         if( ENABLED and
 !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \
     { \
         auto now = std::chrono::system_clock::now(); \
         auto seconds =
 std::chrono::time_point_cast<std::chrono::seconds>( now ); \
         auto microseconds =
 std::chrono::duration_cast<std::chrono::microseconds>(
 now-seconds ); \
         auto t = std::chrono::system_clock::to_time_t( now ); \
             char b[ 64 ]; \
             if( !std::strftime( b, sizeof( b ), "%F, %T",
 std::localtime( &t ) ) ) \
             { \
                 b[ 0 ] = 0; \
             } \
         std::cout << "["#PREFIX" " << level << ' ' << b << '.' <<
 microseconds.count() << "] (" << std::hex <<
 std::this_thread::get_id() << std::dec << ") "; \
     } \
     } \
     template<class T> \
         PREFIX##_Log &operator <<( const T &v ) \
         { \
             if( ENABLED ) \
                 std::cout << v; \
             return( *this ); \
         } \
     ~PREFIX##_Log() \
     { \
         if( ENABLED ) \
         std::cout << std::endl; \
     } \
 }; \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     if( !ACTIVE ) \
         return( PREFIX##_Log<ACTIVE>() ); \
     PREFIX##_Log<ACTIVE> log( "ERROR" ); \
     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \
         log << message << ": "; \
     return( log ); \
 } \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     if( !ACTIVE ) \
         return( PREFIX##_Log<ACTIVE>() ); \
     PREFIX##_Log<ACTIVE> log( "WARN" ); \
     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \
         log << message << ": "; \
     return( log ); \
 } \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     if( !ACTIVE ) \
         return( PREFIX##_Log<ACTIVE>() ); \
     PREFIX##_Log<ACTIVE> log( "INFO" ); \
     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \
         log << message << ": "; \
     return( log ); \
 }

 #else

 // Blank ones
 #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \
 class PREFIX##_Log \
 { \
     public: \
     template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \
     { \
     } \
     template<class T> \
         PREFIX##_Log &operator <<( const T &v ) \
         { \
             return( *this ); \
         } \
     ~PREFIX##_Log() \
     { \
     } \
 }; \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     return( PREFIX##_Log<ACTIVE>() ); \
 } \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     return( PREFIX##_Log<ACTIVE>() ); \
 } \
 template<typename STRING_TYPE=DebugCouts_NoStringType>
 PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE
 message=STRING_TYPE() ) \
 { \
     return( PREFIX##_Log<ACTIVE>() ); \
 }

 #endif
 Michael A. Leonetti
 As warm as green tea

 On 9/20/19 2:44 PM, Jonathan Brockerville wrote:
 Hi,

 I'm trying to create a continuous memory player (for lack of a
 better description). Here's what I'm doing. Create a memory
 player with looping enabled. When it gets to EOF the callback
 method is invoked. I update the memory with new audio data and
 return true. This basically works, however, I keep getting a
 little snippet of the previous audio data. It's as if the
 callback is invoked slightly after the rewind happens. I assumed
 that execution would be: play memory, EOF callback, rewind
 memory, play memory, etc. That doesn't seem to be the case. What
 am I getting wrong here?

 I also tried disabling looping, but I couldn't figure out how to
 manually rewind the memory and transmit again. Destroying the
 memory player, re-creating it, and pointing at the same but
 updated buffer seems heavy handed.

 I've essentially copied and pasted the AudioMediaPlayer class and
 used the memory player methods under the hood. That is,
 https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm


 Should I be approaching this differently or...?

 Thanks,
 -brock



 _______________________________________________
 Visit our blog:http://blog.pjsip.org

 pjsip mailing list
 pjsip@lists.pjsip.org  <mailto:pjsip@lists.pjsip.org>
 http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org
 _______________________________________________
 Visit our blog: http://blog.pjsip.org

 pjsip mailing list
 pjsip@lists.pjsip.org <mailto:pjsip@lists.pjsip.org>
 http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

Visit our blog: http://blog.pjsip.org

pjsip mailing list
pjsip@lists.pjsip.org
http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org

Glad you solved it. I figured it might be not of much help. It helps me a bunch though. I'm always getting into messes like that where I even just need to print what the buffer sizes are resolving to. I get mixed up a bunch with simple things in C/C++ like order of operations and floating point/non-floating point maths. For example, to calculate the buffer frames required for a given period I use this: >                 template<typename Rep,typename Period> >                 static size_t calculateBufferFrames( const > std::chrono::duration<Rep,Period> &period, size_t samplerate ) >                 { >                     // Now how much period is in a second (because the > samplerate is a full second) >                     auto per_second = > std::chrono::duration_cast<std::chrono::duration<Rep,Period>>( > std::chrono::seconds( 1 ) ); > >                     // Now do the math >                     return( > period.count()*samplerate/per_second.count() ); >                 } I used to have ( period.count()/per_second.count() )*samplerate which works fine if you do it with a calculator, but will produce 0 if done here. That caused me a bit of grief with the buffers until I did some print lines. Maybe I'm just inexperienced :D. Michael A. Leonetti As warm as green tea On 9/22/19 10:34 PM, Jonathan Brockerville wrote: > Thanks, Michael. That is some serious console debugging code.  :) > > Turns out I may have fluked into solving it. I can't explain it just > yet, but it feels right. I was trying buffers with different sample > rates, byte rates, and number of channels. One test didn't do the > repeating thing. It was a buffer size of double the byte rate. I > changed all my tests to allocate a buffer of double the byte rate and > they all worked. We'll see if that holds true with more > rigorous testing though.  :P > > > > On Fri, 20 Sep 2019 at 15:13, Michael A. Leonetti > <michael@theleonetti.com <mailto:michael@theleonetti.com>> wrote: > > Maybe not helpful, but if it were me I'd std::cout the heck out of > the code to see in what sequence everything occurs. I would, do this: > > 1. Use some serious print lines as many places as you can, > especially on the EOF function, and the functions where you're > writing your audio source also with memory locations. > 2. Create output files with whatever the same data is that you > write to the media buffers, write to the files (name them each > differently each time EOF occurs close the file and reopen). > Afterwards open the files with audacity or something > (especially if they're raw PCM) and hear what they sound like. > > This is how I debug stuff like this. If the output files are also > weird, then that might tell you something about what's going on. > > Here's a quick and dirty (and ugly) library I wrote for just such > occasions that you can easily turn on and off. > >> // Intelligent debug COUT code to stop code duplication >> >> #ifndef DEBUGCOUTS_H >> #define DEBUGCOUTS_H >> >> #if defined( DEBUG ) >> #include <iomanip> >> #include <iostream> >> #include <string> >> #include <thread> >> #include <chrono> >> #endif >> >> // Define the couts >> #define COUT_LOG_INFO( PREFIX ) PREFIX##_INFO() << '[' << >> __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: " >> #define COUT_LOG_WARN( PREFIX ) PREFIX##_WARN() << '[' << >> __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: " >> #define COUT_LOG_ERROR( PREFIX ) PREFIX##_ERROR() << '[' << >> __LINE__ << ' ' << __PRETTY_FUNCTION__ << "]: " >> >> // Quick class for differentiating when we don't have a string >> class DebugCouts_NoStringType : public std::string {}; // Make it >> a string type >> >> #endif >> >> /** >>  * Prefix name of the logger functions you want. >>  * For example DEBUG_COUTS( MyClassFile, true ) >>  * will make the log functions COUT_LOG_INFO, COUT_LOG_WARN, >> COUT_LOG_ERROR >>  * For use like >>  * COUT_LOG_INFO( MyClassFile ) << "This will get thinged?"; >>  * Or just >>  * COUT_LOG_INFO( MyClassFile ); >>  * Setting the second parameter ACTIVE to "false" causes >> everything to be compiled out except for the iostream code which >> adds some bloat >>  */ >> #if defined( DEBUG ) >> >> #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ >> class PREFIX##_Log \ >> { \ >>     public: \ >>     template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \ >>     { \ >>         if( ENABLED and >> !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >>     { \ >>         auto now = std::chrono::system_clock::now(); \ >>         auto seconds = >> std::chrono::time_point_cast<std::chrono::seconds>( now ); \ >>         auto microseconds = >> std::chrono::duration_cast<std::chrono::microseconds>( >> now-seconds ); \ >>         auto t = std::chrono::system_clock::to_time_t( now ); \ >>             char b[ 64 ]; \ >>             if( !std::strftime( b, sizeof( b ), "%F, %T", >> std::localtime( &t ) ) ) \ >>             { \ >>                 b[ 0 ] = 0; \ >>             } \ >>         std::cout << "["#PREFIX" " << level << ' ' << b << '.' << >> microseconds.count() << "] (" << std::hex << >> std::this_thread::get_id() << std::dec << ") "; \ >>     } \ >>     } \ >>     template<class T> \ >>         PREFIX##_Log &operator <<( const T &v ) \ >>         { \ >>             if( ENABLED ) \ >>                 std::cout << v; \ >>             return( *this ); \ >>         } \ >>     ~PREFIX##_Log() \ >>     { \ >>         if( ENABLED ) \ >>         std::cout << std::endl; \ >>     } \ >> }; \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     if( !ACTIVE ) \ >>         return( PREFIX##_Log<ACTIVE>() ); \ >>     PREFIX##_Log<ACTIVE> log( "ERROR" ); \ >>     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >>         log << message << ": "; \ >>     return( log ); \ >> } \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     if( !ACTIVE ) \ >>         return( PREFIX##_Log<ACTIVE>() ); \ >>     PREFIX##_Log<ACTIVE> log( "WARN" ); \ >>     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >>         log << message << ": "; \ >>     return( log ); \ >> } \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     if( !ACTIVE ) \ >>         return( PREFIX##_Log<ACTIVE>() ); \ >>     PREFIX##_Log<ACTIVE> log( "INFO" ); \ >>     if( !std::is_same<STRING_TYPE,DebugCouts_NoStringType>::value ) \ >>         log << message << ": "; \ >>     return( log ); \ >> } >> >> #else >> >> // Blank ones >> #define DEBUG_COUTS( PREFIX, ACTIVE ) template<bool ENABLED> \ >> class PREFIX##_Log \ >> { \ >>     public: \ >>     template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log( STRING_TYPE level=STRING_TYPE() ) \ >>     { \ >>     } \ >>     template<class T> \ >>         PREFIX##_Log &operator <<( const T &v ) \ >>         { \ >>             return( *this ); \ >>         } \ >>     ~PREFIX##_Log() \ >>     { \ >>     } \ >> }; \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_ERROR( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     return( PREFIX##_Log<ACTIVE>() ); \ >> } \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_WARN( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     return( PREFIX##_Log<ACTIVE>() ); \ >> } \ >> template<typename STRING_TYPE=DebugCouts_NoStringType> >> PREFIX##_Log<ACTIVE> PREFIX##_INFO( STRING_TYPE >> message=STRING_TYPE() ) \ >> { \ >>     return( PREFIX##_Log<ACTIVE>() ); \ >> } >> >> #endif > > > Michael A. Leonetti > As warm as green tea > > On 9/20/19 2:44 PM, Jonathan Brockerville wrote: >> Hi, >> >> I'm trying to create a continuous memory player (for lack of a >> better description). Here's what I'm doing. Create a memory >> player with looping enabled. When it gets to EOF the callback >> method is invoked. I update the memory with new audio data and >> return true. This basically works, however, I keep getting a >> little snippet of the previous audio data. It's as if the >> callback is invoked slightly after the rewind happens. I assumed >> that execution would be: play memory, EOF callback, rewind >> memory, play memory, etc. That doesn't seem to be the case. What >> am I getting wrong here? >> >> I also tried disabling looping, but I couldn't figure out how to >> manually rewind the memory and transmit again. Destroying the >> memory player, re-creating it, and pointing at the same but >> updated buffer seems heavy handed. >> >> I've essentially copied and pasted the AudioMediaPlayer class and >> used the memory player methods under the hood. That is, >> https://www.pjsip.org/pjmedia/docs/html/group__PJMEDIA__MEM__PLAYER.htm >> >> >> Should I be approaching this differently or...? >> >> Thanks, >> -brock >> >> >> >> _______________________________________________ >> Visit our blog:http://blog.pjsip.org >> >> pjsip mailing list >> pjsip@lists.pjsip.org <mailto:pjsip@lists.pjsip.org> >> http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org > _______________________________________________ > Visit our blog: http://blog.pjsip.org > > pjsip mailing list > pjsip@lists.pjsip.org <mailto:pjsip@lists.pjsip.org> > http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org > > > _______________________________________________ > Visit our blog: http://blog.pjsip.org > > pjsip mailing list > pjsip@lists.pjsip.org > http://lists.pjsip.org/mailman/listinfo/pjsip_lists.pjsip.org