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
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:
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
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
/**
#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
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