App-MHFS

 view release on metacpan or  search on metacpan

lib/App/MHFS.pm  view on Meta::CPAN

        _ENOTTY => 25,  #constant for Linux?
    };
    # x86_64 numbers
    require 'syscall.ph';

    my $TFD_CLOEXEC = 0x80000;
    my $TFD_NONBLOCK = 0x800;

    sub new {
        my ($class, $evp) = @_;
        my $timerfd = syscall(SYS_timerfd_create(), _clock_MONOTONIC, $TFD_NONBLOCK | $TFD_CLOEXEC);
        $timerfd != -1 or die("failed to create timerfd: $!");
        my $timerhandle = IO::Handle->new_from_fd($timerfd, "r");
        $timerhandle or die("failed to turn timerfd into a file handle");
        my %self = ('timerfd' => $timerfd, 'timerhandle' => $timerhandle);
        bless \%self, $class;

        $evp->set($self{'timerhandle'}, \%self, POLLIN);
        $self{'evp'} = $evp;
        return \%self;
    }

    sub packitimerspec {
       my ($times) = @_;
       my $it_interval_sec  = int($times->{'it_interval'});
       my $it_interval_nsec = floor(($times->{'it_interval'} - $it_interval_sec) * 1000000000);
       my $it_value_sec = int($times->{'it_value'});
       my $it_value_nsec = floor(($times->{'it_value'} - $it_value_sec) * 1000000000);
       #say "packing $it_interval_sec, $it_interval_nsec, $it_value_sec, $it_value_nsec";
       return pack 'qqqq', $it_interval_sec, $it_interval_nsec, $it_value_sec, $it_value_nsec;
   }

    sub settime_linux {
        my ($self, $start, $interval) = @_;
        # assume start 0 is supposed to run immediately not try to cancel a timer
        $start = ($start > 0.000000001) ? $start : 0.000000001;
        my $new_value = packitimerspec({'it_interval' => $interval, 'it_value' => $start});
        my $settime_success = syscall(SYS_timerfd_settime(), $self->{'timerfd'}, 0, $new_value,0);
        ($settime_success == 0) or die("timerfd_settime failed: $!");
    }

    sub onReadReady {
        my ($self) = @_;
        my $nread;
        my $buf;
        while($nread = sysread($self->{'timerhandle'}, $buf, 8)) {
            if($nread < 8) {
                say "timer hit, ignoring $nread bytes";
                next;
            }
            my $expirations = unpack 'Q', $buf;
            say "Linux::Timer there were $expirations expirations";
        }
        if(! defined $nread) {
            if( ! $!{EAGAIN}) {
                say "sysread failed with $!";
            }

        }
        $self->{'evp'}->check_timers;
        return 1;
    };
1;
}  # package
}; # eval
}; # BEGIN

