<commit_before>/*
 * Copyright 2017 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.
 */

#include <logging_macros.h>
#include <climits>
#include <assert.h>
#include <audio_common.h>
#include "echo_audio_engine.h"


/**
 * Every time the playback stream requires data this method will be called.
 *
 * @param stream the audio stream which is requesting data, this is the playStream_ object
 * @param userData the context in which the function is being called, in this case it will be the
 * EchoAudioEngine instance
 * @param audioData an empty buffer into which we can write our audio data
 * @param numFrames the number of audio frames which are required
 * @return Either AAUDIO_CALLBACK_RESULT_CONTINUE if the stream should continue requesting data
 * or AAUDIO_CALLBACK_RESULT_STOP if the stream should stop.
 *
 * @see EchoAudioEngine#dataCallback
 */
aaudio_data_callback_result_t dataCallback(AAudioStream *stream, void *userData,
                                           void *audioData, int32_t numFrames) {
  assert(userData && audioData);
  EchoAudioEngine *audioEngine = reinterpret_cast<EchoAudioEngine *>(userData);
  return audioEngine->dataCallback(stream, audioData, numFrames);
}

/**
 * If there is an error with a stream this function will be called. A common example of an error
 * is when an audio device (such as headphones) is disconnected. In this case you should not
 * restart the stream within the callback, instead use a separate thread to perform the stream
 * recreation and restart.
 *
 * @param stream the stream with the error
 * @param userData the context in which the function is being called, in this case it will be the
 * EchoAudioEngine instance
 * @param error the error which occured, a human readable string can be obtained using
 * AAudio_convertResultToText(error);
 *
 * @see EchoAudioEngine#errorCallback
 */
void errorCallback(AAudioStream *stream,
                   void *userData,
                   aaudio_result_t error) {
  assert(userData);
  EchoAudioEngine *audioEngine = reinterpret_cast<EchoAudioEngine *>(userData);
  audioEngine->errorCallback(stream, error);
}

EchoAudioEngine::~EchoAudioEngine() {

  closeStream(recordingStream_);
  closeStream(playStream_);
}

void EchoAudioEngine::setRecordingDeviceId(int32_t deviceId) {

  recordingDeviceId_ = deviceId;
}

void EchoAudioEngine::setPlaybackDeviceId(int32_t deviceId) {

  playbackDeviceId_ = deviceId;
}

void EchoAudioEngine::setEchoOn(bool isEchoOn) {

  if (isEchoOn != isEchoOn_) {
    isEchoOn_ = isEchoOn;

    if (isEchoOn) {
      startStreams();
    } else {
      stopStreams();
    }
  }
}

void EchoAudioEngine::startStreams() {

  // Note: The order of stream creation is important. We create the playback stream first,
  // then use properties from the playback stream (e.g. sample rate) to create the
  // recording stream. By matching the properties we should get the lowest latency path
  createPlaybackStream();
  createRecordingStream();

  // Now start the recording stream so that we can read from it during the playback dataCallback
  if (recordingStream_ != nullptr && playStream_ != nullptr) {
    startStream(recordingStream_);
    startStream(playStream_);
  } else {
    LOGE("Failed to create recording and/or playback stream");
  }
}

/**
 * Stops and closes the playback and recording streams
 */
void EchoAudioEngine::stopStreams() {

  if (recordingStream_ != nullptr) {
    stopStream(recordingStream_);
    closeStream(recordingStream_);
    recordingStream_ = nullptr;
  }

  if (playStream_ != nullptr) {
    stopStream(playStream_);
    closeStream(playStream_);
    playStream_ = nullptr;
  }
}

/**
 * Creates a stream builder which can be used to construct streams
 * @return a new stream builder object
 */
AAudioStreamBuilder *EchoAudioEngine::createStreamBuilder() {

  AAudioStreamBuilder *builder = nullptr;
  aaudio_result_t result = AAudio_createStreamBuilder(&builder);
  if (result != AAUDIO_OK && !builder) {
    LOGE("Error creating stream builder: %s", AAudio_convertResultToText(result));
  }
  return builder;
}

/**
 * Creates an audio stream for recording. The audio device used will depend on recordingDeviceId_.
 * If the value is set to AAUDIO_UNSPECIFIED then the default recording device will be used.
 */
