<commit_before>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.exoplayer.audio;

import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Util;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTimestamp;
import android.media.MediaFormat;
import android.os.ConditionVariable;
import android.util.Log;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;

/**
 * Plays audio data. The implementation delegates to an {@link android.media.AudioTrack} and handles
 * playback position smoothing, non-blocking writes and reconfiguration.
 *
 * <p>If {@link #isInitialized} returns {@code false}, the instance can be {@link #initialize}d.
 * After initialization, start playback by calling {@link #play}.
 *
 * <p>Call {@link #handleBuffer} to write data for playback.
 *
 * <p>Call {@link #handleDiscontinuity} when a buffer is skipped.
 *
 * <p>Call {@link #reconfigure} when the output format changes.
 *
 * <p>Call {@link #reset} to free resources. It is safe to re-{@link #initialize} the instance.
 *
 * <p>Call {@link #release} when the instance will no longer be used.
 */
@TargetApi(16)
public final class AudioTrack {

  /**
   * Thrown when a failure occurs instantiating an {@link android.media.AudioTrack}.
   */
  public static class InitializationException extends Exception {

    /** The state as reported by {@link android.media.AudioTrack#getState()}. */
    public final int audioTrackState;

    public InitializationException(
        int audioTrackState, int sampleRate, int channelConfig, int bufferSize) {
      super("AudioTrack init failed: " + audioTrackState + ", Config(" + sampleRate + ", "
          + channelConfig + ", " + bufferSize + ")");
      this.audioTrackState = audioTrackState;
    }

  }

  /**
   * Thrown when a failure occurs writing to an {@link android.media.AudioTrack}.
   */
  public static class WriteException extends Exception {

    /** The value returned from {@link android.media.AudioTrack#write(byte[], int, int)}. */
    public final int errorCode;

    public WriteException(int errorCode) {
      super("AudioTrack write failed: " + errorCode);
      this.errorCode = errorCode;
    }

  }

  /** Returned in the result of {@link #handleBuffer} if the buffer was discontinuous. */
  public static final int RESULT_POSITION_DISCONTINUITY = 1;
  /** Returned in the result of {@link #handleBuffer} if the buffer can be released. */
  public static final int RESULT_BUFFER_CONSUMED = 2;

  /** Represents an unset {@link android.media.AudioTrack} session identifier. */
  public static final int SESSION_ID_NOT_SET = 0;

  /** Returned by {@link #getCurrentPositionUs} when the position is not set. */
  public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE;

  /**
   * Set to {@code true} to enable a workaround for an issue where an audio effect does not keep its
   * session active across releasing/initializing a new audio track, on platform API version < 21.
   */
  private static final boolean ENABLE_PRE_V21_AUDIO_SESSION_WORKAROUND = false;

  /** A minimum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
  private static final long MIN_BUFFER_DURATION_US = 250000;
  /** A maximum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
  private static final long MAX_BUFFER_DURATION_US = 750000;
  /**
   * A multiplication factor to apply to the minimum buffer size requested by the underlying
   * {@link android.media.AudioTrack}.
   */
  private static final int BUFFER_MULTIPLICATION_FACTOR = 4;

  private static final String TAG = "AudioTrack";

  /**
   * AudioTrack timestamps are deemed spurious if they are offset from the system clock by more
   * than this amount.
   *
   * <p>This is a fail safe that should not be required on correctly functioning devices.
   */
  private static final long MAX_AUDIO_TIMESTAMP_OFFSET_US = 5 * C.MICROS_PER_SECOND;

  /**
   * AudioTrack latencies are deemed impossibly large if they are greater than this amount.
   *
   * <p>This is a fail safe that should not be required on correctly functioning devices.
   */
  private static final long MAX_LATENCY_US = 5 * C.MICROS_PER_SECOND;

  /** Value for ac3Bitrate before the bitrate has been calculated. */
  private static final int UNKNOWN_AC3_BITRATE = 0;

  private static final int START_NOT_SET = 0;
  private static final int START_IN_SYNC = 1;
  private static final int START_NEED_SYNC = 2;

  private static final int MAX_PLAYHEAD_OFFSET_COUNT = 10;
  private static final int MIN_PLAYHEAD_OFFSET_SAMPLE_INTERVAL_US = 30000;
  private static final int MIN_TIMESTAMP_SAMPLE_INTERVAL_US = 500000;