# You must provide event handlers for the events you are listening for
# return undef to have them removed from poll's structures
package MHFS::EventLoop::Poll::Base {

lib/App/MHFS.pm  view on Meta::CPAN


    sub _decode_status {
        my ($rc) = @_;
        print "$rc: normal exit with code ". WEXITSTATUS($rc)."\n" if WIFEXITED(  $rc);
        print "$rc: terminated with signal ".WTERMSIG(   $rc)."\n" if WIFSIGNALED($rc);
        print "$rc: stopped with signal ".   WSTOPSIG(   $rc)."\n" if WIFSTOPPED( $rc);
    }

    sub new {
        my ($class) = @_;
        my %self = ('poll' => IO::Poll->new(), 'fh_map' => {}, 'timers' => [], 'children' => {}, 'deadchildren' => []);
        bless \%self, $class;

        $SIG{CHLD} = sub {
            while((my $child = waitpid(-1, WNOHANG)) > 0) {
                my ($wstatus, $exitcode) = ($?, $?>> 8);
                if(defined $self{'children'}{$child}) {
                    say "PID $child reaped (func) $exitcode";
                    push @{$self{'deadchildren'}}, [$self{'children'}{$child}, $child, $wstatus];
                    $self{'children'}{$child} = undef;
                }

lib/App/MHFS.pm  view on Meta::CPAN

        return $self->{'poll'}->mask($handle);
    }

    sub remove {
        my ($self, $handle) = @_;
        $self->{'poll'}->remove($handle);
        $self->{'fh_map'}{$handle} = undef;
    }


   sub _insert_timer {
       my ($self, $timer) = @_;
       my $i;
       for($i = 0; defined($self->{'timers'}[$i]) && ($timer->{'desired'} >= $self->{'timers'}[$i]{'desired'}); $i++) { }
       splice @{$self->{'timers'}}, $i, 0, ($timer);
       return $i;
   }


    # all times are relative, is 0 is set as the interval, it will be run every main loop iteration
    # return undef in the callback to delete the timer
    sub add_timer {
        my ($self, $start, $interval, $callback, $id) = @_;
        my $current_time = clock_gettime(CLOCK_MONOTONIC);
        my $desired = $current_time + $start;
        my $timer = { 'desired' => $desired, 'interval' => $interval, 'callback' => $callback };
        $timer->{'id'} = $id if(defined $id);
        return _insert_timer($self, $timer);
    }

    sub remove_timer_by_id {
        my ($self, $id) = @_;
        my $lastindex = scalar(@{$self->{'timers'}}) - 1;
        for my $i (0 .. $lastindex) {
            next if(! defined $self->{'timers'}[$i]{'id'});
            if($self->{'timers'}[$i]{'id'} == $id) {
                #say "Removing timer with id: $id";
                splice(@{$self->{'timers'}}, $i, 1);
                return;
            }
        }
        say "unable to remove timer $id, not found";
    }

    sub requeue_timers {
        my ($self, $timers, $current_time) = @_;
        foreach my $timer (@$timers) {
            $timer->{'desired'} = $current_time + $timer->{'interval'};
            _insert_timer($self, $timer);
        }
    }

    sub check_timers {
        my ($self) = @_;
        my @requeue_timers;
        my $timerhit = 0;
        my $current_time =  clock_gettime(CLOCK_MONOTONIC);
        while(my $timer = shift (@{$self->{'timers'}})  ) {
            if($current_time >= $timer->{'desired'}) {
                $timerhit = 1;
                if(defined $timer->{'callback'}->($timer, $current_time, $self)) { # callback may change interval
                    push @requeue_timers, $timer;
                }
            }
            else {
                unshift @{$self->{'timers'}}, $timer;
                last;
            }
        }
        $self->requeue_timers(\@requeue_timers, $current_time);
    }

    sub do_poll {
        my ($self, $loop_interval, $poll) = @_;
        my $pollret = $poll->poll($loop_interval);
        if($pollret > 0){
            foreach my $handle ($poll->handles()) {
                my $revents = $poll->events($handle);
                my $obj = $self->{'fh_map'}{$handle};
                if($revents & POLLIN) {

lib/App/MHFS.pm  view on Meta::CPAN


        $self->run_dead_children_callbacks;
    }

    sub run {
        my ($self, $loop_interval) = @_;
        my $default_lp_interval = $loop_interval // -1;
        my $poll = $self->{'poll'};
        for(;;)
        {
            check_timers($self);
            print "do_poll $$";
            if($self->{'timers'}) {
                say " timers " . scalar(@{$self->{'timers'}}) . ' handles ' . scalar($self->{'poll'}->handles());
            }
            else {
                print "\n";
            }
            # we don't need to expire until a timer is expiring
            if(@{$self->{'timers'}}) {
                $loop_interval = $self->{'timers'}[0]{'desired'} - clock_gettime(CLOCK_MONOTONIC);
            }
            else {
                $loop_interval = $default_lp_interval;
            }
            do_poll($self, $loop_interval, $poll);
        }
    }

    1;
}

package MHFS::EventLoop::Poll::Linux {
    use strict; use warnings;
    use feature 'say';
    use parent -norequire, 'MHFS::EventLoop::Poll::Base';
    sub new {
        my $class = shift;
        my $self = $class->SUPER::new(@_);
        $self->{'evp_timer'} = MHFS::EventLoop::Poll::Linux::Timer->new($self);
        return $self;
    };

    sub add_timer {
        my ($self, $start) = @_;
        shift @_;
        if($self->SUPER::add_timer(@_) == 0) {
            say __PACKAGE__.": add_timer, updating linux timer to $start";
            $self->{'evp_timer'}->settime_linux($start, 0);
        }
    };

    sub requeue_timers {
        my $self = shift @_;
        $self->SUPER::requeue_timers(@_);
        my ($timers, $current_time) = @_;
        if(@{$self->{'timers'}}) {
            my $start = $self->{'timers'}[0]{'desired'} - $current_time;
            say __PACKAGE__.": requeue_timers, updating linux timer to $start";
            $self->{'evp_timer'}->settime_linux($start, 0);
        }
    };

    sub run {
        my ($self, $loop_interval) = @_;
        $loop_interval //= -1;
        my $poll = $self->{'poll'};
        for(;;)
        {
            print __PACKAGE__.": do_poll LINUX_X86_64 $$";
            if($self->{'timers'}) {
                say " timers " . scalar(@{$self->{'timers'}}) . ' handles ' . scalar($self->{'poll'}->handles());
            }
            else {
                print "\n";
            }

            $self->SUPER::do_poll($loop_interval, $poll);
        }
    };
    1;
}

lib/App/MHFS.pm  view on Meta::CPAN

        }
        $self{'fs'} = $fs;

        # load the plugins
        foreach my $pluginname (@{$plugins}) {

            next if(defined $settings->{$pluginname}{'enabled'} && (!$settings->{$pluginname}{'enabled'}));
            my $plugin = $pluginname->new($settings, \%self);
            next if(! $plugin);

            foreach my $timer (@{$plugin->{'timers'}}) {
                say __PACKAGE__.': adding '.ref($plugin).' timer';
                $self{'evp'}->add_timer(@{$timer});
            }
            if(my $func = $plugin->{'uploader'}) {
                say __PACKAGE__.': adding '. ref($plugin) .' uploader';
                push (@{$self{'uploaders'}}, $func);
            }
            foreach my $route (@{$plugin->{'routes'}}) {
                say __PACKAGE__.': adding ' . ref($plugin) . ' route ' . $route->[0];
                push @{$self{'routes'}}, $route;
            }
            $plugin->{'server'} = \%self;

lib/App/MHFS.pm  view on Meta::CPAN

        $cb->($self->{fulfill}, $self->{reject});
        return $self;
    }

    sub throw {
        return MHFS::Promise::FakeException->new(@_);
    }

    sub handleResolved {
        my ($self, $deferred) = @_;
        $self->{evp}->add_timer(0, 0, sub {
            my $success = $self->{state} == MHFS_PROMISE_SUCCESS;
            my $value = $self->{end_value};
            if($success && $deferred->{onFulfilled}) {
                $value = $deferred->{onFulfilled}($value);
                if(ref($value) eq 'MHFS::Promise::FakeException') {
                    $success = 0;
                    $value = $$value;
                }
            } elsif(!$success && $deferred->{onRejected}) {
                $value = $deferred->{onRejected}->($value);

lib/App/MHFS.pm  view on Meta::CPAN

    sub new {
        my ($class, $client) = @_;
        my %self = ( 'client' => $client);
        bless \%self, $class;
        weaken($self{'client'}); #don't allow Request to keep client alive
        $self{'on_read_ready'} = \&want_request_line;
        $self{'outheaders'}{'X-MHFS-CONN-ID'} = $client->{'outheaders'}{'X-MHFS-CONN-ID'};
        $self{'rl'} = 0;
        # we want the request
        $client->SetEvents(POLLIN | MHFS::EventLoop::Poll->ALWAYSMASK );
        $self{'recvrequesttimerid'} = $client->AddClientCloseTimer($client->{'server'}{'settings'}{'recvrequestimeout'}, $client->{'CONN-ID'}, 1);
        return \%self;
    }

    # on ready ready handlers
    sub want_request_line {
        my ($self) = @_;

        my $ipos = index($self->{'client'}{'inbuf'}, "\r\n");
        if($ipos != -1) {
            if(substr($self->{'client'}{'inbuf'}, 0, $ipos+2, '') =~ /^(([^\s]+)\s+([^\s]+)\s+(?:HTTP\/1\.([0-1])))\r\n/) {

lib/App/MHFS.pm  view on Meta::CPAN

        }

        # remove the final \r\n
        substr($self->{'client'}{'inbuf'}, 0, 2, '');
        if((defined $self->{'header'}{'Range'}) &&  ($self->{'header'}{'Range'} =~ /^bytes=([0-9]+)\-([0-9]*)$/)) {
            $self->{'header'}{'_RangeStart'} = $1;
            $self->{'header'}{'_RangeEnd'} = ($2 ne  '') ? $2 : undef;
        }
        $self->{'on_read_ready'} = undef;
        $self->{'client'}->SetEvents(MHFS::EventLoop::Poll->ALWAYSMASK );
        $self->{'client'}->KillClientCloseTimer($self->{'recvrequesttimerid'});
        $self->{'recvrequesttimerid'} = undef;

        # finally handle the request
        foreach my $route (@{$self->{'client'}{'server'}{'routes'}}) {
            if($self->{'path'}{'unsafecollapse'} eq $route->[0]) {
                $route->[1]($self);
                return 1;
            }
            else {
                # wildcard ending
                next if(index($route->[0], '*', length($route->[0])-1) == -1);

lib/App/MHFS.pm  view on Meta::CPAN

        my ($class, $sock, $server, $serverhostinfo, $ip) = @_;
        $sock->blocking(0);
        my %self = ('sock' => $sock, 'server' => $server, 'time' => clock_gettime(CLOCK_MONOTONIC), 'inbuf' => '', 'serverhostname' => $serverhostinfo->{'hostname'}, 'absurl' => $serverhostinfo->{'absurl'}, 'ip' => $ip, 'X-MHFS-PROXY-KEY' => $serverh...
        $self{'CONN-ID'} = int($self{'time'} * rand()); # insecure uid
        $self{'outheaders'}{'X-MHFS-CONN-ID'} = sprintf("%X", $self{'CONN-ID'});
        bless \%self, $class;
        $self{'request'} = MHFS::HTTP::Server::Client::Request->new(\%self);
        return \%self;
    }

    # add a connection timeout timer
    sub AddClientCloseTimer {
        my ($self, $timelength, $id, $is_requesttimeout) = @_;
        weaken($self); #don't allow this timer to keep the client object alive
        my $server = $self->{'server'};
        say "CCT | add timer: $id";
        $server->{'evp'}->add_timer($timelength, 0, sub {
            if(! defined $self) {
                say "CCT | $id self undef";
                return undef;
            }
            # Commented out as with connection reuse on, Apache 2.4.10 seems sometimes
            # pass 408 on to the next client.
            #if($is_requesttimeout) {
            #    say "CCT | \$timelength ($timelength) exceeded, sending 408";
            #    $self->{request}->Send408;
            #    CT_WRITE($self);

lib/App/MHFS.pm  view on Meta::CPAN

            $server->{'evp'}->remove($self->{'sock'});
            say "poll has " . scalar ( $server->{'evp'}{'poll'}->handles) . " handles";
            return undef;
        }, $id);
        return $id;
    }

    sub KillClientCloseTimer {
        my ($self, $id) = @_;
        my $server = $self->{'server'};
        say "CCT | removing timer: $id";
        $server->{'evp'}->remove_timer_by_id($id);
    }

    sub SetEvents {
        my ($self, $events) = @_;
        $self->{'server'}{'evp'}->set($self->{'sock'}, $self, $events);
    }

    use constant {
        RECV_SIZE => 65536,
        CT_YIELD => 1,

lib/App/MHFS.pm  view on Meta::CPAN

                goto &onWriteReady;
                #return onWriteReady($self);
            }
            #else {
            elsif(defined $self->{'request'}{'on_read_ready'}) {
                #say "do_on_data: goto onReadReady inbuf " . length($self->{'inbuf'});
                goto &onReadReady;
                #return onReadReady($self);
            }
            else {
                say "do_on_data: response and on_read_ready not defined, response by timer or poll?";
            }
        }
        return $res;
    }


    sub onReadReady {
        goto &CT_READ;
        my ($self) = @_;
        my $tempdata;

lib/App/MHFS.pm  view on Meta::CPAN

                    say "Connection close header set closing conn";
                    say "-------------------------------------------------";
                    return undef;
                }
                $client->{'request'} = MHFS::HTTP::Server::Client::Request->new($client);
                # handle possible existing read data
                goto &do_on_data;
            }
        }
        else {
            say "response not defined, probably set later by a timer or poll";
        }
        return 1;
    }

    sub _TSRReturnPrint {
        my ($sentthiscall) = @_;
        if($sentthiscall > 0) {
            say "wrote $sentthiscall bytes";
        }
    }

lib/App/MHFS.pm  view on Meta::CPAN

            # Try to send the buf if set
            if(length($dataitem->{'buf'})) {
                my $sret = TrySendItem($csock, \$dataitem->{'buf'});
                # critical conn error
                if(! defined($sret)) {
                    _TSRReturnPrint($sentthiscall);
                    return undef;
                }
                if($sret) {
                    $sentthiscall += $sret;
                    # if we sent data, kill the send timer
                    if(defined $client->{'sendresponsetimerid'}) {
                        $client->KillClientCloseTimer($client->{'sendresponsetimerid'});
                        $client->{'sendresponsetimerid'} = undef;
                    }
                }
                # not all data sent, add timer
                if(length($dataitem->{'buf'}) > 0) {
                    $client->{'sendresponsetimerid'} //= $client->AddClientCloseTimer($client->{'server'}{'settings'}{'sendresponsetimeout'}, $client->{'CONN-ID'});
                    _TSRReturnPrint($sentthiscall);
                    return '';
                }

                #we sent the full buf
            }

            # read more data
            my $newdata;
            if(defined $dataitem->{'fh'}) {

lib/App/MHFS.pm  view on Meta::CPAN

        $self->{'routes'} = [
            ['/music', $musicpageroute],
            ['/music_dl', $musicdlroute],
            ['/music_resources', $musicresourcesroute],
            ['/music_art', sub {
                my ($request) = @_;
                return $self->SendArt($request);
            }]
        ];

        $self->{'timers'} = [
            # update the library at start and periodically
            [0, 300, sub {
                my ($timer, $current_time, $evp) = @_;
                say "$pstart library timer";
                UpdateLibrariesAsync($self, $evp, sub {
                    say "$pstart library timer done";
                });
                return 1;
            }],
        ];

        return $self;
    }

    1;
}

lib/App/MHFS.pm  view on Meta::CPAN

        ['/torrent/tracker', sub {
            my ($request) = @_;
            $self->announce($request);
        }],
        ['/torrent/create', sub {
            my ($request) = @_;
            $self->createTorrent($request);
        }],
        ];

        $self->{'timers'} = [
            # once an hour evict peers that left the swarm ungracefully
            [0, 3600, sub {
                my ($timer, $current_time, $evp) = @_;
                say __PACKAGE__.": evict peers timer";
                foreach my $infohash (keys %{$self->{'torrents'}}) {
                    foreach my $peer (keys %{$self->{'torrents'}{$infohash}}) {
                        my $peerdata = $self->{'torrents'}{$infohash}{$peer};
                        if(($current_time - $peerdata->{'last_announce'}) > ($self->{'announce_interval'}+60)) {
                            $self->removeTorrentPeer($infohash, $peer, " timeout");
                        }
                    }
                }
                return 1;
            }],

lib/App/MHFS.pm  view on Meta::CPAN

            #$request->Send404;
            #return undef;

            if($fmt eq 'hls') {
                $video{'on_exists'} = \&video_hls_write_master_playlist;
            }

            # deprecated
            $video{'pid'} = ASYNC(\&shellcmd_unlock, \@cmd, $video{'out_filepath'});

            # our file isn't ready yet, so create a timer to check the progress and act
            weaken($request); # the only one who should be keeping $request alive is the client
            $request->{'client'}{'server'}{'evp'}->add_timer(0, 0, sub {
                if(! defined $request) {
                    say "\$request undef, ignoring CB";
                    return undef;
                }
                # test if its ready to send
                while(1) {
                     my $filename = $video{'out_filepath'};
                     if(! -e $filename) {
                         last;
                     }
                     my $minsize = $videoformats->{$fmt}{'minsize'};
                     if(defined($minsize) && ((-s $filename) < $minsize)) {
                         last;
                     }
                     if(defined $video{'on_exists'}) {
                         last if (! $video{'on_exists'}->($settings, \%video));
                     }
                     say "get_video_timer is destructing";
                     $request->SendLocalFile($filename);
                     return undef;
                }
                # 404, if we didn't send yet the process is not running
                if(pid_running($video{'pid'})) {
                    return 1;
                }
                say "pid not running: " . $video{'pid'} . " get_video_timer done with 404";
                $request->Send404;
                return undef;
            });
            say "get_video: added timer " . $video{'out_filepath'};
            });
        }
        else {
            say "out_fmt: " . $video{'out_fmt'};
            $request->Send404;
            return undef;
        }
        return 1;
    }

share/public_html/static/hls.js  view on Meta::CPAN

    _this._boundTick = _this.tick.bind(_this);
    return _this;
  }

  /**
   * @override
   */


  TaskLoop.prototype.onHandlerDestroying = function onHandlerDestroying() {
    // clear all timers before unregistering from event bus
    this.clearNextTick();
    this.clearInterval();
  };

  /**
   * @returns {boolean}
   */


  TaskLoop.prototype.hasInterval = function hasInterval() {

share/public_html/static/hls.js  view on Meta::CPAN

   */


  TaskLoop.prototype.tick = function tick() {
    this._tickCallCount++;
    if (this._tickCallCount === 1) {
      this.doTick();
      // re-entrant call to tick from previous doTick call stack
      // -> schedule a call on the next main loop iteration to process this task processing request
      if (this._tickCallCount > 1) {
        // make sure only one timer exists at any time at max
        this.clearNextTick();
        this._tickTimer = setTimeout(this._boundTick, 0);
      }
      this._tickCallCount = 0;
    }
  };

  /**
   * For subclass to implement task logic
   * @abstract

share/public_html/static/hls.js  view on Meta::CPAN

    var partial = this.fragmentTracker.getPartialFragment(currentTime);
    if (partial) {
      // Try to skip over the buffer hole caused by a partial fragment
      // This method isn't limited by the size of the gap between buffered ranges
      this._trySkipBufferHole(partial);
    }

    if (bufferInfo.len > jumpThreshold && stalledDuration > config.highBufferWatchdogPeriod * 1000) {
      // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds
      // We only try to jump the hole if it's under the configured size
      // Reset stalled so to rearm watchdog timer
      this.stalled = null;
      this._tryNudgeBuffer();
    }
  };

  /**
   * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period.
   * @param bufferLen - The playhead distance from the end of the current buffer segment.
   * @private
   */

share/public_html/static/hls.js  view on Meta::CPAN

  level_controller__inherits(LevelController, _EventHandler);

  function LevelController(hls) {
    level_controller__classCallCheck(this, LevelController);

    var _this = level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MANIFEST_LOADED, events["a" /* default */].LEVEL_LOADED, events["a" /* default */].AUDIO_TRACK_SWITCHED, events["a" /* default *...

    _this.canload = false;
    _this.currentLevelIndex = null;
    _this.manualLevelIndex = -1;
    _this.timer = null;
    return _this;
  }

  LevelController.prototype.onHandlerDestroying = function onHandlerDestroying() {
    this.clearTimer();
    this.manualLevelIndex = -1;
  };

  LevelController.prototype.clearTimer = function clearTimer() {
    if (this.timer !== null) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  };

  LevelController.prototype.startLoad = function startLoad() {
    var levels = this._levels;

    this.canload = true;
    this.levelRetryCount = 0;

    // clean up live level details to force reload them, and reset load errors
    if (levels) {
      levels.forEach(function (level) {
        level.loadError = 0;
        var levelDetails = level.details;
        if (levelDetails && levelDetails.live) {
          level.details = undefined;
        }
      });
    }
    // speed up live playlist refresh if timer exists
    if (this.timer !== null) {
      this.loadLevel();
    }
  };

  LevelController.prototype.stopLoad = function stopLoad() {
    this.canload = false;
  };

  LevelController.prototype.onManifestLoaded = function onManifestLoaded(data) {
    var levels = [];

share/public_html/static/hls.js  view on Meta::CPAN

        reason: 'no level with compatible codecs found in manifest'
      });
    }
  };

  LevelController.prototype.setLevelInternal = function setLevelInternal(newLevel) {
    var levels = this._levels;
    var hls = this.hls;
    // check if level idx is valid
    if (newLevel >= 0 && newLevel < levels.length) {
      // stopping live reloading timer if any
      this.clearTimer();
      if (this.currentLevelIndex !== newLevel) {
        logger["b" /* logger */].log('switching to level ' + newLevel);
        this.currentLevelIndex = newLevel;
        var levelProperties = levels[newLevel];
        levelProperties.level = newLevel;
        hls.trigger(events["a" /* default */].LEVEL_SWITCHING, levelProperties);
      }
      var level = levels[newLevel];
      var levelDetails = level.details;

share/public_html/static/hls.js  view on Meta::CPAN

        nextLevel = void 0;

    level.loadError++;
    level.fragmentError = fragmentError;

    if (levelError) {
      if (this.levelRetryCount + 1 <= config.levelLoadingMaxRetry) {
        // exponential backoff capped to max retry timeout
        delay = Math.min(Math.pow(2, this.levelRetryCount) * config.levelLoadingRetryDelay, config.levelLoadingMaxRetryTimeout);
        // Schedule level reload
        this.timer = setTimeout(function () {
          return _this2.loadLevel();
        }, delay);
        // boolean used to inform stream controller not to switch back to IDLE on non fatal error
        errorEvent.levelRetry = true;
        this.levelRetryCount++;
        logger["b" /* logger */].warn('level controller, ' + errorDetails + ', retry in ' + delay + ' ms, current retry count is ' + this.levelRetryCount);
      } else {
        logger["b" /* logger */].error('level controller, cannot recover from ' + errorDetails + ' error');
        this.currentLevelIndex = null;
        // stopping live reloading timer if any
        this.clearTimer();
        // switch error to fatal
        errorEvent.fatal = true;
        return;
      }
    }

    // Try any redundant streams if available for both errors: level and fragment
    // If level.loadError reaches redundantLevels it means that we tried them all, no hope  => let's switch down
    if (levelError || fragmentError) {

share/public_html/static/hls.js  view on Meta::CPAN

      return;
    }

    var curLevel = this._levels[levelId];
    // reset level load error counter on successful level loaded only if there is no issues with fragments
    if (!curLevel.fragmentError) {
      curLevel.loadError = 0;
      this.levelRetryCount = 0;
    }
    var newDetails = data.details;
    // if current playlist is a live playlist, arm a timer to reload it
    if (newDetails.live) {
      var targetdurationMs = 1000 * (newDetails.averagetargetduration ? newDetails.averagetargetduration : newDetails.targetduration);
      var reloadInterval = targetdurationMs,
          curDetails = curLevel.details;
      if (curDetails && newDetails.endSN === curDetails.endSN) {
        // follow HLS Spec, If the client reloads a Playlist file and finds that it has not
        // changed then it MUST wait for a period of one-half the target
        // duration before retrying.
        reloadInterval /= 2;
        logger["b" /* logger */].log('same live playlist, reload twice faster');
      }
      // decrement reloadInterval with level loading delay
      reloadInterval -= level_controller_performance.now() - data.stats.trequest;
      // in any case, don't reload more than half of target duration
      reloadInterval = Math.max(targetdurationMs / 2, Math.round(reloadInterval));
      logger["b" /* logger */].log('live playlist, reload in ' + Math.round(reloadInterval) + ' ms');
      this.timer = setTimeout(function () {
        return _this3.loadLevel();
      }, reloadInterval);
    } else {
      this.clearTimer();
    }
  };

  LevelController.prototype.onAudioTrackSwitched = function onAudioTrackSwitched(data) {
    var audioGroupId = this.hls.audioTracks[data.id].groupId;

share/public_html/static/hls.js  view on Meta::CPAN

  abr_controller__inherits(AbrController, _EventHandler);

  function AbrController(hls) {
    abr_controller__classCallCheck(this, AbrController);

    var _this = abr_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FRAG_LOADING, events["a" /* default */].FRAG_LOADED, events["a" /* default */].FRAG_BUFFERED, events["a" /* default */].ERROR));

    _this.lastLoadedFragLevel = 0;
    _this._nextAutoLevel = -1;
    _this.hls = hls;
    _this.timer = null;
    _this._bwEstimator = null;
    _this.onCheck = _this._abandonRulesCheck.bind(_this);
    return _this;
  }

  AbrController.prototype.destroy = function destroy() {
    this.clearTimer();
    event_handler.prototype.destroy.call(this);
  };

  AbrController.prototype.onFragLoading = function onFragLoading(data) {
    var frag = data.frag;
    if (frag.type === 'main') {
      if (!this.timer) {
        this.fragCurrent = frag;
        this.timer = setInterval(this.onCheck, 100);
      }

      // lazy init of BwEstimator, rationale is that we use different params for Live/VoD
      // so we need to wait for stream manifest / playlist type to instantiate it.
      if (!this._bwEstimator) {
        var hls = this.hls;
        var config = hls.config;
        var level = frag.level;
        var isLive = hls.levels[level].details.live;

share/public_html/static/hls.js  view on Meta::CPAN

    var video = hls.media;
    var frag = this.fragCurrent;

    if (!frag) {
      return;
    }

    var loader = frag.loader;
    var minAutoLevel = hls.minAutoLevel;

    // if loader has been destroyed or loading has been aborted, stop timer and return
    if (!loader || loader.stats && loader.stats.aborted) {
      logger["b" /* logger */].warn('frag loader destroy or aborted, disarm abandonRules');
      this.clearTimer();
      // reset forced auto level value so that next level will be selected
      this._nextAutoLevel = -1;
      return;
    }
    var stats = loader.stats;
    /* only monitor frag retrieval time if
    (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */

share/public_html/static/hls.js  view on Meta::CPAN

          // only emergency switch down if it takes less time to load new fragment at lowest level instead
          // of finishing loading current one ...
          if (fragLevelNextLoadedDelay < fragLoadedDelay) {
            logger["b" /* logger */].warn('loading too slow, abort fragment loading and switch to level ' + nextLoadLevel + ':fragLoadedDelay[' + nextLoadLevel + ']<fragLoadedDelay[' + (frag.level - 1) + '];bufferStarvationDelay:' + fragLevelNextLoad...
            // force next load level in auto mode
            hls.nextLoadLevel = nextLoadLevel;
            // update bw estimate for this fragment before cancelling load (this will help reducing the bw)
            this._bwEstimator.sample(requestDelay, stats.loaded);
            // abort fragment loading
            loader.abort();
            // stop abandon rules timer
            this.clearTimer();
            hls.trigger(events["a" /* default */].FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats });
          }
        }
      }
    }
  };

  AbrController.prototype.onFragLoaded = function onFragLoaded(data) {
    var frag = data.frag;

share/public_html/static/hls.js  view on Meta::CPAN

      // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration
      if (frag.bitrateTest) {
        this.bitrateTestDelay = fragLoadingProcessingMs / 1000;
      } else {
        this.bitrateTestDelay = 0;
      }
    }
  };

  AbrController.prototype.onError = function onError(data) {
    // stop timer in case of frag loading error
    switch (data.details) {
      case errors["a" /* ErrorDetails */].FRAG_LOAD_ERROR:
      case errors["a" /* ErrorDetails */].FRAG_LOAD_TIMEOUT:
        this.clearTimer();
        break;
      default:
        break;
    }
  };

  AbrController.prototype.clearTimer = function clearTimer() {
    clearInterval(this.timer);
    this.timer = null;
  };

  // return next auto level


  AbrController.prototype._findBestLevel = function _findBestLevel(currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) {
    for (var i = maxAutoLevel; i >= minAutoLevel; i--) {
      var levelInfo = levels[i],
          levelDetails = levelInfo.details,
          avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration,

share/public_html/static/hls.js  view on Meta::CPAN

  function CapLevelController(hls) {
    cap_level_controller__classCallCheck(this, CapLevelController);

    var _this = cap_level_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].FPS_DROP_LEVEL_CAPPING, events["a" /* default */].MEDIA_ATTACHING, events["a" /* default */].MANIFEST_PARSED, events["a" /* ...

    _this.autoLevelCapping = Number.POSITIVE_INFINITY;
    _this.firstLevel = null;
    _this.levels = [];
    _this.media = null;
    _this.restrictedLevels = [];
    _this.timer = null;
    return _this;
  }

  CapLevelController.prototype.destroy = function destroy() {
    if (this.hls.config.capLevelToPlayerSize) {
      this.media = null;
      this._stopCapping();
    }
  };

share/public_html/static/hls.js  view on Meta::CPAN

    }

    var validLevels = this.levels.filter(function (level, index) {
      return CapLevelController.isLevelAllowed(index, _this2.restrictedLevels) && index <= capLevelIndex;
    });

    return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight);
  };

  CapLevelController.prototype._startCapping = function _startCapping() {
    if (this.timer) {
      // Don't reset capping if started twice; this can happen if the manifest signals a video codec
      return;
    }
    this.autoLevelCapping = Number.POSITIVE_INFINITY;
    this.hls.firstLevel = this.getMaxLevel(this.firstLevel);
    clearInterval(this.timer);
    this.timer = setInterval(this.detectPlayerSize.bind(this), 1000);
    this.detectPlayerSize();
  };

  CapLevelController.prototype._stopCapping = function _stopCapping() {
    this.restrictedLevels = [];
    this.firstLevel = null;
    this.autoLevelCapping = Number.POSITIVE_INFINITY;
    if (this.timer) {
      this.timer = clearInterval(this.timer);
      this.timer = null;
    }
  };

  CapLevelController.isLevelAllowed = function isLevelAllowed(level) {
    var restrictedLevels = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];

    return restrictedLevels.indexOf(level) === -1;
  };

  CapLevelController.getMaxLevelByMediaSize = function getMaxLevelByMediaSize(levels, width, height) {

share/public_html/static/hls.js  view on Meta::CPAN

var fps_controller_FPSController = function (_EventHandler) {
  fps_controller__inherits(FPSController, _EventHandler);

  function FPSController(hls) {
    fps_controller__classCallCheck(this, FPSController);

    return fps_controller__possibleConstructorReturn(this, _EventHandler.call(this, hls, events["a" /* default */].MEDIA_ATTACHING));
  }

  FPSController.prototype.destroy = function destroy() {
    if (this.timer) {
      clearInterval(this.timer);
    }

    this.isVideoPlaybackQualityAvailable = false;
  };

  FPSController.prototype.onMediaAttaching = function onMediaAttaching(data) {
    var config = this.hls.config;
    if (config.capLevelOnFPSDrop) {
      var video = this.video = data.media instanceof window.HTMLVideoElement ? data.media : null;
      if (typeof video.getVideoPlaybackQuality === 'function') {
        this.isVideoPlaybackQualityAvailable = true;
      }

      clearInterval(this.timer);
      this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod);
    }
  };

  FPSController.prototype.checkFPS = function checkFPS(video, decodedFrames, droppedFrames) {
    var currentTime = fps_controller_performance.now();
    if (decodedFrames) {
      if (this.lastTime) {
        var currentPeriod = currentTime - this.lastTime,
            currentDropped = droppedFrames - this.lastDroppedFrames,
            currentDecoded = decodedFrames - this.lastDecodedFrames,

share/public_html/static/hls.js  view on Meta::CPAN

    // check if current playlist is a live playlist
    // and if we have already our reload interval setup
    if (data.details.live && !this.hasInterval()) {
      // if live playlist we will have to reload it periodically
      // set reload period to playlist target duration
      var updatePeriodMs = data.details.targetduration * 1000;
      this.setInterval(updatePeriodMs);
    }

    if (!data.details.live && this.hasInterval()) {
      // playlist is not live and timer is scheduled: cancel it
      this.clearInterval();
    }
  };

  /**
   * Update the internal group ID to any audio-track we may have set manually
   * or because of a failure-handling fallback.
   *
   * Quality-levels should update to that group ID in this case.
   *

share/public_html/static/hls.js  view on Meta::CPAN

   * @param {number} newId
   */


  AudioTrackController.prototype._updateTrack = function _updateTrack(newId) {
    // check if level idx is valid
    if (newId < 0 || newId >= this.tracks.length) {
      return;
    }

    // stopping live reloading timer if any
    this.clearInterval();
    this.trackId = newId;
    logger["b" /* logger */].log('trying to update audio-track ' + newId);
    var audioTrack = this.tracks[newId];
    this._loadTrackDetailsIfNeeded(audioTrack);
  };

  /**
   * @private
   */

share/public_html/static/hls.js  view on Meta::CPAN

      // check if level idx is valid
      if (newId < 0 || newId >= this.tracks.length) {
        logger["b" /* logger */].warn('Invalid id passed to audio-track controller');
        return;
      }

      var audioTrack = this.tracks[newId];

      logger["b" /* logger */].log('Now switching to audio-track index ' + newId);

      // stopping live reloading timer if any
      this.clearInterval();
      this.trackId = newId;

      var url = audioTrack.url,
          type = audioTrack.type,
          id = audioTrack.id;

      this.hls.trigger(events["a" /* default */].AUDIO_TRACK_SWITCHING, { id: id, type: type, url: url });
      this._loadTrackDetailsIfNeeded(audioTrack);
    }

share/public_html/static/hls.js  view on Meta::CPAN

    this.fragCurrent = null;
    this.state = audio_stream_controller_State.PAUSED;
    this.waitingFragment = null;
    // destroy useless demuxer when switching audio to main
    if (!altAudio) {
      if (this.demuxer) {
        this.demuxer.destroy();
        this.demuxer = null;
      }
    } else {
      // switching to audio track, start timer if not already started
      this.setInterval(100);
    }

    // should we switch tracks ?
    if (altAudio) {
      this.audioSwitch = true;
      // main audio track are handled by stream-controller, just do something if switching to alt audio track
      this.state = audio_stream_controller_State.IDLE;
    }
    this.tick();

share/public_html/static/hls.js  view on Meta::CPAN

    }
  };

  SubtitleTrackController.prototype.onSubtitleTrackLoaded = function onSubtitleTrackLoaded(data) {
    var _this4 = this;

    if (data.id < this.tracks.length) {
      logger["b" /* logger */].log('subtitle track ' + data.id + ' loaded');
      this.tracks[data.id].details = data.details;
      // check if current playlist is a live playlist
      if (data.details.live && !this.timer) {
        // if live playlist we will have to reload it periodically
        // set reload period to playlist target duration
        this.timer = setInterval(function () {
          _this4.onTick();
        }, 1000 * data.details.targetduration, this);
      }
      if (!data.details.live && this.timer) {
        // playlist is not live and timer is armed : stopping it
        this._stopTimer();
      }
    }
  };

  /** get alternate subtitle tracks list from playlist **/


  /**
   * This method is responsible for validating the subtitle index and periodically reloading if live.

share/public_html/static/hls.js  view on Meta::CPAN

    var subtitleTrack = tracks[newId];
    var details = subtitleTrack.details;
    if (!details || details.live) {
      // track not retrieved yet, or live playlist we need to (re)load it
      logger["b" /* logger */].log('(re)loading playlist for subtitle track ' + newId);
      hls.trigger(events["a" /* default */].SUBTITLE_TRACK_LOADING, { url: subtitleTrack.url, id: newId });
    }
  };

  SubtitleTrackController.prototype._stopTimer = function _stopTimer() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  };

  /**
   * Disables the old subtitleTrack and sets current mode on the next subtitleTrack.
   * This operates on the DOM textTracks.
   * A value of -1 will disable all subtitle tracks.
   * @param newId - The id of the next track to enable
   * @private
   */

