def play_internal
      count = 0
      @playing = true

      # Default play length (ms), will be adjusted later
      @length = IDEAL_LENGTH

      self.speaking = true
      loop do
        # Starting from the tenth packet, perform length adjustment every 100 packets (2 seconds)
        should_adjust_this_packet = (count % @adjust_interval == @adjust_offset)

        # If we should adjust, start now
        @length_adjust = Time.now.nsec if should_adjust_this_packet

        break unless @playing

        # If we should skip, get some data, discard it and go to the next iteration
        if @skips.positive?
          @skips -= 1
          yield
          next
        end

        # Track packet count, sequence and time (Discord requires this)
        count += 1
        increment_packet_headers

        # Get packet data
        buf = yield

        # Stop doing anything if the stop signal was sent
        break if buf == :stop

        # Proceed to the next packet if we got nil
        next unless buf

        # Track intermediate adjustment so we can measure how much encoding contributes to the total time
        @intermediate_adjust = Time.now.nsec if should_adjust_this_packet

        # Send the packet
        @udp.send_audio(buf, @sequence, @time)

        # Set the stream time (for tracking how long we've been playing)
        @stream_time = count * @length / 1000

        if @length_override # Don't do adjustment because the user has manually specified an override value
          @length = @length_override
        elsif @length_adjust # Perform length adjustment
          # Define the time once so it doesn't get inaccurate
          now = Time.now.nsec

          # Difference between length_adjust and now in ms
          ms_diff = (now - @length_adjust) / 1_000_000.0
          if ms_diff >= 0
            @length = if @adjust_average
                        (IDEAL_LENGTH - ms_diff + @length) / 2.0
                      else
                        IDEAL_LENGTH - ms_diff
                      end

            # Track the time it took to encode
            encode_ms = (@intermediate_adjust - @length_adjust) / 1_000_000.0
            @bot.debug("Length adjustment: new length #{@length} (measured #{ms_diff}, #{(100 * encode_ms) / ms_diff}% encoding)") if @adjust_debug
          end
          @length_adjust = nil
        end

        # If paused, wait
        sleep 0.1 while @paused

        if @length.positive?
          # Wait `length` ms, then send the next packet
          sleep @length / 1000.0
        else
          Discordrb::LOGGER.warn('Audio encoding and sending together took longer than Discord expects one packet to be (20 ms)! This may be indicative of network problems.')
        end
      end

      @bot.debug('Sending five silent frames to clear out buffers')

      5.times do
        increment_packet_headers
        @udp.send_audio(Encoder::OPUS_SILENCE, @sequence, @time)

        # Length adjustments don't matter here, we can just wait 20 ms since nobody is going to hear it anyway
        sleep IDEAL_LENGTH / 1000.0
      end

      @bot.debug('Performing final cleanup after stream ended')

      # Final cleanup
      stop_playing

      # Notify any stop_playing methods running right now that we have actually stopped
      @has_stopped_playing = true
    end
def play_internal
      count = 0
      @playing = true

      # Default play length (ms), will be adjusted later
      @length = IDEAL_LENGTH

      self.speaking = true
      loop do
        # Starting from the tenth packet, perform length adjustment every 100 packets (2 seconds)
        should_adjust_this_packet = (count % @adjust_interval == @adjust_offset)

        # If we should adjust, start now
        @length_adjust = Time.now.nsec if should_adjust_this_packet

        break unless @playing

        # If we should skip, get some data, discard it and go to the next iteration
        if @skips.positive?
          @skips -= 1
          yield
          next
        end

        # Track packet count, sequence and time (Discord requires this)
        count += 1
        increment_packet_headers

        # Get packet data
        buf = yield

        # Stop doing anything if the stop signal was sent
        break if buf == :stop

        # Proceed to the next packet if we got nil
        next unless buf

        # Track intermediate adjustment so we can measure how much encoding contributes to the total time
        @intermediate_adjust = Time.now.nsec if should_adjust_this_packet

        # Send the packet
        @udp.send_audio(buf, @sequence, @time)

        # Set the stream time (for tracking how long we've been playing)
        @stream_time = count * @length / 1000

        if @length_override # Don't do adjustment because the user has manually specified an override value
          @length = @length_override
        elsif @length_adjust # Perform length adjustment
          # Define the time once so it doesn't get inaccurate
          now = Time.now.nsec

          # Difference between length_adjust and now in ms
          ms_diff = (now - @length_adjust) / 1_000_000.0
          if ms_diff >= 0
            @length = if @adjust_average
                        (IDEAL_LENGTH - ms_diff + @length) / 2.0
                      else
                        IDEAL_LENGTH - ms_diff
                      end

            # Track the time it took to encode
            encode_ms = (@intermediate_adjust - @length_adjust) / 1_000_000.0
            @bot.debug("Length adjustment: new length #{@length} (measured #{ms_diff}, #{(100 * encode_ms) / ms_diff}% encoding)") if @adjust_debug
          end
          @length_adjust = nil
        end

        # If paused, wait
        sleep 0.1 while @paused

        if @length.positive?
          # Wait `length` ms, then send the next packet
          sleep @length / 1000.0
        else
          Discordrb::LOGGER.warn('Audio encoding and sending together took longer than Discord expects one packet to be (20 ms)! This may be indicative of network problems.')
        end
      end

      @bot.debug('Sending five silent frames to clear out buffers')

      5.times do
        increment_packet_headers
        @udp.send_audio(Encoder::OPUS_SILENCE, @sequence, @time)

        # Length adjustments don't matter here, we can just wait 20 ms since nobody is going to hear it anyway
        sleep IDEAL_LENGTH / 1000.0
      end

      @bot.debug('Performing final cleanup after stream ended')

      # Final cleanup
      stop_playing

      # Notify any stop_playing methods running right now that we have actually stopped
      @has_stopped_playing = true
    end
Plays the data from the @io stream as Discord requires it