namespace FreeDV {
/// Codec "no-op", just copies its input to its output. For plain SSB voice, and for testing.
class CodecNoOp : public Codec {
+ static const std::size_t FrameDuration = 40;
+
public:
/// Instantiate the no-op codec.
CodecNoOp::decode16(const std::uint8_t * i, std::int16_t * o, std::size_t * data_length, std::size_t sample_length)
{
const std::size_t length = min(*data_length / 2, sample_length);
- if ( length < (std::size_t)(((double)SampleRate / 1000.0) * 40) ) {
+ if ( length < (std::size_t)(((double)SampleRate / 1000.0) * FrameDuration) )
+ {
*data_length = 0;
return 0;
}
int
CodecNoOp::min_frame_duration() const
{
- return 40;
+ return FrameDuration;
}
Codec *
if ( audio ) {
gid_t groups[NGROUPS_MAX];
- int length = sizeof(groups) / sizeof(*groups);
+ int size = sizeof(groups) / sizeof(*groups);
+ int length;
if ( gid == audio->gr_gid || egid == audio->gr_gid )
return;
- if ( getgroups(length, groups) == 0 ) {
+ if ( (length = getgroups(size, groups)) > 0 ) {
for ( int i = 0; i < length; i++ ) {
if ( groups[i] == audio->gr_gid )
return;
#include <unistd.h>
#ifdef _POSIX_PRIORITY_SCHEDULING
-#include <errno.h>
#include <sched.h>
+#endif
+#ifdef _POSIX_MEMLOCK_RANGE
+#include <sys/mman.h>
+#endif
+#include <errno.h>
#include <cstring>
#include <iostream>
-#endif
#include "drivers.h"
static const char insufficient_privilege_message[] =
-"Warning: Insufficient privilege to set a real-time scheduling priority.\n"
+"Warning: Insufficient privilege to set a real-time scheduling priority,\n"
+"or to lock memory.\n"
"This could cause audio to be interrupted while other programs use the CPU.\n"
"To fix: As root, run\n"
"\n"
"\tsetcap cap_sys_nice+ep filename\n"
+"\tsetcap cap_ipc_lock+ep filename\n"
"\n"
-"where filename is the executable file for this program. That will\n"
-"allow you to use a real-time scheduling priority while running as any user.\n"
-"Alternatively, you can execute this program as root.\n\n";
+"where filename is the executable file for this program.\n"
+"That will allow you to use a real-time scheduling priority and locked memory\n"
+"while running as any user.\n"
+"Alternatively, you can execute this program as root.\n";
static const char old_kernel_message[] =
-"This kernel doesn't seem to have real-time facilities.\n"
-"If audio is sometimes interrupted, try a newer kernel.\n\n";
+"This kernel doesn't seem to have real-time facilities or memory locking.\n"
+"If audio is sometimes interrupted, try a newer kernel.\n";
namespace FreeDV {
void
set_scheduler()
{
+ bool insufficient_privilege = false;
+ bool old_kernel = false;
+
#ifdef _POSIX_PRIORITY_SCHEDULING
+ // Put this process on the round-robin realtime scheduling queue at the
+ // minimum priority. Other real-time processes (perhaps portaudio) can
+ // run with a higher priority than this, but this process will run at
+ // a higher priority than all normal processes.
sched_param p;
int policy = SCHED_RR;
p.sched_priority = sched_get_priority_min(policy);
const int result = sched_setscheduler(0, policy, &p);
if ( result < 0 ) {
+ std::cerr << "sched_setscheduler: " << strerror(errno) << std::endl;
+ if ( errno == EINVAL )
+ old_kernel = true;
if ( errno == EPERM )
- std::cerr << insufficient_privilege_message << std::flush;
- else if ( errno == EINVAL )
- std::cerr << old_kernel_message << std::flush;
- else {
- std::cerr << "sched_setscheduler: " << strerror(errno) << std::endl;
- }
+ insufficient_privilege = true;
+ }
+#endif
+
+#ifdef _POSIX_MEMLOCK_RANGE
+ if ( mlockall(MCL_CURRENT|MCL_FUTURE) < 0 ) {
+ std::cerr << "mlockall: " << strerror(errno) << std::endl;
+ if ( errno == EINVAL )
+ old_kernel = true;
+ if ( errno == EPERM )
+ insufficient_privilege = true;
}
- }
#endif
+ if ( old_kernel )
+ std::cerr << old_kernel_message;
+ else if ( insufficient_privilege )
+ std::cerr << insufficient_privilege_message;
+ }
}
bool ptt_digital;
bool ptt_ssb;
std::size_t min_frame_duration;
- struct timespec last_frame_time;
struct timespec next_frame_time;
void key_down();
void
Run::reset()
{
- clock_gettime(CLOCK_MONOTONIC, &last_frame_time);
in_fifo.reset();
codec_fifo.reset();
out_fifo.reset();
if ( bytes_to_decode > 0 )
codec_fifo.get_done(bytes_to_decode);
- // TODO: We can do clock tuning here to maximize power saving, if it
- // results in any noticable gain.
if ( result > 0 ) {
// std::cerr << '.';
out_fifo.put_done(result * 2);
- last_frame_time = start_time;
+ // Calculate a time one millisecond short of when the next frame
+ // should start. We can sleep until then.
const long duration = ((min_frame_duration - 1) * 1000000);
- next_frame_time.tv_sec = last_frame_time.tv_sec;
- next_frame_time.tv_nsec = last_frame_time.tv_nsec + duration;
+ next_frame_time.tv_sec = start_time.tv_sec;
+ next_frame_time.tv_nsec = start_time.tv_nsec + duration;
next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
next_frame_time.tv_nsec %= 1000000000;
}
else {
+
// std::cerr << '+';
- // Add a microsecond. We really could poll the I/O interfaces instead.
+
+ // Go to sleep for a millisecond and try again. We could poll the
+ // I/O interfaces for ready instead.
+ next_frame_time = start_time;
next_frame_time.tv_nsec += 1000000;
next_frame_time.tv_sec += next_frame_time.tv_nsec / 1000000000;
next_frame_time.tv_nsec %= 1000000000;
min_frame_duration = max(min_frame_duration, i->codec->min_frame_duration());
min_frame_duration = max(min_frame_duration, i->framer->min_frame_duration());
- std::cerr << "minimum frame duration is " << min_frame_duration << std::endl;
+ std::cerr << "The minimum frame duration is "
+ << min_frame_duration << " milliseconds." << std::endl;
if ( min_frame_duration > MaximumFrameDuration ) {
std::ostringstream str;
str << " is larger than MaximumFrameDuration of ";
str << MaximumFrameDuration << '.' << std::endl;
str << "A Modem, Framer, or Codec returned min_frame_duration() that";
- str << " was too large, or MaximumFrameDuration must be increased.";
+ str << " was too large," << std::endl;
+ str << "or MaximumFrameDuration must be increased.";
throw std::runtime_error(str.str().c_str());
}
assert(min_frame_duration < 1000000);