share/public_html/static/music_inc/drflac.js  view on Meta::CPAN


var Module = (() => {
  var _scriptName = import.meta.url;
  
  return (
async function(moduleArg = {}) {
  var moduleRtn;

var Module=moduleArg;var readyPromiseResolve,readyPromiseReject;var readyPromise=new Promise((resolve,reject)=>{readyPromiseResolve=resolve;readyPromiseReject=reject});["_network_drflac_open_mem","_network_drflac_read_pcm_frames_f32_mem","_network_dr...


  return moduleRtn;
}
);
})();
export default Module;

share/public_html/static/music_inc/music_inc_module.js  view on Meta::CPAN

    if(!tracks.length) return;  
    QueueTracks(tracks, after);
}

// remove played items from the audio queue
function AQ_clean() {
    let toDelete = 0;
    for(let i = 0; i < AudioQueue.length; i++) {
        if(! AudioQueue[i].endTime) break;        

        // run and remove associated graphics timers
        let timerdel = 0;
        for(let j = 0; j < AudioQueue[i].timers.length; j++) {
            if(AudioQueue[i].timers[j].time <= MainAudioContext.currentTime) {
                console.log('aqid: ' + AudioQueue[i].aqid + ' running timer at ' + MainAudioContext.currentTime);
                AudioQueue[i].timers[j].func(AudioQueue[i].timers[j]);
                timerdel++;
            }
        }
        if(timerdel)AudioQueue[i].timers.splice(0, timerdel);
        
        // remove if it has passed
        if(AudioQueue[i].endTime <= MainAudioContext.currentTime) {
            console.log('aqid: ' + AudioQueue[i].aqid + ' segment elapsed, removing');
            toDelete++;
        }
    }
    if(toDelete) {
        // if the AQ is empty and there's a current track we fell behind
        if((toDelete === AudioQueue.length) && (Tracks_QueueCurrent)) {

share/public_html/static/music_inc/music_inc_module.js  view on Meta::CPAN

    if(dCount) {
        AudioQueue.splice(AudioQueue.length - dCount, dCount);
    }    
}

if(typeof abortablesleep === 'undefined') {
    //const sleep = m => new Promise(r => setTimeout(r, m));
    const abortablesleep = (ms, signal) => new Promise(function(resolve) {
        const onTimerDone = function() {
            resolve();
            signal.removeEventListener('abort', stoptimer);
        };
        let timer = setTimeout(function() {
            console.log('sleep done ' + ms);
            onTimerDone();
        }, ms);

        const stoptimer = function() {
            console.log('aborted sleep');            
            onTimerDone();
            clearTimeout(timer);            
        };
        signal.addEventListener('abort', stoptimer);
    });
    DeclareGlobalFunc('abortablesleep', abortablesleep);
}

let FAQ_MUTEX = new Mutex();
async function fillAudioQueue(time) {
    // starting a fresh queue, render the text
    if(Tracks_QueueCurrent) {
        let track = Tracks_QueueCurrent;
        let prevtext = track.prev ? track.prev.trackname : '';

share/public_html/static/music_inc/music_inc_module.js  view on Meta::CPAN

                        console.log('Encountered error twice, advancing to next track');
                         // assume it's corrupted. force free it
                        await NWDRFLAC.close();
                        NWDRFLAC = null;                      
                        continue TRACKLOOP;
                    }
                }
            }
         
            // Add to the audio queue
            let aqItem = { 'buffer' : buffer, 'duration' : todec, 'aqid' : AQID, 'skiptime' : (dectime / NWDRFLAC.sampleRate), 'track' : track, 'playbackinfo' : {}, 'timers' : []};
            // At start and end track update the GUI
            let isEnd = ((dectime+todec) === NWDRFLAC.totalPCMFrameCount);
            if(isStart || isEnd) {            
                aqItem.func = function(startTime, endTime) {
                    if(isStart) {
                        console.log('aqid: ' + aqItem.aqid + ' start timer at ' + startTime + ' currentTime ' + MainAudioContext.currentTime);
                        aqItem.timers.push({'time': startTime, 'func': function() {                             
                            seekbar.min = 0;
                            seekbar.max = track.duration;
                            SetEndtimeText(track.duration);
                            SetPlayText(track.trackname);
                            let prevtext = track.prev ? track.prev.trackname : '';
                            SetPrevText(prevtext);       
                            let nexttext =  track.next ? track.next.trackname : '';
                            SetNextText(nexttext);
                        }});
                        isStart = false;
                    }
                    if(isEnd) {
                        console.log('aqid: ' + aqItem.aqid + ' end timer at ' + endTime + ' currentTime ' + MainAudioContext.currentTime); 
                        aqItem.timers.push({'time': endTime, 'func': function(){
                            let curTime = 0;
                            SetEndtimeText(0);                    
                            SetCurtimeText(curTime);
                            SetSeekbarValue(curTime);
                            SetPrevText(track.trackname);
                            SetPlayText('');
                            SetNextText('');
                        }});
                    }
                }

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    ma_ios_session_category_option_default_to_speaker                         = 0x08,   /* AVAudioSessionCategoryOptionDefaultToSpeaker */
    ma_ios_session_category_option_interrupt_spoken_audio_and_mix_with_others = 0x11,   /* AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers */
    ma_ios_session_category_option_allow_bluetooth_a2dp                       = 0x20,   /* AVAudioSessionCategoryOptionAllowBluetoothA2DP */
    ma_ios_session_category_option_allow_air_play                             = 0x40,   /* AVAudioSessionCategoryOptionAllowAirPlay */
} ma_ios_session_category_option;

typedef union
{
    ma_int64 counter;
    double counterD;
} ma_timer;

typedef union
{
    wchar_t wasapi[64];             /* WASAPI uses a wchar_t string for identification. */
    ma_uint8 dsound[16];            /* DirectSound uses a GUID for identification. */
    /*UINT_PTR*/ ma_uint32 winmm;   /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
    char alsa[256];                 /* ALSA uses a name string for identification. */
    char pulse[256];                /* PulseAudio uses a name string for identification. */
    int jack;                       /* JACK always uses default devices. */
    char coreaudio[256];            /* Core Audio uses a string for identification. */

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

        } webaudio;
#endif
#ifdef MA_SUPPORT_NULL
        struct
        {
            ma_thread deviceThread;
            ma_event operationEvent;
            ma_event operationCompletionEvent;
            ma_uint32 operation;
            ma_result operationResult;
            ma_timer timer;
            double priorRunTime;
            ma_uint32 currentPeriodFramesRemainingPlayback;
            ma_uint32 currentPeriodFramesRemainingCapture;
            ma_uint64 lastProcessedFramePlayback;
            ma_uint64 lastProcessedFrameCapture;
            ma_bool32 isStarted;
        } null_device;
#endif
    };
};

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

}