  private final ConditionVariable releasingConditionVariable;
  private final long[] playheadOffsets;

  /** Used to keep the audio session active on pre-V21 builds (see {@link #initialize()}). */
  private android.media.AudioTrack keepSessionIdAudioTrack;

  private android.media.AudioTrack audioTrack;
  private AudioTrackUtil audioTrackUtil;
  private int sampleRate;
  private int channelConfig;
  private int encoding;
  private int frameSize;
  private int minBufferSize;
  private int bufferSize;

  private int nextPlayheadOffsetIndex;
  private int playheadOffsetCount;
  private long smoothedPlayheadOffsetUs;
  private long lastPlayheadSampleTimeUs;
  private boolean audioTimestampSet;
  private long lastTimestampSampleTimeUs;

  private Method getLatencyMethod;
  private long submittedBytes;
  private int startMediaTimeState;
  private long startMediaTimeUs;
  private long resumeSystemTimeUs;
  private long latencyUs;
  private float volume;

  private byte[] temporaryBuffer;
  private int temporaryBufferOffset;
  private int temporaryBufferSize;

  private boolean isAc3;

  /** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */
  private int ac3Bitrate;

  public AudioTrack() {
    releasingConditionVariable = new ConditionVariable(true);
    if (Util.SDK_INT >= 18) {
      try {
        getLatencyMethod =
            android.media.AudioTrack.class.getMethod("getLatency", (Class<?>[]) null);
      } catch (NoSuchMethodException e) {
        // There's no guarantee this method exists. Do nothing.
      }
    }
    playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
    volume = 1.0f;
    startMediaTimeState = START_NOT_SET;
  }

  /**
   * Returns whether the audio track has been successfully initialized via {@link #initialize} and
   * not yet {@link #reset}.
   */
  public boolean isInitialized() {
    return audioTrack != null;
  }

  /**
   * Returns the playback position in the stream starting at zero, in microseconds, or
   * {@link #CURRENT_POSITION_NOT_SET} if it is not yet available.
   *
   * <p>If the device supports it, the method uses the playback timestamp from
   * {@link android.media.AudioTrack#getTimestamp}. Otherwise, it derives a smoothed position by
   * sampling the {@link android.media.AudioTrack}'s frame position.
   *
   * @param sourceEnded Specify {@code true} if no more input buffers will be provided.
   * @return The playback position relative to the start of playback, in microseconds.
   */
  public long getCurrentPositionUs(boolean sourceEnded) {
    if (!hasCurrentPositionUs()) {
      return CURRENT_POSITION_NOT_SET;
    }

    if (audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PLAYING) {
      maybeSampleSyncParams();
    }

    long systemClockUs = System.nanoTime() / 1000;
    long currentPositionUs;
    if (audioTimestampSet) {
      // How long ago in the past the audio timestamp is (negative if it's in the future).
      long presentationDiff = systemClockUs - (audioTrackUtil.getTimestampNanoTime() / 1000);
      long framesDiff = durationUsToFrames(presentationDiff);
      // The position of the frame that's currently being presented.
      long currentFramePosition = audioTrackUtil.getTimestampFramePosition() + framesDiff;
      currentPositionUs = framesToDurationUs(currentFramePosition) + startMediaTimeUs;
    } else {
      if (playheadOffsetCount == 0) {
        // The AudioTrack has started, but we don't have any samples to compute a smoothed position.
        currentPositionUs = audioTrackUtil.getPlaybackHeadPositionUs() + startMediaTimeUs;
      } else {
        // getPlayheadPositionUs() only has a granularity of ~20ms, so we base the position off the
        // system clock (and a smoothed offset between it and the playhead position) so as to
        // prevent jitter in the reported positions.
        currentPositionUs = systemClockUs + smoothedPlayheadOffsetUs + startMediaTimeUs;
      }
      if (!sourceEnded) {
        currentPositionUs -= latencyUs;
      }
    }

    return currentPositionUs;
  }

  /**
   * Initializes the audio track for writing new buffers using {@link #handleBuffer}.
   *
   * @return The audio track session identifier.
   */
  public int initialize() throws InitializationException {
    return initialize(SESSION_ID_NOT_SET);
  }