void EchoAudioEngine::createRecordingStream() {<commit_msg>Reversing the stream closing sequence when tearing down the echo engine:   play stream depends recording stream, so close play stream first<commit_after>/*
 * Copyright 2017 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.
 */

#include <logging_macros.h>
#include <climits>
#include <assert.h>
#include <audio_common.h>
#include "echo_audio_engine.h"


/**
 * Every time the playback stream requires data this method will be called.
 *
 * @param stream the audio stream which is requesting data, this is the playStream_ object
 * @param userData the context in which the function is being called, in this case it will be the
 * EchoAudioEngine instance
 * @param audioData an empty buffer into which we can write our audio data
 * @param numFrames the number of audio frames which are required
 * @return Either AAUDIO_CALLBACK_RESULT_CONTINUE if the stream should continue requesting data
 * or AAUDIO_CALLBACK_RESULT_STOP if the stream should stop.
 *
 * @see EchoAudioEngine#dataCallback
 */
aaudio_data_callback_result_t dataCallback(AAudioStream *stream, void *userData,
                                           void *audioData, int32_t numFrames) {
  assert(userData && audioData);
  EchoAudioEngine *audioEngine = reinterpret_cast<EchoAudioEngine *>(userData);
  return audioEngine->dataCallback(stream, audioData, numFrames);
}

/**
 * If there is an error with a stream this function will be called. A common example of an error
 * is when an audio device (such as headphones) is disconnected. In this case you should not
 * restart the stream within the callback, instead use a separate thread to perform the stream
 * recreation and restart.
 *
 * @param stream the stream with the error
 * @param userData the context in which the function is being called, in this case it will be the
 * EchoAudioEngine instance
 * @param error the error which occured, a human readable string can be obtained using
 * AAudio_convertResultToText(error);
 *
 * @see EchoAudioEngine#errorCallback
 */
void errorCallback(AAudioStream *stream,
                   void *userData,
                   aaudio_result_t error) {
  assert(userData);
  EchoAudioEngine *audioEngine = reinterpret_cast<EchoAudioEngine *>(userData);
  audioEngine->errorCallback(stream, error);
}

EchoAudioEngine::~EchoAudioEngine() {
  stopStream(playStream_);
  stopStream(recordingStream_);
  closeStream(playStream_);
  closeStream(recordingStream_);
}

void EchoAudioEngine::setRecordingDeviceId(int32_t deviceId) {

  recordingDeviceId_ = deviceId;
}

void EchoAudioEngine::setPlaybackDeviceId(int32_t deviceId) {

  playbackDeviceId_ = deviceId;
}

void EchoAudioEngine::setEchoOn(bool isEchoOn) {

  if (isEchoOn != isEchoOn_) {
    isEchoOn_ = isEchoOn;

    if (isEchoOn) {
      startStreams();
    } else {
      stopStreams();
    }
  }
}

void EchoAudioEngine::startStreams() {

  // Note: The order of stream creation is important. We create the playback stream first,
  // then use properties from the playback stream (e.g. sample rate) to create the
  // recording stream. By matching the properties we should get the lowest latency path
  createPlaybackStream();
  createRecordingStream();

  // Now start the recording stream so that we can read from it during the playback dataCallback
  if (recordingStream_ != nullptr && playStream_ != nullptr) {
    startStream(recordingStream_);
    startStream(playStream_);
  } else {
    LOGE("Failed to create recording and/or playback stream");
  }
}

/**
 * Stops and closes the playback and recording streams
 */
void EchoAudioEngine::stopStreams() {

  if (playStream_ != nullptr) {
    stopStream(playStream_);
    closeStream(playStream_);
    playStream_ = nullptr;
  }

  if (recordingStream_ != nullptr) {
    stopStream(recordingStream_);
    closeStream(recordingStream_);
    recordingStream_ = nullptr;
  }
}

/**
 * Creates a stream builder which can be used to construct streams
 * @return a new stream builder object
 */
AAudioStreamBuilder *EchoAudioEngine::createStreamBuilder() {

  AAudioStreamBuilder *builder = nullptr;
  aaudio_result_t result = AAudio_createStreamBuilder(&builder);
  if (result != AAUDIO_OK && !builder) {
    LOGE("Error creating stream builder: %s", AAudio_convertResultToText(result));
  }
  return builder;
}

/**
 * Creates an audio stream for recording. The audio device used will depend on recordingDeviceId_.
 * If the value is set to AAUDIO_UNSPECIFIED then the default recording device will be used.
 */
void EchoAudioEngine::createRecordingStream() {
{'fraction_non_alphanumeric': 0.07764847512038524, 'fraction_numerical': 0.0028089887640449437, 'mean_word_length': 4.17601246105919, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7548783', 'n_tokens_mistral': 2775, 'n_tokens_neox': 2694, 'n_words': 1036}