/*******************************************************************************

Timing

*******************************************************************************/
#ifdef MA_WIN32
    static LARGE_INTEGER g_ma_TimerFrequency = {{0}};
    static void ma_timer_init(ma_timer* pTimer)
    {
        LARGE_INTEGER counter;

        if (g_ma_TimerFrequency.QuadPart == 0) {
            QueryPerformanceFrequency(&g_ma_TimerFrequency);
        }

        QueryPerformanceCounter(&counter);
        pTimer->counter = counter.QuadPart;
    }

    static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        LARGE_INTEGER counter;
        if (!QueryPerformanceCounter(&counter)) {
            return 0;
        }

        return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
    }
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
    static ma_uint64 g_ma_TimerFrequency = 0;
    static void ma_timer_init(ma_timer* pTimer)
    {
        mach_timebase_info_data_t baseTime;
        mach_timebase_info(&baseTime);
        g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;

        pTimer->counter = mach_absolute_time();
    }

    static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        ma_uint64 newTimeCounter = mach_absolute_time();
        ma_uint64 oldTimeCounter = pTimer->counter;

        return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
    }
#elif defined(MA_EMSCRIPTEN)
    static MA_INLINE void ma_timer_init(ma_timer* pTimer)
    {
        pTimer->counterD = emscripten_get_now();
    }

    static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        return (emscripten_get_now() - pTimer->counterD) / 1000;    /* Emscripten is in milliseconds. */
    }