  /**
   * Initializes the audio track for writing new buffers using {@link #handleBuffer}.
   *
   * @param sessionId Audio track session identifier to re-use, or {@link #SESSION_ID_NOT_SET} to
   *     create a new one.
   * @return The new (or re-used) session identifier.
   */
  public int initialize(int sessionId) throws InitializationException {
    // If we're asynchronously releasing a previous audio track then we block until it has been
    // released. This guarantees that we cannot end up in a state where we have multiple audio
    // track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
    // the shared memory that's available for audio track buffers. This would in turn cause the
    // initialization of the audio track to fail.
    releasingConditionVariable.block();

    if (sessionId == SESSION_ID_NOT_SET) {
      audioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
          channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STREAM);
    } else {
      // Re-attach to the same audio session.
      audioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
          channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STREAM, sessionId);
    }
    checkAudioTrackInitialized();

    sessionId = audioTrack.getAudioSessionId();
    if (ENABLE_PRE_V21_AUDIO_SESSION_WORKAROUND) {
      if (Util.SDK_INT < 21) {
        // The workaround creates an audio track with a one byte buffer on the same session, and
        // does not release it until this object is released, which keeps the session active.
        if (keepSessionIdAudioTrack != null
            && sessionId != keepSessionIdAudioTrack.getAudioSessionId()) {
          releaseKeepSessionIdAudioTrack();
        }
        if (keepSessionIdAudioTrack == null) {
          int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
          int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
          int encoding = AudioFormat.ENCODING_PCM_16BIT;
          int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
          keepSessionIdAudioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC,
              sampleRate, channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STATIC,
              sessionId);
        }
      }
    }

    if (Util.SDK_INT >= 19) {
      audioTrackUtil = new AudioTrackUtilV19(audioTrack);
    } else {
      audioTrackUtil = new AudioTrackUtil(audioTrack);<commit_msg>Make the audio track session workaround flag non-final.<commit_after>/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.android.exoplayer.audio;

import com.google.android.exoplayer.C;
import com.google.android.exoplayer.util.Util;

import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTimestamp;
import android.media.MediaFormat;
import android.os.ConditionVariable;
import android.util.Log;

import java.lang.reflect.Method;
import java.nio.ByteBuffer;

/**
 * Plays audio data. The implementation delegates to an {@link android.media.AudioTrack} and handles
 * playback position smoothing, non-blocking writes and reconfiguration.
 *
 * <p>If {@link #isInitialized} returns {@code false}, the instance can be {@link #initialize}d.
 * After initialization, start playback by calling {@link #play}.
 *
 * <p>Call {@link #handleBuffer} to write data for playback.
 *
 * <p>Call {@link #handleDiscontinuity} when a buffer is skipped.
 *
 * <p>Call {@link #reconfigure} when the output format changes.
 *
 * <p>Call {@link #reset} to free resources. It is safe to re-{@link #initialize} the instance.
 *
 * <p>Call {@link #release} when the instance will no longer be used.
 */
@TargetApi(16)
public final class AudioTrack {

  /**
   * Thrown when a failure occurs instantiating an {@link android.media.AudioTrack}.
   */
  public static class InitializationException extends Exception {

    /** The state as reported by {@link android.media.AudioTrack#getState()}. */
    public final int audioTrackState;

    public InitializationException(
        int audioTrackState, int sampleRate, int channelConfig, int bufferSize) {
      super("AudioTrack init failed: " + audioTrackState + ", Config(" + sampleRate + ", "
          + channelConfig + ", " + bufferSize + ")");
      this.audioTrackState = audioTrackState;
    }

  }

  /**
   * Thrown when a failure occurs writing to an {@link android.media.AudioTrack}.
   */
  public static class WriteException extends Exception {

    /** The value returned from {@link android.media.AudioTrack#write(byte[], int, int)}. */
    public final int errorCode;

    public WriteException(int errorCode) {
      super("AudioTrack write failed: " + errorCode);
      this.errorCode = errorCode;
    }

  }

  /** Returned in the result of {@link #handleBuffer} if the buffer was discontinuous. */
  public static final int RESULT_POSITION_DISCONTINUITY = 1;
  /** Returned in the result of {@link #handleBuffer} if the buffer can be released. */
  public static final int RESULT_BUFFER_CONSUMED = 2;

  /** Represents an unset {@link android.media.AudioTrack} session identifier. */
  public static final int SESSION_ID_NOT_SET = 0;

  /** Returned by {@link #getCurrentPositionUs} when the position is not set. */
  public static final long CURRENT_POSITION_NOT_SET = Long.MIN_VALUE;

  /** A minimum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
  private static final long MIN_BUFFER_DURATION_US = 250000;
  /** A maximum length for the {@link android.media.AudioTrack} buffer, in microseconds. */
  private static final long MAX_BUFFER_DURATION_US = 750000;
  /**
   * A multiplication factor to apply to the minimum buffer size requested by the underlying
   * {@link android.media.AudioTrack}.
   */
  private static final int BUFFER_MULTIPLICATION_FACTOR = 4;

  private static final String TAG = "AudioTrack";

  /**
   * AudioTrack timestamps are deemed spurious if they are offset from the system clock by more
   * than this amount.
   *
   * <p>This is a fail safe that should not be required on correctly functioning devices.
   */
  private static final long MAX_AUDIO_TIMESTAMP_OFFSET_US = 5 * C.MICROS_PER_SECOND;

  /**
   * AudioTrack latencies are deemed impossibly large if they are greater than this amount.
   *
   * <p>This is a fail safe that should not be required on correctly functioning devices.
   */
  private static final long MAX_LATENCY_US = 5 * C.MICROS_PER_SECOND;

  /** Value for ac3Bitrate before the bitrate has been calculated. */
  private static final int UNKNOWN_AC3_BITRATE = 0;

  private static final int START_NOT_SET = 0;
  private static final int START_IN_SYNC = 1;
  private static final int START_NEED_SYNC = 2;

  private static final int MAX_PLAYHEAD_OFFSET_COUNT = 10;
  private static final int MIN_PLAYHEAD_OFFSET_SAMPLE_INTERVAL_US = 30000;
  private static final int MIN_TIMESTAMP_SAMPLE_INTERVAL_US = 500000;

  /**
   * Set to {@code true} to enable a workaround for an issue where an audio effect does not keep its
   * session active across releasing/initializing a new audio track, on platform API version < 21.
   * The flag must be set before creating the player.
   */
  public static boolean enablePreV21AudioSessionWorkaround = false;

  private final ConditionVariable releasingConditionVariable;
  private final long[] playheadOffsets;

  /** Used to keep the audio session active on pre-V21 builds (see {@link #initialize()}). */
  private android.media.AudioTrack keepSessionIdAudioTrack;

  private android.media.AudioTrack audioTrack;
  private AudioTrackUtil audioTrackUtil;
  private int sampleRate;
  private int channelConfig;
  private int encoding;
  private int frameSize;
  private int minBufferSize;
  private int bufferSize;

  private int nextPlayheadOffsetIndex;
  private int playheadOffsetCount;
  private long smoothedPlayheadOffsetUs;
  private long lastPlayheadSampleTimeUs;
  private boolean audioTimestampSet;
  private long lastTimestampSampleTimeUs;

  private Method getLatencyMethod;
  private long submittedBytes;
  private int startMediaTimeState;
  private long startMediaTimeUs;
  private long resumeSystemTimeUs;
  private long latencyUs;
  private float volume;

  private byte[] temporaryBuffer;
  private int temporaryBufferOffset;
  private int temporaryBufferSize;

  private boolean isAc3;

  /** Bitrate measured in kilobits per second, if {@link #isAc3} is true. */
  private int ac3Bitrate;

  public AudioTrack() {
    releasingConditionVariable = new ConditionVariable(true);
    if (Util.SDK_INT >= 18) {
      try {
        getLatencyMethod =
            android.media.AudioTrack.class.getMethod("getLatency", (Class<?>[]) null);
      } catch (NoSuchMethodException e) {
        // There's no guarantee this method exists. Do nothing.
      }
    }
    playheadOffsets = new long[MAX_PLAYHEAD_OFFSET_COUNT];
    volume = 1.0f;
    startMediaTimeState = START_NOT_SET;
  }

  /**
   * Returns whether the audio track has been successfully initialized via {@link #initialize} and
   * not yet {@link #reset}.
   */
  public boolean isInitialized() {
    return audioTrack != null;
  }

  /**
   * Returns the playback position in the stream starting at zero, in microseconds, or
   * {@link #CURRENT_POSITION_NOT_SET} if it is not yet available.
   *
   * <p>If the device supports it, the method uses the playback timestamp from
   * {@link android.media.AudioTrack#getTimestamp}. Otherwise, it derives a smoothed position by
   * sampling the {@link android.media.AudioTrack}'s frame position.
   *
   * @param sourceEnded Specify {@code true} if no more input buffers will be provided.
   * @return The playback position relative to the start of playback, in microseconds.
   */
  public long getCurrentPositionUs(boolean sourceEnded) {
    if (!hasCurrentPositionUs()) {
      return CURRENT_POSITION_NOT_SET;
    }

    if (audioTrack.getPlayState() == android.media.AudioTrack.PLAYSTATE_PLAYING) {
      maybeSampleSyncParams();
    }

    long systemClockUs = System.nanoTime() / 1000;
    long currentPositionUs;
    if (audioTimestampSet) {
      // How long ago in the past the audio timestamp is (negative if it's in the future).
      long presentationDiff = systemClockUs - (audioTrackUtil.getTimestampNanoTime() / 1000);
      long framesDiff = durationUsToFrames(presentationDiff);
      // The position of the frame that's currently being presented.
      long currentFramePosition = audioTrackUtil.getTimestampFramePosition() + framesDiff;
      currentPositionUs = framesToDurationUs(currentFramePosition) + startMediaTimeUs;
    } else {
      if (playheadOffsetCount == 0) {
        // The AudioTrack has started, but we don't have any samples to compute a smoothed position.
        currentPositionUs = audioTrackUtil.getPlaybackHeadPositionUs() + startMediaTimeUs;
      } else {
        // getPlayheadPositionUs() only has a granularity of ~20ms, so we base the position off the
        // system clock (and a smoothed offset between it and the playhead position) so as to
        // prevent jitter in the reported positions.
        currentPositionUs = systemClockUs + smoothedPlayheadOffsetUs + startMediaTimeUs;
      }
      if (!sourceEnded) {
        currentPositionUs -= latencyUs;
      }
    }

    return currentPositionUs;
  }

  /**
   * Initializes the audio track for writing new buffers using {@link #handleBuffer}.
   *
   * @return The audio track session identifier.
   */
  public int initialize() throws InitializationException {
    return initialize(SESSION_ID_NOT_SET);
  }

  /**
   * Initializes the audio track for writing new buffers using {@link #handleBuffer}.
   *
   * @param sessionId Audio track session identifier to re-use, or {@link #SESSION_ID_NOT_SET} to
   *     create a new one.
   * @return The new (or re-used) session identifier.
   */
  public int initialize(int sessionId) throws InitializationException {
    // If we're asynchronously releasing a previous audio track then we block until it has been
    // released. This guarantees that we cannot end up in a state where we have multiple audio
    // track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
    // the shared memory that's available for audio track buffers. This would in turn cause the
    // initialization of the audio track to fail.
    releasingConditionVariable.block();

    if (sessionId == SESSION_ID_NOT_SET) {
      audioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
          channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STREAM);
    } else {
      // Re-attach to the same audio session.
      audioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
          channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STREAM, sessionId);
    }
    checkAudioTrackInitialized();

    sessionId = audioTrack.getAudioSessionId();
    if (enablePreV21AudioSessionWorkaround) {
      if (Util.SDK_INT < 21) {
        // The workaround creates an audio track with a one byte buffer on the same session, and
        // does not release it until this object is released, which keeps the session active.
        if (keepSessionIdAudioTrack != null
            && sessionId != keepSessionIdAudioTrack.getAudioSessionId()) {
          releaseKeepSessionIdAudioTrack();
        }
        if (keepSessionIdAudioTrack == null) {
          int sampleRate = 4000; // Equal to private android.media.AudioTrack.MIN_SAMPLE_RATE.
          int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
          int encoding = AudioFormat.ENCODING_PCM_16BIT;
          int bufferSize = 2; // Use a two byte buffer, as it is not actually used for playback.
          keepSessionIdAudioTrack = new android.media.AudioTrack(AudioManager.STREAM_MUSIC,
              sampleRate, channelConfig, encoding, bufferSize, android.media.AudioTrack.MODE_STATIC,
              sessionId);
        }
      }
    }

    if (Util.SDK_INT >= 19) {
      audioTrackUtil = new AudioTrackUtilV19(audioTrack);
    } else {
      audioTrackUtil = new AudioTrackUtil(audioTrack);
{'fraction_non_alphanumeric': 0.0802396664102668, 'fraction_numerical': 0.006963280838832436, 'mean_word_length': 3.5888909530001856, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1090504', 'n_tokens_mistral': 6886, 'n_tokens_neox': 6577, 'n_words': 2414}