#else
    #if _POSIX_C_SOURCE >= 199309L
        #if defined(CLOCK_MONOTONIC)
            #define MA_CLOCK_ID CLOCK_MONOTONIC
        #else
            #define MA_CLOCK_ID CLOCK_REALTIME
        #endif

        static void ma_timer_init(ma_timer* pTimer)
        {
            struct timespec newTime;
            clock_gettime(MA_CLOCK_ID, &newTime);

            pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
        }

        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
        {
            ma_uint64 newTimeCounter;
            ma_uint64 oldTimeCounter;

            struct timespec newTime;
            clock_gettime(MA_CLOCK_ID, &newTime);

            newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
            oldTimeCounter = pTimer->counter;

            return (newTimeCounter - oldTimeCounter) / 1000000000.0;
        }
    #else
        static void ma_timer_init(ma_timer* pTimer)
        {
            struct timeval newTime;
            gettimeofday(&newTime, NULL);

            pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
        }

        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
        {
            ma_uint64 newTimeCounter;
            ma_uint64 oldTimeCounter;

            struct timeval newTime;
            gettimeofday(&newTime, NULL);

            newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
            oldTimeCounter = pTimer->counter;

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    for (;;) {  /* Keep the thread alive until the device is uninitialized. */
        /* Wait for an operation to be requested. */
        ma_event_wait(&pDevice->null_device.operationEvent);

        /* At this point an event should have been triggered. */

        /* Starting the device needs to put the thread into a loop. */
        if (pDevice->null_device.operation == MA_DEVICE_OP_START__NULL) {
            c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);

            /* Reset the timer just in case. */
            ma_timer_init(&pDevice->null_device.timer);

            /* Keep looping until an operation has been requested. */
            while (pDevice->null_device.operation != MA_DEVICE_OP_NONE__NULL && pDevice->null_device.operation != MA_DEVICE_OP_START__NULL) {
                ma_sleep(10); /* Don't hog the CPU. */
            }

            /* Getting here means a suspend or kill operation has been requested. */
            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            continue;
        }

        /* Suspending the device means we need to stop the timer and just continue the loop. */
        if (pDevice->null_device.operation == MA_DEVICE_OP_SUSPEND__NULL) {
            c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);

            /* We need to add the current run time to the prior run time, then reset the timer. */
            pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
            ma_timer_init(&pDevice->null_device.timer);

            /* We're done. */
            c89atomic_exchange_32((c89atomic_uint32*)&pDevice->null_device.operationResult, MA_SUCCESS);
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            continue;
        }

        /* Killing the device means we need to get out of this loop so that this thread can terminate. */
        if (pDevice->null_device.operation == MA_DEVICE_OP_KILL__NULL) {
            c89atomic_exchange_32(&pDevice->null_device.operation, MA_DEVICE_OP_NONE__NULL);

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
    ma_uint32 internalSampleRate;
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        internalSampleRate = pDevice->capture.internalSampleRate;
    } else {
        internalSampleRate = pDevice->playback.internalSampleRate;
    }


    return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}

static ma_bool32 ma_context_is_device_id_equal__null(ma_context* pContext, const ma_device_id* pID0, const ma_device_id* pID1)
{
    MA_ASSERT(pContext != NULL);
    MA_ASSERT(pID0 != NULL);
    MA_ASSERT(pID1 != NULL);
    (void)pContext;

    return pID0->nullbackend == pID1->nullbackend;

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) {
        ma_strncpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), "NULL Playback Device", (size_t)-1);
        pDevice->playback.internalFormat             = pConfig->playback.format;
        pDevice->playback.internalChannels           = pConfig->playback.channels;
        ma_channel_map_copy(pDevice->playback.internalChannelMap, pConfig->playback.channelMap, ma_min(pConfig->playback.channels, MA_MAX_CHANNELS));
        pDevice->playback.internalPeriodSizeInFrames = periodSizeInFrames;
        pDevice->playback.internalPeriods            = pConfig->periods;
    }

    /*
    In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the
    first period is "written" to it, and then stopped in ma_device_stop__null().
    */
    result = ma_event_init(&pDevice->null_device.operationEvent);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
    if (result != MA_SUCCESS) {
        return result;

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    config.format           = format;
    config.channels         = channels;
    config.sampleRateIn     = sampleRateIn;
    config.sampleRateOut    = sampleRateOut;
    config.lpfOrder         = ma_min(MA_DEFAULT_RESAMPLER_LPF_ORDER, MA_MAX_FILTER_ORDER);
    config.lpfNyquistFactor = 1;

    return config;
}

static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
{
    /*
    So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will
    be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
    */
    ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut;  /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */
    ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;

    pResampler->inTimeFrac =
         (oldRateTimeWhole * newSampleRateOut) +

share/public_html/static/music_inc/src/miniaudio.h  view on Meta::CPAN

    }

    if (result != MA_SUCCESS) {
        return result;
    }


    pResampler->inAdvanceInt  = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
    pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;

    /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
    ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);

    return MA_SUCCESS;
}

MA_API ma_result ma_linear_resampler_init(const ma_linear_resampler_config* pConfig, ma_linear_resampler* pResampler)
{
    ma_result result;

    if (pResampler == NULL) {
        return MA_INVALID_ARGS;

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

and stopping sounds happens immediately, but sometimes it might be convenient to schedule the sound
the be started and/or stopped at a specific time. This can be done with the following functions:

    ```c
    ma_sound_set_start_time_in_pcm_frames()
    ma_sound_set_start_time_in_milliseconds()
    ma_sound_set_stop_time_in_pcm_frames()
    ma_sound_set_stop_time_in_milliseconds()
    ```

The start/stop time needs to be specified based on the absolute timer which is controlled by the
engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`.
The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if
required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()`
before anything will play:

    ```c
    ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2);
    ma_sound_start(&sound);
    ```

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

*/
MA_API ma_result ma_resampler_get_required_input_frame_count(const ma_resampler* pResampler, ma_uint64 outputFrameCount, ma_uint64* pInputFrameCount);

/*
Calculates the number of whole output frames that would be output after fully reading and consuming the specified number of
input frames.
*/
MA_API ma_result ma_resampler_get_expected_output_frame_count(const ma_resampler* pResampler, ma_uint64 inputFrameCount, ma_uint64* pOutputFrameCount);

/*
Resets the resampler's timer and clears it's internal cache.
*/
MA_API ma_result ma_resampler_reset(ma_resampler* pResampler);


/**************************************************************************************************************************************************************

Channel Conversion

**************************************************************************************************************************************************************/
typedef enum

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

    ma_aaudio_input_preset_voice_recognition,       /* AAUDIO_INPUT_PRESET_VOICE_RECOGNITION */
    ma_aaudio_input_preset_voice_communication,     /* AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION */
    ma_aaudio_input_preset_voice_performance        /* AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE */
} ma_aaudio_input_preset;


typedef union
{
    ma_int64 counter;
    double counterD;
} ma_timer;

typedef union
{
    wchar_t wasapi[64];             /* WASAPI uses a wchar_t string for identification. */
    ma_uint8 dsound[16];            /* DirectSound uses a GUID for identification. */
    /*UINT_PTR*/ ma_uint32 winmm;   /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */
    char alsa[256];                 /* ALSA uses a name string for identification. */
    char pulse[256];                /* PulseAudio uses a name string for identification. */
    int jack;                       /* JACK always uses default devices. */
    char coreaudio[256];            /* Core Audio uses a string for identification. */

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

#endif
#ifdef MA_SUPPORT_NULL
        struct
        {
            ma_thread deviceThread;
            ma_event operationEvent;
            ma_event operationCompletionEvent;
            ma_semaphore operationSemaphore;
            ma_uint32 operation;
            ma_result operationResult;
            ma_timer timer;
            double priorRunTime;
            ma_uint32 currentPeriodFramesRemainingPlayback;
            ma_uint32 currentPeriodFramesRemainingCapture;
            ma_uint64 lastProcessedFramePlayback;
            ma_uint64 lastProcessedFrameCapture;
            MA_ATOMIC(4, ma_bool32) isStarted;  /* Read and written by multiple threads. Must be used atomically, and must be 32-bit for compiler compatibility. */
        } null_device;
#endif
    };
};

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN




/*******************************************************************************

Timing

*******************************************************************************/
#ifdef MA_WIN32
    static LARGE_INTEGER g_ma_TimerFrequency;   /* <-- Initialized to zero since it's static. */
    void ma_timer_init(ma_timer* pTimer)
    {
        LARGE_INTEGER counter;

        if (g_ma_TimerFrequency.QuadPart == 0) {
            QueryPerformanceFrequency(&g_ma_TimerFrequency);
        }

        QueryPerformanceCounter(&counter);
        pTimer->counter = counter.QuadPart;
    }

    double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        LARGE_INTEGER counter;
        if (!QueryPerformanceCounter(&counter)) {
            return 0;
        }

        return (double)(counter.QuadPart - pTimer->counter) / g_ma_TimerFrequency.QuadPart;
    }
#elif defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200)
    static ma_uint64 g_ma_TimerFrequency = 0;
    static void ma_timer_init(ma_timer* pTimer)
    {
        mach_timebase_info_data_t baseTime;
        mach_timebase_info(&baseTime);
        g_ma_TimerFrequency = (baseTime.denom * 1e9) / baseTime.numer;

        pTimer->counter = mach_absolute_time();
    }

    static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        ma_uint64 newTimeCounter = mach_absolute_time();
        ma_uint64 oldTimeCounter = pTimer->counter;

        return (newTimeCounter - oldTimeCounter) / g_ma_TimerFrequency;
    }
#elif defined(MA_EMSCRIPTEN)
    static MA_INLINE void ma_timer_init(ma_timer* pTimer)
    {
        pTimer->counterD = emscripten_get_now();
    }

    static MA_INLINE double ma_timer_get_time_in_seconds(ma_timer* pTimer)
    {
        return (emscripten_get_now() - pTimer->counterD) / 1000;    /* Emscripten is in milliseconds. */
    }
#else
    #if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L
        #if defined(CLOCK_MONOTONIC)
            #define MA_CLOCK_ID CLOCK_MONOTONIC
        #else
            #define MA_CLOCK_ID CLOCK_REALTIME
        #endif

        static void ma_timer_init(ma_timer* pTimer)
        {
            struct timespec newTime;
            clock_gettime(MA_CLOCK_ID, &newTime);

            pTimer->counter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
        }

        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
        {
            ma_uint64 newTimeCounter;
            ma_uint64 oldTimeCounter;

            struct timespec newTime;
            clock_gettime(MA_CLOCK_ID, &newTime);

            newTimeCounter = (newTime.tv_sec * 1000000000) + newTime.tv_nsec;
            oldTimeCounter = pTimer->counter;

            return (newTimeCounter - oldTimeCounter) / 1000000000.0;
        }
    #else
        static void ma_timer_init(ma_timer* pTimer)
        {
            struct timeval newTime;
            gettimeofday(&newTime, NULL);

            pTimer->counter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
        }

        static double ma_timer_get_time_in_seconds(ma_timer* pTimer)
        {
            ma_uint64 newTimeCounter;
            ma_uint64 oldTimeCounter;

            struct timeval newTime;
            gettimeofday(&newTime, NULL);

            newTimeCounter = (newTime.tv_sec * 1000000) + newTime.tv_usec;
            oldTimeCounter = pTimer->counter;

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

        ma_uint32 operation;

        /* Wait for an operation to be requested. */
        ma_event_wait(&pDevice->null_device.operationEvent);

        /* At this point an event should have been triggered. */
        operation = pDevice->null_device.operation;

        /* Starting the device needs to put the thread into a loop. */
        if (operation == MA_DEVICE_OP_START__NULL) {
            /* Reset the timer just in case. */
            ma_timer_init(&pDevice->null_device.timer);

            /* Getting here means a suspend or kill operation has been requested. */
            pDevice->null_device.operationResult = MA_SUCCESS;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            continue;
        }

        /* Suspending the device means we need to stop the timer and just continue the loop. */
        if (operation == MA_DEVICE_OP_SUSPEND__NULL) {
            /* We need to add the current run time to the prior run time, then reset the timer. */
            pDevice->null_device.priorRunTime += ma_timer_get_time_in_seconds(&pDevice->null_device.timer);
            ma_timer_init(&pDevice->null_device.timer);

            /* We're done. */
            pDevice->null_device.operationResult = MA_SUCCESS;
            ma_event_signal(&pDevice->null_device.operationCompletionEvent);
            ma_semaphore_release(&pDevice->null_device.operationSemaphore);
            continue;
        }

        /* Killing the device means we need to get out of this loop so that this thread can terminate. */
        if (operation == MA_DEVICE_OP_KILL__NULL) {

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN


static ma_uint64 ma_device_get_total_run_time_in_frames__null(ma_device* pDevice)
{
    ma_uint32 internalSampleRate;
    if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) {
        internalSampleRate = pDevice->capture.internalSampleRate;
    } else {
        internalSampleRate = pDevice->playback.internalSampleRate;
    }

    return (ma_uint64)((pDevice->null_device.priorRunTime + ma_timer_get_time_in_seconds(&pDevice->null_device.timer)) * internalSampleRate);
}

static ma_result ma_context_enumerate_devices__null(ma_context* pContext, ma_enum_devices_callback_proc callback, void* pUserData)
{
    ma_bool32 cbResult = MA_TRUE;

    MA_ASSERT(pContext != NULL);
    MA_ASSERT(callback != NULL);

    /* Playback. */

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

        pDescriptorPlayback->sampleRate = (pDescriptorPlayback->sampleRate != 0)                 ? pDescriptorPlayback->sampleRate : MA_DEFAULT_SAMPLE_RATE;

        if (pDescriptorPlayback->channelMap[0] == MA_CHANNEL_NONE) {
            ma_channel_map_init_standard(ma_standard_channel_map_default, pDescriptorPlayback->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorPlayback->channels);
        }

        pDescriptorPlayback->periodSizeInFrames = ma_calculate_buffer_size_in_frames_from_descriptor(pDescriptorPlayback, pDescriptorPlayback->sampleRate, pConfig->performanceProfile);
    }

    /*
    In order to get timing right, we need to create a thread that does nothing but keeps track of the timer. This timer is started when the
    first period is "written" to it, and then stopped in ma_device_stop__null().
    */
    result = ma_event_init(&pDevice->null_device.operationEvent);
    if (result != MA_SUCCESS) {
        return result;
    }

    result = ma_event_init(&pDevice->null_device.operationCompletionEvent);
    if (result != MA_SUCCESS) {
        return result;

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

        /* Fast path. No gain calculation required. */
        ma_copy_and_apply_volume_factor_per_channel_f32(pFramesOutF32, pFramesInF32, frameCount, pGainer->config.channels, pGainer->pNewGains);

        /* Now that some frames have been processed we need to make sure future changes to the gain are interpolated. */
        if (pGainer->t == (ma_uint32)-1) {
            pGainer->t = pGainer->config.smoothTimeInFrames;
        }
    } else {
        /* Slow path. Need to interpolate the gain for each channel individually. */

        /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
        if (pFramesOut != NULL && pFramesIn != NULL) {
            float a = (float)pGainer->t / pGainer->config.smoothTimeInFrames;
            float d = 1.0f / pGainer->config.smoothTimeInFrames;
            ma_uint32 channelCount = pGainer->config.channels;

            for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
                for (iChannel = 0; iChannel < channelCount; iChannel += 1) {
                    pFramesOutF32[iChannel] = pFramesInF32[iChannel] * ma_mix_f32_fast(pGainer->pOldGains[iChannel], pGainer->pNewGains[iChannel], a);
                }

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

                if (a > 1) {
                    a = 1;
                }
            }
        }

        pGainer->t = (ma_uint32)ma_min(pGainer->t + frameCount, pGainer->config.smoothTimeInFrames);

    #if 0   /* Reference implementation. */
        for (iFrame = 0; iFrame < frameCount; iFrame += 1) {
            /* We can allow the input and output buffers to be null in which case we'll just update the internal timer. */
            if (pFramesOut != NULL && pFramesIn != NULL) {
                for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) {
                    pFramesOutF32[iFrame*pGainer->config.channels + iChannel] = pFramesInF32[iFrame*pGainer->config.channels + iChannel] * ma_gainer_calculate_current_gain(pGainer, iChannel);
                }
            }

            /* Move interpolation time forward, but don't go beyond our smoothing time. */
            pGainer->t = ma_min(pGainer->t + 1, pGainer->config.smoothTimeInFrames);
        }
    #endif

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN


typedef struct
{
    size_t sizeInBytes;
    size_t x0Offset;
    size_t x1Offset;
    size_t lpfOffset;
} ma_linear_resampler_heap_layout;


static void ma_linear_resampler_adjust_timer_for_new_rate(ma_linear_resampler* pResampler, ma_uint32 oldSampleRateOut, ma_uint32 newSampleRateOut)
{
    /*
    So what's happening here? Basically we need to adjust the fractional component of the time advance based on the new rate. The old time advance will
    be based on the old sample rate, but we are needing to adjust it to that it's based on the new sample rate.
    */
    ma_uint32 oldRateTimeWhole = pResampler->inTimeFrac / oldSampleRateOut;  /* <-- This should almost never be anything other than 0, but leaving it here to make this more general and robust just in case. */
    ma_uint32 oldRateTimeFract = pResampler->inTimeFrac % oldSampleRateOut;

    pResampler->inTimeFrac =
         (oldRateTimeWhole * newSampleRateOut) +

share/public_html/static/music_worklet_inprogress/decoder/deps/miniaudio/miniaudio.h  view on Meta::CPAN

    }

    if (result != MA_SUCCESS) {
        return result;
    }


    pResampler->inAdvanceInt  = pResampler->config.sampleRateIn / pResampler->config.sampleRateOut;
    pResampler->inAdvanceFrac = pResampler->config.sampleRateIn % pResampler->config.sampleRateOut;

    /* Our timer was based on the old rate. We need to adjust it so that it's based on the new rate. */
    ma_linear_resampler_adjust_timer_for_new_rate(pResampler, oldSampleRateOut, pResampler->config.sampleRateOut);

    return MA_SUCCESS;
}

static ma_result ma_linear_resampler_get_heap_layout(const ma_linear_resampler_config* pConfig, ma_linear_resampler_heap_layout* pHeapLayout)
{
    MA_ASSERT(pHeapLayout != NULL);

    MA_ZERO_OBJECT(pHeapLayout);

share/public_html/static/music_worklet_inprogress/player/mhfsplayer.js  view on Meta::CPAN

      willLock.then(() => this._locked = false);
      let willUnlock = this._locking.then(() => unlockNext);
      this._locking = this._locking.then(() => willLock);
      return willUnlock;
    }
}

const abortablesleep = (ms, signal) => new Promise(function(resolve) {
    const onTimerDone = function() {
        resolve();
        signal.removeEventListener('abort', stoptimer);
    };
    let timer = setTimeout(function() {
        //console.log('sleep done ' + ms);
        onTimerDone();
    }, ms);

    const stoptimer = function() {
        console.log('aborted sleep');            
        onTimerDone();
        clearTimeout(timer);            
    };
    signal.addEventListener('abort', stoptimer);
});

const abortablesleep_status = async function (ms, signal) {
    await abortablesleep(ms, signal);
    if(signal.aborted) {
        return false;
    }
    return true;
}



( run in 1.501 second using v1.01-cache-2.11-cpan-49f99fa48dc )