unwind info for arm64. use static c++ lib for android
include oboe static libs get rid of old regalloc
This commit is contained in:
parent
5fd00e3063
commit
7561ce753a
|
@ -357,9 +357,15 @@ if(ASAN)
|
|||
endif()
|
||||
|
||||
if(ANDROID AND NOT LIBRETRO)
|
||||
find_package(oboe REQUIRED CONFIG)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_OBOE)
|
||||
find_package(oboe CONFIG)
|
||||
if(OBOE_FOUND)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE oboe::oboe)
|
||||
else()
|
||||
include("core/deps/oboe/gamesdk.cmake")
|
||||
add_gamesdk_target(PACKAGE_DIR core/deps/oboe ANDROID_API_LEVEL 28 ANDROID_NDK_VERSION 21.0.0)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE oboe OpenSLES)
|
||||
endif()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_OBOE)
|
||||
endif()
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
|
@ -638,7 +644,6 @@ target_sources(${PROJECT_NAME} PRIVATE
|
|||
core/hw/sh4/dyna/decoder_opcodes.h
|
||||
core/hw/sh4/dyna/driver.cpp
|
||||
core/hw/sh4/dyna/ngen.h
|
||||
core/hw/sh4/dyna/regalloc.h
|
||||
core/hw/sh4/dyna/shil_canonical.h
|
||||
core/hw/sh4/dyna/shil.cpp
|
||||
core/hw/sh4/dyna/shil.h
|
||||
|
|
|
@ -0,0 +1,134 @@
|
|||
include(CMakeParseArguments) # CMake 2.8 - 3.4 compatibility
|
||||
|
||||
set( _MY_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
# This function will use (and build if asked to) a static library target called 'gamesdk'.
|
||||
# The location of the library is set according to your ANDROID_NDK_REVISION
|
||||
# and ANDROID_PLATFORM, unless you explicitly set ANDROID_NDK_VERSION and/or
|
||||
# ANDROID_SDK_VERSION arguments.
|
||||
#
|
||||
# All supported arguments are:
|
||||
# PACKAGE_DIR: where the packaged version of the library is (relative to the caller working directory).
|
||||
# This parameter is mandatory.
|
||||
# ROOT_DIR: where the gamesdk directory is located (relative to the caller working directory), when building from sources.
|
||||
# This must be specified if DO_LOCAL_BUILD is set
|
||||
# LIBRARIES: the library names to build, when building from sources.
|
||||
# This must be specified if DO_LOCAL_BUILD is set.
|
||||
# DO_LOCAL_BUILD: whether to add a custom build command to build the gamesdk (ON/OFF).
|
||||
# default value: OFF
|
||||
# ANDROID_NDK_VERSION: override the version number for the NDK (major.minor.patch).
|
||||
# It's recommended to use `ndkVersion` in your build.gradle file if you want to use a specific NDK.
|
||||
# default value: derived from ANDROID_NDK_REVISION.
|
||||
# ANDROID_API_LEVEL: override the android API level.
|
||||
# It's recommended to change the SDK version in your build.gradle to use a specific SDK.
|
||||
# default value: derived from ANDROID_PLATFORM.
|
||||
# BUILD_TYPE: type of Game SDK build libraries to use. Can be "Release" or "Debug".
|
||||
# default value: Release
|
||||
function(add_gamesdk_target)
|
||||
set(options DO_LOCAL_BUILD)
|
||||
set(oneValueArgs LIBRARIES PACKAGE_DIR ROOT_DIR ANDROID_NDK_VERSION ANDROID_API_LEVEL BUILD_TYPE)
|
||||
cmake_parse_arguments(GAMESDK "${options}" "${oneValueArgs}" "" ${ARGN} )
|
||||
|
||||
# Make sanity checks to avoid hard to debug errors at compile/link time.
|
||||
if(NOT DEFINED GAMESDK_PACKAGE_DIR)
|
||||
message(FATAL_ERROR "You must specify PACKAGE_DIR when calling add_gamesdk_target. It should be the folder where gamesdk is extracted (or where packages should be built, if compiling from sources).")
|
||||
endif()
|
||||
get_filename_component(INCLUDE_FULL_PATH "${GAMESDK_PACKAGE_DIR}/include/" REALPATH)
|
||||
if (NOT GAMESDK_DO_LOCAL_BUILD AND NOT EXISTS ${INCLUDE_FULL_PATH})
|
||||
message(FATAL_ERROR "Can't find the gamesdk includes in ${INCLUDE_FULL_PATH}. Are you sure you properly set up the path to the Game SDK using GAMESDK_PACKAGE_DIR?")
|
||||
endif()
|
||||
if (GAMESDK_DO_LOCAL_BUILD AND NOT DEFINED GAMESDK_LIBRARIES)
|
||||
message(FATAL_ERROR "You specified DO_LOCAL_BUILD to build the game sdk from sources, but did not specified the libraries to build with LIBRARIES.")
|
||||
endif()
|
||||
if(GAMESDK_DO_LOCAL_BUILD AND NOT DEFINED GAMESDK_ROOT_DIR)
|
||||
message(FATAL_ERROR "You specified DO_LOCAL_BUILD to build the game sdk from sources, but did not specified the gamesdk root folder with ROOT_DIR (used to run Gradle).")
|
||||
endif()
|
||||
if(NOT DEFINED GAMESDK_BUILD_TYPE)
|
||||
set(GAMESDK_BUILD_TYPE "${CMAKE_BUILD_TYPE}")
|
||||
endif()
|
||||
|
||||
# Infer Android SDK/NDK and STL versions
|
||||
if (NOT DEFINED GAMESDK_ANDROID_NDK_VERSION)
|
||||
set(GAMESDK_ANDROID_NDK_VERSION ${ANDROID_NDK_REVISION})
|
||||
endif()
|
||||
string(REGEX REPLACE "^([^.]+).*" "\\1" GAMESDK_ANDROID_NDK_MAJOR_VERSION ${GAMESDK_ANDROID_NDK_VERSION} ) # Get NDK major version
|
||||
if (NOT DEFINED GAMESDK_ANDROID_API_LEVEL)
|
||||
string(REGEX REPLACE "^android-([^.]+)" "\\1" GAMESDK_ANDROID_API_LEVEL ${ANDROID_PLATFORM} )
|
||||
endif()
|
||||
string(REPLACE "+" "p" GAMESDK_ANDROID_STL ${ANDROID_STL}) # Game SDK build names use a sanitized STL name (c++ => cpp)
|
||||
|
||||
# Set up the "gamesdk" libraries
|
||||
set(BUILD_NAME ${ANDROID_ABI}_API${GAMESDK_ANDROID_API_LEVEL}_NDK${GAMESDK_ANDROID_NDK_MAJOR_VERSION}_${GAMESDK_ANDROID_STL}_${GAMESDK_BUILD_TYPE})
|
||||
set(GAMESDK_LIBS_DIR "${GAMESDK_PACKAGE_DIR}/libs/${BUILD_NAME}")
|
||||
|
||||
include_directories( "${GAMESDK_PACKAGE_DIR}/include" ) # Games SDK Public Includes
|
||||
|
||||
get_filename_component(SWAPPY_DEP_LIB "${GAMESDK_LIBS_DIR}/libswappy_static.a" REALPATH)
|
||||
get_filename_component(TUNINGFORK_DEP_LIB "${GAMESDK_LIBS_DIR}/libtuningfork_static.a" REALPATH)
|
||||
get_filename_component(MEMORY_ADVICE_DEP_LIB "${GAMESDK_LIBS_DIR}/libmemory_advice_static.a" REALPATH)
|
||||
get_filename_component(OBOE_DEP_LIB "${GAMESDK_LIBS_DIR}/liboboe_static.a" REALPATH)
|
||||
add_library(swappy STATIC IMPORTED GLOBAL)
|
||||
add_library(tuningfork STATIC IMPORTED GLOBAL)
|
||||
add_library(memory_advice STATIC IMPORTED GLOBAL)
|
||||
add_library(oboe STATIC IMPORTED GLOBAL)
|
||||
|
||||
if(GAMESDK_DO_LOCAL_BUILD)
|
||||
# Get the absolute path for the root dir, otherwise it can't be used as a working directory for commands.
|
||||
get_filename_component(GAMESDK_ROOT_DIR "${GAMESDK_ROOT_DIR}" REALPATH)
|
||||
|
||||
# If building from a project containing local.properties, generated by Android Studio with
|
||||
# the local Android SDK and NDK paths, copy it to gamesdk to allow it to build with the local
|
||||
# toolchain.
|
||||
if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../local.properties")
|
||||
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/../local.properties
|
||||
DESTINATION ${GAMESDK_ROOT_DIR})
|
||||
endif()
|
||||
|
||||
# Build Game SDK (Gradle will use local.properties to find the Android SDK/NDK,
|
||||
# or the environment variables if no local.properties - i.e: if compiling from command line).
|
||||
if (${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")
|
||||
file(TO_NATIVE_PATH "${GAMESDK_ROOT_DIR}/gradlew.bat" GAMESDK_GRADLE_BIN)
|
||||
else()
|
||||
file(TO_NATIVE_PATH "${GAMESDK_ROOT_DIR}/gradlew" GAMESDK_GRADLE_BIN)
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${SWAPPY_DEP_LIB} ${TUNINGFORK_DEP_LIB} ${MEMORY_ADVICE_DEP_LIB} ${OBOE_DEP_LIB}
|
||||
COMMAND
|
||||
${GAMESDK_GRADLE_BIN} buildLocal -Plibraries=${GAMESDK_LIBRARIES} -PandroidApiLevel=${GAMESDK_ANDROID_API_LEVEL} -PbuildType=${GAMESDK_BUILD_TYPE} -PpackageName=local -Pndk=${GAMESDK_ANDROID_NDK_VERSION}
|
||||
VERBATIM
|
||||
WORKING_DIRECTORY
|
||||
"${GAMESDK_ROOT_DIR}"
|
||||
)
|
||||
add_custom_target(swappy_lib DEPENDS ${SWAPPY_DEP_LIB})
|
||||
add_custom_target(tuningfork_lib DEPENDS ${TUNINGFORK_DEP_LIB})
|
||||
add_custom_target(memory_advice_lib DEPENDS ${MEMORY_ADVICE_DEP_LIB})
|
||||
add_custom_target(oboe_lib DEPENDS ${OBOE_DEP_LIB})
|
||||
add_dependencies(swappy swappy_lib)
|
||||
add_dependencies(tuningfork tuningfork_lib)
|
||||
add_dependencies(memory_advice memory_advice_lib)
|
||||
add_dependencies(oboe oboe_lib)
|
||||
else()
|
||||
# Validity check to ensure that the library files exist
|
||||
if(NOT EXISTS ${SWAPPY_DEP_LIB} AND
|
||||
NOT EXISTS ${TUNINGFORK_DEP_LIB} AND
|
||||
NOT EXISTS ${MEMORY_ADVICE_DEP_LIB} AND
|
||||
NOT EXISTS ${OBOE_DEP_LIB})
|
||||
message(FATAL_ERROR "Can't find any library in \"${GAMESDK_LIBS_DIR}\". Are you sure you are using a supported Android SDK/NDK and STL variant?")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set targets to use the libraries (see https://gitlab.kitware.com/cmake/community/wikis/doc/tutorials/Exporting-and-Importing-Targets)
|
||||
set_target_properties(swappy PROPERTIES IMPORTED_LOCATION ${SWAPPY_DEP_LIB})
|
||||
set_target_properties(tuningfork PROPERTIES IMPORTED_LOCATION ${TUNINGFORK_DEP_LIB})
|
||||
set_target_properties(memory_advice PROPERTIES IMPORTED_LOCATION ${MEMORY_ADVICE_DEP_LIB})
|
||||
set_target_properties(oboe PROPERTIES IMPORTED_LOCATION ${OBOE_DEP_LIB})
|
||||
|
||||
endfunction()
|
||||
|
||||
# Use this function in addition to add_gamesdk_target, to integrate GameSDK
|
||||
# sources to your project - allowing the IDE to provide autocompletions and
|
||||
# debugging.
|
||||
function(add_gamesdk_sources)
|
||||
add_subdirectory("${_MY_DIR}/../src")
|
||||
endfunction()
|
|
@ -0,0 +1,565 @@
|
|||
/*
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_STREAM_H_
|
||||
#define OBOE_STREAM_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <mutex>
|
||||
#include "oboe/Definitions.h"
|
||||
#include "oboe/ResultWithValue.h"
|
||||
#include "oboe/AudioStreamBuilder.h"
|
||||
#include "oboe/AudioStreamBase.h"
|
||||
|
||||
/** WARNING - UNDER CONSTRUCTION - THIS API WILL CHANGE. */
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* The default number of nanoseconds to wait for when performing state change operations on the
|
||||
* stream, such as `start` and `stop`.
|
||||
*
|
||||
* @see oboe::AudioStream::start
|
||||
*/
|
||||
constexpr int64_t kDefaultTimeoutNanos = (2000 * kNanosPerMillisecond);
|
||||
|
||||
/**
|
||||
* Base class for Oboe C++ audio stream.
|
||||
*/
|
||||
class AudioStream : public AudioStreamBase {
|
||||
friend class AudioStreamBuilder; // allow access to setWeakThis() and lockWeakThis()
|
||||
public:
|
||||
|
||||
AudioStream() {}
|
||||
|
||||
/**
|
||||
* Construct an `AudioStream` using the given `AudioStreamBuilder`
|
||||
*
|
||||
* @param builder containing all the stream's attributes
|
||||
*/
|
||||
explicit AudioStream(const AudioStreamBuilder &builder);
|
||||
|
||||
virtual ~AudioStream() = default;
|
||||
|
||||
/**
|
||||
* Open a stream based on the current settings.
|
||||
*
|
||||
* Note that we do not recommend re-opening a stream that has been closed.
|
||||
* TODO Should we prevent re-opening?
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
virtual Result open() {
|
||||
return Result::OK; // Called by subclasses. Might do more in the future.
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the stream and deallocate any resources from the open() call.
|
||||
*/
|
||||
virtual Result close();
|
||||
|
||||
/**
|
||||
* Start the stream. This will block until the stream has been started, an error occurs
|
||||
* or `timeoutNanoseconds` has been reached.
|
||||
*/
|
||||
virtual Result start(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
|
||||
|
||||
/**
|
||||
* Pause the stream. This will block until the stream has been paused, an error occurs
|
||||
* or `timeoutNanoseconds` has been reached.
|
||||
*/
|
||||
virtual Result pause(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
|
||||
|
||||
/**
|
||||
* Flush the stream. This will block until the stream has been flushed, an error occurs
|
||||
* or `timeoutNanoseconds` has been reached.
|
||||
*/
|
||||
virtual Result flush(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
|
||||
|
||||
/**
|
||||
* Stop the stream. This will block until the stream has been stopped, an error occurs
|
||||
* or `timeoutNanoseconds` has been reached.
|
||||
*/
|
||||
virtual Result stop(int64_t timeoutNanoseconds = kDefaultTimeoutNanos);
|
||||
|
||||
/* Asynchronous requests.
|
||||
* Use waitForStateChange() if you need to wait for completion.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Start the stream asynchronously. Returns immediately (does not block). Equivalent to calling
|
||||
* `start(0)`.
|
||||
*/
|
||||
virtual Result requestStart() = 0;
|
||||
|
||||
/**
|
||||
* Pause the stream asynchronously. Returns immediately (does not block). Equivalent to calling
|
||||
* `pause(0)`.
|
||||
*/
|
||||
virtual Result requestPause() = 0;
|
||||
|
||||
/**
|
||||
* Flush the stream asynchronously. Returns immediately (does not block). Equivalent to calling
|
||||
* `flush(0)`.
|
||||
*/
|
||||
virtual Result requestFlush() = 0;
|
||||
|
||||
/**
|
||||
* Stop the stream asynchronously. Returns immediately (does not block). Equivalent to calling
|
||||
* `stop(0)`.
|
||||
*/
|
||||
virtual Result requestStop() = 0;
|
||||
|
||||
/**
|
||||
* Query the current state, eg. StreamState::Pausing
|
||||
*
|
||||
* @return state or a negative error.
|
||||
*/
|
||||
virtual StreamState getState() const = 0;
|
||||
|
||||
/**
|
||||
* Wait until the stream's current state no longer matches the input state.
|
||||
* The input state is passed to avoid race conditions caused by the state
|
||||
* changing between calls.
|
||||
*
|
||||
* Note that generally applications do not need to call this. It is considered
|
||||
* an advanced technique and is mostly used for testing.
|
||||
*
|
||||
* <pre><code>
|
||||
* int64_t timeoutNanos = 500 * kNanosPerMillisecond; // arbitrary 1/2 second
|
||||
* StreamState currentState = stream->getState();
|
||||
* StreamState nextState = StreamState::Unknown;
|
||||
* while (result == Result::OK && currentState != StreamState::Paused) {
|
||||
* result = stream->waitForStateChange(
|
||||
* currentState, &nextState, timeoutNanos);
|
||||
* currentState = nextState;
|
||||
* }
|
||||
* </code></pre>
|
||||
*
|
||||
* If the state does not change within the timeout period then it will
|
||||
* return ErrorTimeout. This is true even if timeoutNanoseconds is zero.
|
||||
*
|
||||
* @param inputState The state we want to change away from.
|
||||
* @param nextState Pointer to a variable that will be set to the new state.
|
||||
* @param timeoutNanoseconds The maximum time to wait in nanoseconds.
|
||||
* @return Result::OK or a Result::Error.
|
||||
*/
|
||||
virtual Result waitForStateChange(StreamState inputState,
|
||||
StreamState *nextState,
|
||||
int64_t timeoutNanoseconds) = 0;
|
||||
|
||||
/**
|
||||
* This can be used to adjust the latency of the buffer by changing
|
||||
* the threshold where blocking will occur.
|
||||
* By combining this with getXRunCount(), the latency can be tuned
|
||||
* at run-time for each device.
|
||||
*
|
||||
* This cannot be set higher than getBufferCapacity().
|
||||
*
|
||||
* @param requestedFrames requested number of frames that can be filled without blocking
|
||||
* @return the resulting buffer size in frames (obtained using value()) or an error (obtained
|
||||
* using error())
|
||||
*/
|
||||
virtual ResultWithValue<int32_t> setBufferSizeInFrames(int32_t /* requestedFrames */) {
|
||||
return Result::ErrorUnimplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* An XRun is an Underrun or an Overrun.
|
||||
* During playing, an underrun will occur if the stream is not written in time
|
||||
* and the system runs out of valid data.
|
||||
* During recording, an overrun will occur if the stream is not read in time
|
||||
* and there is no place to put the incoming data so it is discarded.
|
||||
*
|
||||
* An underrun or overrun can cause an audible "pop" or "glitch".
|
||||
*
|
||||
* @return a result which is either Result::OK with the xRun count as the value, or a
|
||||
* Result::Error* code
|
||||
*/
|
||||
virtual ResultWithValue<int32_t> getXRunCount() const {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if XRun counts are supported on the stream
|
||||
*/
|
||||
virtual bool isXRunCountSupported() const = 0;
|
||||
|
||||
/**
|
||||
* Query the number of frames that are read or written by the endpoint at one time.
|
||||
*
|
||||
* @return burst size
|
||||
*/
|
||||
virtual int32_t getFramesPerBurst() = 0;
|
||||
|
||||
/**
|
||||
* Get the number of bytes in each audio frame. This is calculated using the channel count
|
||||
* and the sample format. For example, a 2 channel floating point stream will have
|
||||
* 2 * 4 = 8 bytes per frame.
|
||||
*
|
||||
* @return number of bytes in each audio frame.
|
||||
*/
|
||||
int32_t getBytesPerFrame() const { return mChannelCount * getBytesPerSample(); }
|
||||
|
||||
/**
|
||||
* Get the number of bytes per sample. This is calculated using the sample format. For example,
|
||||
* a stream using 16-bit integer samples will have 2 bytes per sample.
|
||||
*
|
||||
* @return the number of bytes per sample.
|
||||
*/
|
||||
int32_t getBytesPerSample() const;
|
||||
|
||||
/**
|
||||
* The number of audio frames written into the stream.
|
||||
* This monotonic counter will never get reset.
|
||||
*
|
||||
* @return the number of frames written so far
|
||||
*/
|
||||
virtual int64_t getFramesWritten();
|
||||
|
||||
/**
|
||||
* The number of audio frames read from the stream.
|
||||
* This monotonic counter will never get reset.
|
||||
*
|
||||
* @return the number of frames read so far
|
||||
*/
|
||||
virtual int64_t getFramesRead();
|
||||
|
||||
/**
|
||||
* Calculate the latency of a stream based on getTimestamp().
|
||||
*
|
||||
* Output latency is the time it takes for a given frame to travel from the
|
||||
* app to some type of digital-to-analog converter. If the DAC is external, for example
|
||||
* in a USB interface or a TV connected by HDMI, then there may be additional latency
|
||||
* that the Android device is unaware of.
|
||||
*
|
||||
* Input latency is the time it takes to a given frame to travel from an analog-to-digital
|
||||
* converter (ADC) to the app.
|
||||
*
|
||||
* Note that the latency of an OUTPUT stream will increase abruptly when you write data to it
|
||||
* and then decrease slowly over time as the data is consumed.
|
||||
*
|
||||
* The latency of an INPUT stream will decrease abruptly when you read data from it
|
||||
* and then increase slowly over time as more data arrives.
|
||||
*
|
||||
* The latency of an OUTPUT stream is generally higher than the INPUT latency
|
||||
* because an app generally tries to keep the OUTPUT buffer full and the INPUT buffer empty.
|
||||
*
|
||||
* @return a ResultWithValue which has a result of Result::OK and a value containing the latency
|
||||
* in milliseconds, or a result of Result::Error*.
|
||||
*/
|
||||
virtual ResultWithValue<double> calculateLatencyMillis() {
|
||||
return ResultWithValue<double>(Result::ErrorUnimplemented);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the estimated time that the frame at `framePosition` entered or left the audio processing
|
||||
* pipeline.
|
||||
*
|
||||
* This can be used to coordinate events and interactions with the external environment, and to
|
||||
* estimate the latency of an audio stream. An example of usage can be found in the hello-oboe
|
||||
* sample (search for "calculateCurrentOutputLatencyMillis").
|
||||
*
|
||||
* The time is based on the implementation's best effort, using whatever knowledge is available
|
||||
* to the system, but cannot account for any delay unknown to the implementation.
|
||||
*
|
||||
* @deprecated since 1.0, use AudioStream::getTimestamp(clockid_t clockId) instead, which
|
||||
* returns ResultWithValue
|
||||
* @param clockId the type of clock to use e.g. CLOCK_MONOTONIC
|
||||
* @param framePosition the frame number to query
|
||||
* @param timeNanoseconds an output parameter which will contain the presentation timestamp
|
||||
*/
|
||||
virtual Result getTimestamp(clockid_t /* clockId */,
|
||||
int64_t* /* framePosition */,
|
||||
int64_t* /* timeNanoseconds */) {
|
||||
return Result::ErrorUnimplemented;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the estimated time that the frame at `framePosition` entered or left the audio processing
|
||||
* pipeline.
|
||||
*
|
||||
* This can be used to coordinate events and interactions with the external environment, and to
|
||||
* estimate the latency of an audio stream. An example of usage can be found in the hello-oboe
|
||||
* sample (search for "calculateCurrentOutputLatencyMillis").
|
||||
*
|
||||
* The time is based on the implementation's best effort, using whatever knowledge is available
|
||||
* to the system, but cannot account for any delay unknown to the implementation.
|
||||
*
|
||||
* @param clockId the type of clock to use e.g. CLOCK_MONOTONIC
|
||||
* @return a FrameTimestamp containing the position and time at which a particular audio frame
|
||||
* entered or left the audio processing pipeline, or an error if the operation failed.
|
||||
*/
|
||||
virtual ResultWithValue<FrameTimestamp> getTimestamp(clockid_t /* clockId */);
|
||||
|
||||
// ============== I/O ===========================
|
||||
/**
|
||||
* Write data from the supplied buffer into the stream. This method will block until the write
|
||||
* is complete or it runs out of time.
|
||||
*
|
||||
* If `timeoutNanoseconds` is zero then this call will not wait.
|
||||
*
|
||||
* @param buffer The address of the first sample.
|
||||
* @param numFrames Number of frames to write. Only complete frames will be written.
|
||||
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
|
||||
* @return a ResultWithValue which has a result of Result::OK and a value containing the number
|
||||
* of frames actually written, or result of Result::Error*.
|
||||
*/
|
||||
virtual ResultWithValue<int32_t> write(const void* /* buffer */,
|
||||
int32_t /* numFrames */,
|
||||
int64_t /* timeoutNanoseconds */ ) {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data into the supplied buffer from the stream. This method will block until the read
|
||||
* is complete or it runs out of time.
|
||||
*
|
||||
* If `timeoutNanoseconds` is zero then this call will not wait.
|
||||
*
|
||||
* @param buffer The address of the first sample.
|
||||
* @param numFrames Number of frames to read. Only complete frames will be read.
|
||||
* @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
|
||||
* @return a ResultWithValue which has a result of Result::OK and a value containing the number
|
||||
* of frames actually read, or result of Result::Error*.
|
||||
*/
|
||||
virtual ResultWithValue<int32_t> read(void* /* buffer */,
|
||||
int32_t /* numFrames */,
|
||||
int64_t /* timeoutNanoseconds */) {
|
||||
return ResultWithValue<int32_t>(Result::ErrorUnimplemented);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying audio API which the stream uses.
|
||||
*
|
||||
* @return the API that this stream uses.
|
||||
*/
|
||||
virtual AudioApi getAudioApi() const = 0;
|
||||
|
||||
/**
|
||||
* Returns true if the underlying audio API is AAudio.
|
||||
*
|
||||
* @return true if this stream is implemented using the AAudio API.
|
||||
*/
|
||||
bool usesAAudio() const {
|
||||
return getAudioApi() == AudioApi::AAudio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only for debugging. Do not use in production.
|
||||
* If you need to call this method something is wrong.
|
||||
* If you think you need it for production then please let us know
|
||||
* so we can modify Oboe so that you don't need this.
|
||||
*
|
||||
* @return nullptr or a pointer to a stream from the system API
|
||||
*/
|
||||
virtual void *getUnderlyingStream() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Launch a thread that will stop the stream.
|
||||
*/
|
||||
void launchStopThread();
|
||||
|
||||
/**
|
||||
* Update mFramesWritten.
|
||||
* For internal use only.
|
||||
*/
|
||||
virtual void updateFramesWritten() = 0;
|
||||
|
||||
/**
|
||||
* Update mFramesRead.
|
||||
* For internal use only.
|
||||
*/
|
||||
virtual void updateFramesRead() = 0;
|
||||
|
||||
/*
|
||||
* Swap old callback for new callback.
|
||||
* This not atomic.
|
||||
* This should only be used internally.
|
||||
* @param dataCallback
|
||||
* @return previous dataCallback
|
||||
*/
|
||||
AudioStreamDataCallback *swapDataCallback(AudioStreamDataCallback *dataCallback) {
|
||||
AudioStreamDataCallback *previousCallback = mDataCallback;
|
||||
mDataCallback = dataCallback;
|
||||
return previousCallback;
|
||||
}
|
||||
|
||||
/*
|
||||
* Swap old callback for new callback.
|
||||
* This not atomic.
|
||||
* This should only be used internally.
|
||||
* @param errorCallback
|
||||
* @return previous errorCallback
|
||||
*/
|
||||
AudioStreamErrorCallback *swapErrorCallback(AudioStreamErrorCallback *errorCallback) {
|
||||
AudioStreamErrorCallback *previousCallback = mErrorCallback;
|
||||
mErrorCallback = errorCallback;
|
||||
return previousCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return number of frames of data currently in the buffer
|
||||
*/
|
||||
ResultWithValue<int32_t> getAvailableFrames();
|
||||
|
||||
/**
|
||||
* Wait until the stream has a minimum amount of data available in its buffer.
|
||||
* This can be used with an EXCLUSIVE MMAP input stream to avoid reading data too close to
|
||||
* the DSP write position, which may cause glitches.
|
||||
*
|
||||
* @param numFrames minimum frames available
|
||||
* @param timeoutNanoseconds
|
||||
* @return number of frames available, ErrorTimeout
|
||||
*/
|
||||
ResultWithValue<int32_t> waitForAvailableFrames(int32_t numFrames,
|
||||
int64_t timeoutNanoseconds);
|
||||
|
||||
/**
|
||||
* @return last result passed from an error callback
|
||||
*/
|
||||
virtual oboe::Result getLastErrorCallbackResult() const {
|
||||
return mErrorCallbackResult;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* This is used to detect more than one error callback from a stream.
|
||||
* These were bugs in some versions of Android that caused multiple error callbacks.
|
||||
* Internal bug b/63087953
|
||||
*
|
||||
* Calling this sets an atomic<bool> true and returns the previous value.
|
||||
*
|
||||
* @return false on first call, true on subsequent calls
|
||||
*/
|
||||
bool wasErrorCallbackCalled() {
|
||||
return mErrorCallbackCalled.exchange(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for a transition from one state to another.
|
||||
* @return OK if the endingState was observed, or ErrorUnexpectedState
|
||||
* if any state that was not the startingState or endingState was observed
|
||||
* or ErrorTimeout.
|
||||
*/
|
||||
virtual Result waitForStateTransition(StreamState startingState,
|
||||
StreamState endingState,
|
||||
int64_t timeoutNanoseconds);
|
||||
|
||||
/**
|
||||
* Override this to provide a default for when the application did not specify a callback.
|
||||
*
|
||||
* @param audioData
|
||||
* @param numFrames
|
||||
* @return result
|
||||
*/
|
||||
virtual DataCallbackResult onDefaultCallback(void* /* audioData */, int /* numFrames */) {
|
||||
return DataCallbackResult::Stop;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override this to provide your own behaviour for the audio callback
|
||||
*
|
||||
* @param audioData container array which audio frames will be written into or read from
|
||||
* @param numFrames number of frames which were read/written
|
||||
* @return the result of the callback: stop or continue
|
||||
*
|
||||
*/
|
||||
DataCallbackResult fireDataCallback(void *audioData, int numFrames);
|
||||
|
||||
/**
|
||||
* @return true if callbacks may be called
|
||||
*/
|
||||
bool isDataCallbackEnabled() {
|
||||
return mDataCallbackEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* This can be set false internally to prevent callbacks
|
||||
* after DataCallbackResult::Stop has been returned.
|
||||
*/
|
||||
void setDataCallbackEnabled(bool enabled) {
|
||||
mDataCallbackEnabled = enabled;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a weak_ptr to this stream from the shared_ptr so that we can
|
||||
* later use a shared_ptr in the error callback.
|
||||
*/
|
||||
void setWeakThis(std::shared_ptr<oboe::AudioStream> &sharedStream) {
|
||||
mWeakThis = sharedStream;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a shared_ptr that will prevent this stream from being deleted.
|
||||
*/
|
||||
std::shared_ptr<oboe::AudioStream> lockWeakThis() {
|
||||
return mWeakThis.lock();
|
||||
}
|
||||
|
||||
std::weak_ptr<AudioStream> mWeakThis; // weak pointer to this object
|
||||
|
||||
/**
|
||||
* Number of frames which have been written into the stream
|
||||
*
|
||||
* This is signed integer to match the counters in AAudio.
|
||||
* At audio rates, the counter will overflow in about six million years.
|
||||
*/
|
||||
std::atomic<int64_t> mFramesWritten{};
|
||||
|
||||
/**
|
||||
* Number of frames which have been read from the stream.
|
||||
*
|
||||
* This is signed integer to match the counters in AAudio.
|
||||
* At audio rates, the counter will overflow in about six million years.
|
||||
*/
|
||||
std::atomic<int64_t> mFramesRead{};
|
||||
|
||||
std::mutex mLock; // for synchronizing start/stop/close
|
||||
|
||||
oboe::Result mErrorCallbackResult = oboe::Result::OK;
|
||||
|
||||
private:
|
||||
|
||||
// Log the scheduler if it changes.
|
||||
void checkScheduler();
|
||||
int mPreviousScheduler = -1;
|
||||
|
||||
std::atomic<bool> mDataCallbackEnabled{false};
|
||||
std::atomic<bool> mErrorCallbackCalled{false};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This struct is a stateless functor which closes an AudioStream prior to its deletion.
|
||||
* This means it can be used to safely delete a smart pointer referring to an open stream.
|
||||
*/
|
||||
struct StreamDeleterFunctor {
|
||||
void operator()(AudioStream *audioStream) {
|
||||
if (audioStream) {
|
||||
audioStream->close();
|
||||
}
|
||||
delete audioStream;
|
||||
}
|
||||
};
|
||||
} // namespace oboe
|
||||
|
||||
#endif /* OBOE_STREAM_H_ */
|
|
@ -0,0 +1,260 @@
|
|||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_STREAM_BASE_H_
|
||||
#define OBOE_STREAM_BASE_H_
|
||||
|
||||
#include <memory>
|
||||
#include "oboe/AudioStreamCallback.h"
|
||||
#include "oboe/Definitions.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* Base class containing parameters for audio streams and builders.
|
||||
**/
|
||||
class AudioStreamBase {
|
||||
|
||||
public:
|
||||
|
||||
AudioStreamBase() {}
|
||||
|
||||
virtual ~AudioStreamBase() = default;
|
||||
|
||||
// This class only contains primitives so we can use default constructor and copy methods.
|
||||
|
||||
/**
|
||||
* Default copy constructor
|
||||
*/
|
||||
AudioStreamBase(const AudioStreamBase&) = default;
|
||||
|
||||
/**
|
||||
* Default assignment operator
|
||||
*/
|
||||
AudioStreamBase& operator=(const AudioStreamBase&) = default;
|
||||
|
||||
/**
|
||||
* @return number of channels, for example 2 for stereo, or kUnspecified
|
||||
*/
|
||||
int32_t getChannelCount() const { return mChannelCount; }
|
||||
|
||||
/**
|
||||
* @return Direction::Input or Direction::Output
|
||||
*/
|
||||
Direction getDirection() const { return mDirection; }
|
||||
|
||||
/**
|
||||
* @return sample rate for the stream or kUnspecified
|
||||
*/
|
||||
int32_t getSampleRate() const { return mSampleRate; }
|
||||
|
||||
/**
|
||||
* @deprecated use `getFramesPerDataCallback` instead.
|
||||
*/
|
||||
int32_t getFramesPerCallback() const { return getFramesPerDataCallback(); }
|
||||
|
||||
/**
|
||||
* @return the number of frames in each data callback or kUnspecified.
|
||||
*/
|
||||
int32_t getFramesPerDataCallback() const { return mFramesPerCallback; }
|
||||
|
||||
/**
|
||||
* @return the audio sample format (e.g. Float or I16)
|
||||
*/
|
||||
AudioFormat getFormat() const { return mFormat; }
|
||||
|
||||
/**
|
||||
* Query the maximum number of frames that can be filled without blocking.
|
||||
* If the stream has been closed the last known value will be returned.
|
||||
*
|
||||
* @return buffer size
|
||||
*/
|
||||
virtual int32_t getBufferSizeInFrames() { return mBufferSizeInFrames; }
|
||||
|
||||
/**
|
||||
* @return capacityInFrames or kUnspecified
|
||||
*/
|
||||
virtual int32_t getBufferCapacityInFrames() const { return mBufferCapacityInFrames; }
|
||||
|
||||
/**
|
||||
* @return the sharing mode of the stream.
|
||||
*/
|
||||
SharingMode getSharingMode() const { return mSharingMode; }
|
||||
|
||||
/**
|
||||
* @return the performance mode of the stream.
|
||||
*/
|
||||
PerformanceMode getPerformanceMode() const { return mPerformanceMode; }
|
||||
|
||||
/**
|
||||
* @return the device ID of the stream.
|
||||
*/
|
||||
int32_t getDeviceId() const { return mDeviceId; }
|
||||
|
||||
/**
|
||||
* For internal use only.
|
||||
* @return the data callback object for this stream, if set.
|
||||
*/
|
||||
AudioStreamDataCallback *getDataCallback() const {
|
||||
return mDataCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal use only.
|
||||
* @return the error callback object for this stream, if set.
|
||||
*/
|
||||
AudioStreamErrorCallback *getErrorCallback() const {
|
||||
return mErrorCallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a data callback was set for this stream
|
||||
*/
|
||||
bool isDataCallbackSpecified() const {
|
||||
return mDataCallback != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that if the app does not set an error callback then a
|
||||
* default one may be provided.
|
||||
* @return true if an error callback was set for this stream
|
||||
*/
|
||||
bool isErrorCallbackSpecified() const {
|
||||
return mErrorCallback != nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the usage for this stream.
|
||||
*/
|
||||
Usage getUsage() const { return mUsage; }
|
||||
|
||||
/**
|
||||
* @return the stream's content type.
|
||||
*/
|
||||
ContentType getContentType() const { return mContentType; }
|
||||
|
||||
/**
|
||||
* @return the stream's input preset.
|
||||
*/
|
||||
InputPreset getInputPreset() const { return mInputPreset; }
|
||||
|
||||
/**
|
||||
* @return the stream's session ID allocation strategy (None or Allocate).
|
||||
*/
|
||||
SessionId getSessionId() const { return mSessionId; }
|
||||
|
||||
/**
|
||||
* @return true if Oboe can convert channel counts to achieve optimal results.
|
||||
*/
|
||||
bool isChannelConversionAllowed() const {
|
||||
return mChannelConversionAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if Oboe can convert data formats to achieve optimal results.
|
||||
*/
|
||||
bool isFormatConversionAllowed() const {
|
||||
return mFormatConversionAllowed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether and how Oboe can convert sample rates to achieve optimal results.
|
||||
*/
|
||||
SampleRateConversionQuality getSampleRateConversionQuality() const {
|
||||
return mSampleRateConversionQuality;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The callback which will be fired when new data is ready to be read/written. **/
|
||||
AudioStreamDataCallback *mDataCallback = nullptr;
|
||||
|
||||
/** The callback which will be fired when an error or a disconnect occurs. **/
|
||||
AudioStreamErrorCallback *mErrorCallback = nullptr;
|
||||
|
||||
/** Number of audio frames which will be requested in each callback */
|
||||
int32_t mFramesPerCallback = kUnspecified;
|
||||
/** Stream channel count */
|
||||
int32_t mChannelCount = kUnspecified;
|
||||
/** Stream sample rate */
|
||||
int32_t mSampleRate = kUnspecified;
|
||||
/** Stream audio device ID */
|
||||
int32_t mDeviceId = kUnspecified;
|
||||
/** Stream buffer capacity specified as a number of audio frames */
|
||||
int32_t mBufferCapacityInFrames = kUnspecified;
|
||||
/** Stream buffer size specified as a number of audio frames */
|
||||
int32_t mBufferSizeInFrames = kUnspecified;
|
||||
/**
|
||||
* Number of frames which will be copied to/from the audio device in a single read/write
|
||||
* operation
|
||||
*/
|
||||
int32_t mFramesPerBurst = kUnspecified;
|
||||
|
||||
/** Stream sharing mode */
|
||||
SharingMode mSharingMode = SharingMode::Shared;
|
||||
/** Format of audio frames */
|
||||
AudioFormat mFormat = AudioFormat::Unspecified;
|
||||
/** Stream direction */
|
||||
Direction mDirection = Direction::Output;
|
||||
/** Stream performance mode */
|
||||
PerformanceMode mPerformanceMode = PerformanceMode::None;
|
||||
|
||||
/** Stream usage. Only active on Android 28+ */
|
||||
Usage mUsage = Usage::Media;
|
||||
/** Stream content type. Only active on Android 28+ */
|
||||
ContentType mContentType = ContentType::Music;
|
||||
/** Stream input preset. Only active on Android 28+
|
||||
* TODO InputPreset::Unspecified should be considered as a possible default alternative.
|
||||
*/
|
||||
InputPreset mInputPreset = InputPreset::VoiceRecognition;
|
||||
/** Stream session ID allocation strategy. Only active on Android 28+ */
|
||||
SessionId mSessionId = SessionId::None;
|
||||
|
||||
// Control whether Oboe can convert channel counts to achieve optimal results.
|
||||
bool mChannelConversionAllowed = false;
|
||||
// Control whether Oboe can convert data formats to achieve optimal results.
|
||||
bool mFormatConversionAllowed = false;
|
||||
// Control whether and how Oboe can convert sample rates to achieve optimal results.
|
||||
SampleRateConversionQuality mSampleRateConversionQuality = SampleRateConversionQuality::None;
|
||||
|
||||
/** Validate stream parameters that might not be checked in lower layers */
|
||||
virtual Result isValidConfig() {
|
||||
switch (mFormat) {
|
||||
case AudioFormat::Unspecified:
|
||||
case AudioFormat::I16:
|
||||
case AudioFormat::Float:
|
||||
break;
|
||||
|
||||
default:
|
||||
return Result::ErrorInvalidFormat;
|
||||
}
|
||||
|
||||
switch (mSampleRateConversionQuality) {
|
||||
case SampleRateConversionQuality::None:
|
||||
case SampleRateConversionQuality::Fastest:
|
||||
case SampleRateConversionQuality::Low:
|
||||
case SampleRateConversionQuality::Medium:
|
||||
case SampleRateConversionQuality::High:
|
||||
case SampleRateConversionQuality::Best:
|
||||
return Result::OK;
|
||||
default:
|
||||
return Result::ErrorIllegalArgument;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif /* OBOE_STREAM_BASE_H_ */
|
|
@ -0,0 +1,485 @@
|
|||
/*
|
||||
* Copyright 2015 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_STREAM_BUILDER_H_
|
||||
#define OBOE_STREAM_BUILDER_H_
|
||||
|
||||
#include "oboe/Definitions.h"
|
||||
#include "oboe/AudioStreamBase.h"
|
||||
#include "ResultWithValue.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
// This depends on AudioStream, so we use forward declaration, it will close and delete the stream
|
||||
struct StreamDeleterFunctor;
|
||||
using ManagedStream = std::unique_ptr<AudioStream, StreamDeleterFunctor>;
|
||||
|
||||
/**
|
||||
* Factory class for an audio Stream.
|
||||
*/
|
||||
class AudioStreamBuilder : public AudioStreamBase {
|
||||
public:
|
||||
|
||||
AudioStreamBuilder() : AudioStreamBase() {}
|
||||
|
||||
AudioStreamBuilder(const AudioStreamBase &audioStreamBase): AudioStreamBase(audioStreamBase) {}
|
||||
|
||||
/**
|
||||
* Request a specific number of channels.
|
||||
*
|
||||
* Default is kUnspecified. If the value is unspecified then
|
||||
* the application should query for the actual value after the stream is opened.
|
||||
*/
|
||||
AudioStreamBuilder *setChannelCount(int channelCount) {
|
||||
mChannelCount = channelCount;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request the direction for a stream. The default is Direction::Output.
|
||||
*
|
||||
* @param direction Direction::Output or Direction::Input
|
||||
*/
|
||||
AudioStreamBuilder *setDirection(Direction direction) {
|
||||
mDirection = direction;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a specific sample rate in Hz.
|
||||
*
|
||||
* Default is kUnspecified. If the value is unspecified then
|
||||
* the application should query for the actual value after the stream is opened.
|
||||
*
|
||||
* Technically, this should be called the "frame rate" or "frames per second",
|
||||
* because it refers to the number of complete frames transferred per second.
|
||||
* But it is traditionally called "sample rate". Se we use that term.
|
||||
*
|
||||
*/
|
||||
AudioStreamBuilder *setSampleRate(int32_t sampleRate) {
|
||||
mSampleRate = sampleRate;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use `setFramesPerDataCallback` instead.
|
||||
*/
|
||||
AudioStreamBuilder *setFramesPerCallback(int framesPerCallback) {
|
||||
return setFramesPerDataCallback(framesPerCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a specific number of frames for the data callback.
|
||||
*
|
||||
* Default is kUnspecified. If the value is unspecified then
|
||||
* the actual number may vary from callback to callback.
|
||||
*
|
||||
* If an application can handle a varying number of frames then we recommend
|
||||
* leaving this unspecified. This allow the underlying API to optimize
|
||||
* the callbacks. But if your application is, for example, doing FFTs or other block
|
||||
* oriented operations, then call this function to get the sizes you need.
|
||||
*
|
||||
* @param framesPerCallback
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setFramesPerDataCallback(int framesPerCallback) {
|
||||
mFramesPerCallback = framesPerCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a sample data format, for example Format::Float.
|
||||
*
|
||||
* Default is Format::Unspecified. If the value is unspecified then
|
||||
* the application should query for the actual value after the stream is opened.
|
||||
*/
|
||||
AudioStreamBuilder *setFormat(AudioFormat format) {
|
||||
mFormat = format;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the requested buffer capacity in frames.
|
||||
* BufferCapacityInFrames is the maximum possible BufferSizeInFrames.
|
||||
*
|
||||
* The final stream capacity may differ. For AAudio it should be at least this big.
|
||||
* For OpenSL ES, it could be smaller.
|
||||
*
|
||||
* Default is kUnspecified.
|
||||
*
|
||||
* @param bufferCapacityInFrames the desired buffer capacity in frames or kUnspecified
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setBufferCapacityInFrames(int32_t bufferCapacityInFrames) {
|
||||
mBufferCapacityInFrames = bufferCapacityInFrames;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the audio API which will be requested when opening the stream. No guarantees that this is
|
||||
* the API which will actually be used. Query the stream itself to find out the API which is
|
||||
* being used.
|
||||
*
|
||||
* If you do not specify the API, then AAudio will be used if isAAudioRecommended()
|
||||
* returns true. Otherwise OpenSL ES will be used.
|
||||
*
|
||||
* @return the requested audio API
|
||||
*/
|
||||
AudioApi getAudioApi() const { return mAudioApi; }
|
||||
|
||||
/**
|
||||
* If you leave this unspecified then Oboe will choose the best API
|
||||
* for the device and SDK version at runtime.
|
||||
*
|
||||
* This should almost always be left unspecified, except for debugging purposes.
|
||||
* Specifying AAudio will force Oboe to use AAudio on 8.0, which is extremely risky.
|
||||
* Specifying OpenSLES should mainly be used to test legacy performance/functionality.
|
||||
*
|
||||
* If the caller requests AAudio and it is supported then AAudio will be used.
|
||||
*
|
||||
* @param audioApi Must be AudioApi::Unspecified, AudioApi::OpenSLES or AudioApi::AAudio.
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setAudioApi(AudioApi audioApi) {
|
||||
mAudioApi = audioApi;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the AAudio API supported on this device?
|
||||
*
|
||||
* AAudio was introduced in the Oreo 8.0 release.
|
||||
*
|
||||
* @return true if supported
|
||||
*/
|
||||
static bool isAAudioSupported();
|
||||
|
||||
/**
|
||||
* Is the AAudio API recommended this device?
|
||||
*
|
||||
* AAudio may be supported but not recommended because of version specific issues.
|
||||
* AAudio is not recommended for Android 8.0 or earlier versions.
|
||||
*
|
||||
* @return true if recommended
|
||||
*/
|
||||
static bool isAAudioRecommended();
|
||||
|
||||
/**
|
||||
* Request a mode for sharing the device.
|
||||
* The requested sharing mode may not be available.
|
||||
* So the application should query for the actual mode after the stream is opened.
|
||||
*
|
||||
* @param sharingMode SharingMode::Shared or SharingMode::Exclusive
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setSharingMode(SharingMode sharingMode) {
|
||||
mSharingMode = sharingMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a performance level for the stream.
|
||||
* This will determine the latency, the power consumption, and the level of
|
||||
* protection from glitches.
|
||||
*
|
||||
* @param performanceMode for example, PerformanceMode::LowLatency
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setPerformanceMode(PerformanceMode performanceMode) {
|
||||
mPerformanceMode = performanceMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the intended use case for an output stream.
|
||||
*
|
||||
* The system will use this information to optimize the behavior of the stream.
|
||||
* This could, for example, affect how volume and focus is handled for the stream.
|
||||
* The usage is ignored for input streams.
|
||||
*
|
||||
* The default, if you do not call this function, is Usage::Media.
|
||||
*
|
||||
* Added in API level 28.
|
||||
*
|
||||
* @param usage the desired usage, eg. Usage::Game
|
||||
*/
|
||||
AudioStreamBuilder *setUsage(Usage usage) {
|
||||
mUsage = usage;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the type of audio data that an output stream will carry.
|
||||
*
|
||||
* The system will use this information to optimize the behavior of the stream.
|
||||
* This could, for example, affect whether a stream is paused when a notification occurs.
|
||||
* The contentType is ignored for input streams.
|
||||
*
|
||||
* The default, if you do not call this function, is ContentType::Music.
|
||||
*
|
||||
* Added in API level 28.
|
||||
*
|
||||
* @param contentType the type of audio data, eg. ContentType::Speech
|
||||
*/
|
||||
AudioStreamBuilder *setContentType(ContentType contentType) {
|
||||
mContentType = contentType;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the input (capture) preset for the stream.
|
||||
*
|
||||
* The system will use this information to optimize the behavior of the stream.
|
||||
* This could, for example, affect which microphones are used and how the
|
||||
* recorded data is processed.
|
||||
*
|
||||
* The default, if you do not call this function, is InputPreset::VoiceRecognition.
|
||||
* That is because VoiceRecognition is the preset with the lowest latency
|
||||
* on many platforms.
|
||||
*
|
||||
* Added in API level 28.
|
||||
*
|
||||
* @param inputPreset the desired configuration for recording
|
||||
*/
|
||||
AudioStreamBuilder *setInputPreset(InputPreset inputPreset) {
|
||||
mInputPreset = inputPreset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Set the requested session ID.
|
||||
*
|
||||
* The session ID can be used to associate a stream with effects processors.
|
||||
* The effects are controlled using the Android AudioEffect Java API.
|
||||
*
|
||||
* The default, if you do not call this function, is SessionId::None.
|
||||
*
|
||||
* If set to SessionId::Allocate then a session ID will be allocated
|
||||
* when the stream is opened.
|
||||
*
|
||||
* The allocated session ID can be obtained by calling AudioStream::getSessionId()
|
||||
* and then used with this function when opening another stream.
|
||||
* This allows effects to be shared between streams.
|
||||
*
|
||||
* Session IDs from Oboe can be used the Android Java APIs and vice versa.
|
||||
* So a session ID from an Oboe stream can be passed to Java
|
||||
* and effects applied using the Java AudioEffect API.
|
||||
*
|
||||
* Allocated session IDs will always be positive and nonzero.
|
||||
*
|
||||
* Added in API level 28.
|
||||
*
|
||||
* @param sessionId an allocated sessionID or SessionId::Allocate
|
||||
*/
|
||||
AudioStreamBuilder *setSessionId(SessionId sessionId) {
|
||||
mSessionId = sessionId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Request a stream to a specific audio input/output device given an audio device ID.
|
||||
*
|
||||
* In most cases, the primary device will be the appropriate device to use, and the
|
||||
* deviceId can be left kUnspecified.
|
||||
*
|
||||
* On Android, for example, the ID could be obtained from the Java AudioManager.
|
||||
* AudioManager.getDevices() returns an array of AudioDeviceInfo[], which contains
|
||||
* a getId() method (as well as other type information), that should be passed
|
||||
* to this method.
|
||||
*
|
||||
*
|
||||
* Note that when using OpenSL ES, this will be ignored and the created
|
||||
* stream will have deviceId kUnspecified.
|
||||
*
|
||||
* @param deviceId device identifier or kUnspecified
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setDeviceId(int32_t deviceId) {
|
||||
mDeviceId = deviceId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an object to handle data related callbacks from the underlying API.
|
||||
*
|
||||
* <strong>Important: See AudioStreamCallback for restrictions on what may be called
|
||||
* from the callback methods.</strong>
|
||||
*
|
||||
* @param dataCallback
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setDataCallback(oboe::AudioStreamDataCallback *dataCallback) {
|
||||
mDataCallback = dataCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an object to handle error related callbacks from the underlying API.
|
||||
* This can occur when a stream is disconnected because a headset is plugged in or unplugged.
|
||||
* It can also occur if the audio service fails or if an exclusive stream is stolen by
|
||||
* another stream.
|
||||
*
|
||||
* <strong>Important: See AudioStreamCallback for restrictions on what may be called
|
||||
* from the callback methods.</strong>
|
||||
*
|
||||
* <strong>When an error callback occurs, the associated stream must be stopped and closed
|
||||
* in a separate thread.</strong>
|
||||
*
|
||||
* @param errorCallback
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setErrorCallback(oboe::AudioStreamErrorCallback *errorCallback) {
|
||||
mErrorCallback = errorCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specifies an object to handle data or error related callbacks from the underlying API.
|
||||
*
|
||||
* This is the equivalent of calling both setDataCallback() and setErrorCallback().
|
||||
*
|
||||
* <strong>Important: See AudioStreamCallback for restrictions on what may be called
|
||||
* from the callback methods.</strong>
|
||||
*
|
||||
* When an error callback occurs, the associated stream will be stopped and closed in a separate thread.
|
||||
*
|
||||
* A note on why the streamCallback parameter is a raw pointer rather than a smart pointer:
|
||||
*
|
||||
* The caller should retain ownership of the object streamCallback points to. At first glance weak_ptr may seem like
|
||||
* a good candidate for streamCallback as this implies temporary ownership. However, a weak_ptr can only be created
|
||||
* from a shared_ptr. A shared_ptr incurs some performance overhead. The callback object is likely to be accessed
|
||||
* every few milliseconds when the stream requires new data so this overhead is something we want to avoid.
|
||||
*
|
||||
* This leaves a raw pointer as the logical type choice. The only caveat being that the caller must not destroy
|
||||
* the callback before the stream has been closed.
|
||||
*
|
||||
* @param streamCallback
|
||||
* @return pointer to the builder so calls can be chained
|
||||
*/
|
||||
AudioStreamBuilder *setCallback(AudioStreamCallback *streamCallback) {
|
||||
// Use the same callback object for both, dual inheritance.
|
||||
mDataCallback = streamCallback;
|
||||
mErrorCallback = streamCallback;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true then Oboe might convert channel counts to achieve optimal results.
|
||||
* On some versions of Android for example, stereo streams could not use a FAST track.
|
||||
* So a mono stream might be used instead and duplicated to two channels.
|
||||
* On some devices, mono streams might be broken, so a stereo stream might be opened
|
||||
* and converted to mono.
|
||||
*
|
||||
* Default is true.
|
||||
*/
|
||||
AudioStreamBuilder *setChannelConversionAllowed(bool allowed) {
|
||||
mChannelConversionAllowed = allowed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* If true then Oboe might convert data formats to achieve optimal results.
|
||||
* On some versions of Android, for example, a float stream could not get a
|
||||
* low latency data path. So an I16 stream might be opened and converted to float.
|
||||
*
|
||||
* Default is true.
|
||||
*/
|
||||
AudioStreamBuilder *setFormatConversionAllowed(bool allowed) {
|
||||
mFormatConversionAllowed = allowed;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the quality of the sample rate converter in Oboe.
|
||||
*
|
||||
* If set to None then Oboe will not do sample rate conversion. But the underlying APIs might
|
||||
* still do sample rate conversion if you specify a sample rate.
|
||||
* That can prevent you from getting a low latency stream.
|
||||
*
|
||||
* If you do the conversion in Oboe then you might still get a low latency stream.
|
||||
*
|
||||
* Default is SampleRateConversionQuality::None
|
||||
*/
|
||||
AudioStreamBuilder *setSampleRateConversionQuality(SampleRateConversionQuality quality) {
|
||||
mSampleRateConversionQuality = quality;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if AAudio will be used based on the current settings.
|
||||
*/
|
||||
bool willUseAAudio() const {
|
||||
return (mAudioApi == AudioApi::AAudio && isAAudioSupported())
|
||||
|| (mAudioApi == AudioApi::Unspecified && isAAudioRecommended());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and open a stream object based on the current settings.
|
||||
*
|
||||
* The caller owns the pointer to the AudioStream object.
|
||||
*
|
||||
* @deprecated Use openStream(std::shared_ptr<oboe::AudioStream> &stream) instead.
|
||||
* @param stream pointer to a variable to receive the stream address
|
||||
* @return OBOE_OK if successful or a negative error code
|
||||
*/
|
||||
Result openStream(AudioStream **stream);
|
||||
|
||||
/**
|
||||
* Create and open a stream object based on the current settings.
|
||||
*
|
||||
* The caller shares the pointer to the AudioStream object.
|
||||
* The shared_ptr is used internally by Oboe to prevent the stream from being
|
||||
* deleted while it is being used by callbacks.
|
||||
*
|
||||
* @param stream reference to a shared_ptr to receive the stream address
|
||||
* @return OBOE_OK if successful or a negative error code
|
||||
*/
|
||||
Result openStream(std::shared_ptr<oboe::AudioStream> &stream);
|
||||
|
||||
/**
|
||||
* Create and open a ManagedStream object based on the current builder state.
|
||||
*
|
||||
* The caller must create a unique ptr, and pass by reference so it can be
|
||||
* modified to point to an opened stream. The caller owns the unique ptr,
|
||||
* and it will be automatically closed and deleted when going out of scope.
|
||||
* @param stream Reference to the ManagedStream (uniqueptr) used to keep track of stream
|
||||
* @return OBOE_OK if successful or a negative error code.
|
||||
*/
|
||||
Result openManagedStream(ManagedStream &stream);
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @param other
|
||||
* @return true if channels, format and sample rate match
|
||||
*/
|
||||
bool isCompatible(AudioStreamBase &other);
|
||||
|
||||
/**
|
||||
* Create an AudioStream object. The AudioStream must be opened before use.
|
||||
*
|
||||
* The caller owns the pointer.
|
||||
*
|
||||
* @return pointer to an AudioStream object or nullptr.
|
||||
*/
|
||||
oboe::AudioStream *build();
|
||||
|
||||
AudioApi mAudioApi = AudioApi::Unspecified;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif /* OBOE_STREAM_BUILDER_H_ */
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_STREAM_CALLBACK_H
|
||||
#define OBOE_STREAM_CALLBACK_H
|
||||
|
||||
#include "oboe/Definitions.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
class AudioStream;
|
||||
|
||||
/**
|
||||
* AudioStreamDataCallback defines a callback interface for
|
||||
* moving data to/from an audio stream using `onAudioReady`
|
||||
* 2) being alerted when a stream has an error using `onError*` methods
|
||||
*
|
||||
* It is used with AudioStreamBuilder::setDataCallback().
|
||||
*/
|
||||
|
||||
class AudioStreamDataCallback {
|
||||
public:
|
||||
virtual ~AudioStreamDataCallback() = default;
|
||||
|
||||
/**
|
||||
* A buffer is ready for processing.
|
||||
*
|
||||
* For an output stream, this function should render and write numFrames of data
|
||||
* in the stream's current data format to the audioData buffer.
|
||||
*
|
||||
* For an input stream, this function should read and process numFrames of data
|
||||
* from the audioData buffer.
|
||||
*
|
||||
* The audio data is passed through the buffer. So do NOT call read() or
|
||||
* write() on the stream that is making the callback.
|
||||
*
|
||||
* Note that numFrames can vary unless AudioStreamBuilder::setFramesPerCallback()
|
||||
* is called.
|
||||
*
|
||||
* Also note that this callback function should be considered a "real-time" function.
|
||||
* It must not do anything that could cause an unbounded delay because that can cause the
|
||||
* audio to glitch or pop.
|
||||
*
|
||||
* These are things the function should NOT do:
|
||||
* <ul>
|
||||
* <li>allocate memory using, for example, malloc() or new</li>
|
||||
* <li>any file operations such as opening, closing, reading or writing</li>
|
||||
* <li>any network operations such as streaming</li>
|
||||
* <li>use any mutexes or other synchronization primitives</li>
|
||||
* <li>sleep</li>
|
||||
* <li>oboeStream->stop(), pause(), flush() or close()</li>
|
||||
* <li>oboeStream->read()</li>
|
||||
* <li>oboeStream->write()</li>
|
||||
* </ul>
|
||||
*
|
||||
* The following are OK to call from the data callback:
|
||||
* <ul>
|
||||
* <li>oboeStream->get*()</li>
|
||||
* <li>oboe::convertToText()</li>
|
||||
* <li>oboeStream->setBufferSizeInFrames()</li>
|
||||
* </ul>
|
||||
*
|
||||
* If you need to move data, eg. MIDI commands, in or out of the callback function then
|
||||
* we recommend the use of non-blocking techniques such as an atomic FIFO.
|
||||
*
|
||||
* @param audioStream pointer to the associated stream
|
||||
* @param audioData buffer containing input data or a place to put output data
|
||||
* @param numFrames number of frames to be processed
|
||||
* @return DataCallbackResult::Continue or DataCallbackResult::Stop
|
||||
*/
|
||||
virtual DataCallbackResult onAudioReady(
|
||||
AudioStream *audioStream,
|
||||
void *audioData,
|
||||
int32_t numFrames) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* AudioStreamErrorCallback defines a callback interface for
|
||||
* being alerted when a stream has an error or is disconnected
|
||||
* using `onError*` methods.
|
||||
*
|
||||
* It is used with AudioStreamBuilder::setErrorCallback().
|
||||
*/
|
||||
class AudioStreamErrorCallback {
|
||||
public:
|
||||
virtual ~AudioStreamErrorCallback() = default;
|
||||
|
||||
/**
|
||||
* This will be called before other `onError` methods when an error occurs on a stream,
|
||||
* such as when the stream is disconnected.
|
||||
*
|
||||
* It can be used to override and customize the normal error processing.
|
||||
* Use of this method is considered an advanced technique.
|
||||
* It might, for example, be used if an app want to use a high level lock when
|
||||
* closing and reopening a stream.
|
||||
* Or it might be used when an app want to signal a management thread that handles
|
||||
* all of the stream state.
|
||||
*
|
||||
* If this method returns false it indicates that the stream has *not been stopped and closed
|
||||
* by the application. In this case it will be stopped by Oboe in the following way:
|
||||
* onErrorBeforeClose() will be called, then the stream will be closed and onErrorAfterClose()
|
||||
* will be closed.
|
||||
*
|
||||
* If this method returns true it indicates that the stream *has* been stopped and closed
|
||||
* by the application and Oboe will not do this.
|
||||
* In that case, the app MUST stop() and close() the stream.
|
||||
*
|
||||
* This method will be called on a thread created by Oboe.
|
||||
*
|
||||
* @param audioStream pointer to the associated stream
|
||||
* @param error
|
||||
* @return true if the stream has been stopped and closed, false if not
|
||||
*/
|
||||
virtual bool onError(AudioStream* /* audioStream */, Result /* error */) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called when an error occurs on a stream,
|
||||
* such as when the stream is disconnected,
|
||||
* and if onError() returns false (indicating that the error has not already been handled).
|
||||
*
|
||||
* Note that this will be called on a thread created by Oboe.
|
||||
*
|
||||
* The underlying stream will already be stopped by Oboe but not yet closed.
|
||||
* So the stream can be queried.
|
||||
*
|
||||
* Do not close or delete the stream in this method because it will be
|
||||
* closed after this method returns.
|
||||
*
|
||||
* @param audioStream pointer to the associated stream
|
||||
* @param error
|
||||
*/
|
||||
virtual void onErrorBeforeClose(AudioStream* /* audioStream */, Result /* error */) {}
|
||||
|
||||
/**
|
||||
* This will be called when an error occurs on a stream,
|
||||
* such as when the stream is disconnected,
|
||||
* and if onError() returns false (indicating that the error has not already been handled).
|
||||
*
|
||||
* The underlying AAudio or OpenSL ES stream will already be stopped AND closed by Oboe.
|
||||
* So the underlying stream cannot be referenced.
|
||||
* But you can still query most parameters.
|
||||
*
|
||||
* This callback could be used to reopen a new stream on another device.
|
||||
*
|
||||
* @param audioStream pointer to the associated stream
|
||||
* @param error
|
||||
*/
|
||||
virtual void onErrorAfterClose(AudioStream* /* audioStream */, Result /* error */) {}
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* AudioStreamCallback defines a callback interface for:
|
||||
*
|
||||
* 1) moving data to/from an audio stream using `onAudioReady`
|
||||
* 2) being alerted when a stream has an error using `onError*` methods
|
||||
*
|
||||
* It is used with AudioStreamBuilder::setCallback().
|
||||
*
|
||||
* It combines the interfaces defined by AudioStreamDataCallback and AudioStreamErrorCallback.
|
||||
* This was the original callback object. We now recommend using the individual interfaces
|
||||
* and using setDataCallback() and setErrorCallback().
|
||||
*
|
||||
* @deprecated Use `AudioStreamDataCallback` and `AudioStreamErrorCallback` instead
|
||||
*/
|
||||
class AudioStreamCallback : public AudioStreamDataCallback,
|
||||
public AudioStreamErrorCallback {
|
||||
public:
|
||||
virtual ~AudioStreamCallback() = default;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //OBOE_STREAM_CALLBACK_H
|
|
@ -0,0 +1,519 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_DEFINITIONS_H
|
||||
#define OBOE_DEFINITIONS_H
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
// Oboe needs to be able to build on old NDKs so we use hard coded constants.
|
||||
// The correctness of these constants is verified in "aaudio/AAudioLoader.cpp".
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* Represents any attribute, property or value which hasn't been specified.
|
||||
*/
|
||||
constexpr int32_t kUnspecified = 0;
|
||||
|
||||
// TODO: Investigate using std::chrono
|
||||
/**
|
||||
* The number of nanoseconds in a microsecond. 1,000.
|
||||
*/
|
||||
constexpr int64_t kNanosPerMicrosecond = 1000;
|
||||
|
||||
/**
|
||||
* The number of nanoseconds in a millisecond. 1,000,000.
|
||||
*/
|
||||
constexpr int64_t kNanosPerMillisecond = kNanosPerMicrosecond * 1000;
|
||||
|
||||
/**
|
||||
* The number of milliseconds in a second. 1,000.
|
||||
*/
|
||||
constexpr int64_t kMillisPerSecond = 1000;
|
||||
|
||||
/**
|
||||
* The number of nanoseconds in a second. 1,000,000,000.
|
||||
*/
|
||||
constexpr int64_t kNanosPerSecond = kNanosPerMillisecond * kMillisPerSecond;
|
||||
|
||||
/**
|
||||
* The state of the audio stream.
|
||||
*/
|
||||
enum class StreamState : int32_t { // aaudio_stream_state_t
|
||||
Uninitialized = 0, // AAUDIO_STREAM_STATE_UNINITIALIZED,
|
||||
Unknown = 1, // AAUDIO_STREAM_STATE_UNKNOWN,
|
||||
Open = 2, // AAUDIO_STREAM_STATE_OPEN,
|
||||
Starting = 3, // AAUDIO_STREAM_STATE_STARTING,
|
||||
Started = 4, // AAUDIO_STREAM_STATE_STARTED,
|
||||
Pausing = 5, // AAUDIO_STREAM_STATE_PAUSING,
|
||||
Paused = 6, // AAUDIO_STREAM_STATE_PAUSED,
|
||||
Flushing = 7, // AAUDIO_STREAM_STATE_FLUSHING,
|
||||
Flushed = 8, // AAUDIO_STREAM_STATE_FLUSHED,
|
||||
Stopping = 9, // AAUDIO_STREAM_STATE_STOPPING,
|
||||
Stopped = 10, // AAUDIO_STREAM_STATE_STOPPED,
|
||||
Closing = 11, // AAUDIO_STREAM_STATE_CLOSING,
|
||||
Closed = 12, // AAUDIO_STREAM_STATE_CLOSED,
|
||||
Disconnected = 13, // AAUDIO_STREAM_STATE_DISCONNECTED,
|
||||
};
|
||||
|
||||
/**
|
||||
* The direction of the stream.
|
||||
*/
|
||||
enum class Direction : int32_t { // aaudio_direction_t
|
||||
|
||||
/**
|
||||
* Used for playback.
|
||||
*/
|
||||
Output = 0, // AAUDIO_DIRECTION_OUTPUT,
|
||||
|
||||
/**
|
||||
* Used for recording.
|
||||
*/
|
||||
Input = 1, // AAUDIO_DIRECTION_INPUT,
|
||||
};
|
||||
|
||||
/**
|
||||
* The format of audio samples.
|
||||
*/
|
||||
enum class AudioFormat : int32_t { // aaudio_format_t
|
||||
/**
|
||||
* Invalid format.
|
||||
*/
|
||||
Invalid = -1, // AAUDIO_FORMAT_INVALID,
|
||||
|
||||
/**
|
||||
* Unspecified format. Format will be decided by Oboe.
|
||||
*/
|
||||
Unspecified = 0, // AAUDIO_FORMAT_UNSPECIFIED,
|
||||
|
||||
/**
|
||||
* Signed 16-bit integers.
|
||||
*/
|
||||
I16 = 1, // AAUDIO_FORMAT_PCM_I16,
|
||||
|
||||
/**
|
||||
* Single precision floating points.
|
||||
*/
|
||||
Float = 2, // AAUDIO_FORMAT_PCM_FLOAT,
|
||||
};
|
||||
|
||||
/**
|
||||
* The result of an audio callback.
|
||||
*/
|
||||
enum class DataCallbackResult : int32_t { // aaudio_data_callback_result_t
|
||||
// Indicates to the caller that the callbacks should continue.
|
||||
Continue = 0, // AAUDIO_CALLBACK_RESULT_CONTINUE,
|
||||
|
||||
// Indicates to the caller that the callbacks should stop immediately.
|
||||
Stop = 1, // AAUDIO_CALLBACK_RESULT_STOP,
|
||||
};
|
||||
|
||||
/**
|
||||
* The result of an operation. All except the `OK` result indicates that an error occurred.
|
||||
* The `Result` can be converted into a human readable string using `convertToText`.
|
||||
*/
|
||||
enum class Result : int32_t { // aaudio_result_t
|
||||
OK = 0, // AAUDIO_OK
|
||||
ErrorBase = -900, // AAUDIO_ERROR_BASE,
|
||||
ErrorDisconnected = -899, // AAUDIO_ERROR_DISCONNECTED,
|
||||
ErrorIllegalArgument = -898, // AAUDIO_ERROR_ILLEGAL_ARGUMENT,
|
||||
ErrorInternal = -896, // AAUDIO_ERROR_INTERNAL,
|
||||
ErrorInvalidState = -895, // AAUDIO_ERROR_INVALID_STATE,
|
||||
ErrorInvalidHandle = -892, // AAUDIO_ERROR_INVALID_HANDLE,
|
||||
ErrorUnimplemented = -890, // AAUDIO_ERROR_UNIMPLEMENTED,
|
||||
ErrorUnavailable = -889, // AAUDIO_ERROR_UNAVAILABLE,
|
||||
ErrorNoFreeHandles = -888, // AAUDIO_ERROR_NO_FREE_HANDLES,
|
||||
ErrorNoMemory = -887, // AAUDIO_ERROR_NO_MEMORY,
|
||||
ErrorNull = -886, // AAUDIO_ERROR_NULL,
|
||||
ErrorTimeout = -885, // AAUDIO_ERROR_TIMEOUT,
|
||||
ErrorWouldBlock = -884, // AAUDIO_ERROR_WOULD_BLOCK,
|
||||
ErrorInvalidFormat = -883, // AAUDIO_ERROR_INVALID_FORMAT,
|
||||
ErrorOutOfRange = -882, // AAUDIO_ERROR_OUT_OF_RANGE,
|
||||
ErrorNoService = -881, // AAUDIO_ERROR_NO_SERVICE,
|
||||
ErrorInvalidRate = -880, // AAUDIO_ERROR_INVALID_RATE,
|
||||
// Reserved for future AAudio result types
|
||||
Reserved1,
|
||||
Reserved2,
|
||||
Reserved3,
|
||||
Reserved4,
|
||||
Reserved5,
|
||||
Reserved6,
|
||||
Reserved7,
|
||||
Reserved8,
|
||||
Reserved9,
|
||||
Reserved10,
|
||||
ErrorClosed,
|
||||
};
|
||||
|
||||
/**
|
||||
* The sharing mode of the audio stream.
|
||||
*/
|
||||
enum class SharingMode : int32_t { // aaudio_sharing_mode_t
|
||||
|
||||
/**
|
||||
* This will be the only stream using a particular source or sink.
|
||||
* This mode will provide the lowest possible latency.
|
||||
* You should close EXCLUSIVE streams immediately when you are not using them.
|
||||
*
|
||||
* If you do not need the lowest possible latency then we recommend using Shared,
|
||||
* which is the default.
|
||||
*/
|
||||
Exclusive = 0, // AAUDIO_SHARING_MODE_EXCLUSIVE,
|
||||
|
||||
/**
|
||||
* Multiple applications can share the same device.
|
||||
* The data from output streams will be mixed by the audio service.
|
||||
* The data for input streams will be distributed by the audio service.
|
||||
*
|
||||
* This will have higher latency than the EXCLUSIVE mode.
|
||||
*/
|
||||
Shared = 1, // AAUDIO_SHARING_MODE_SHARED,
|
||||
};
|
||||
|
||||
/**
|
||||
* The performance mode of the audio stream.
|
||||
*/
|
||||
enum class PerformanceMode : int32_t { // aaudio_performance_mode_t
|
||||
|
||||
/**
|
||||
* No particular performance needs. Default.
|
||||
*/
|
||||
None = 10, // AAUDIO_PERFORMANCE_MODE_NONE,
|
||||
|
||||
/**
|
||||
* Extending battery life is most important.
|
||||
*/
|
||||
PowerSaving = 11, // AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
|
||||
|
||||
/**
|
||||
* Reducing latency is most important.
|
||||
*/
|
||||
LowLatency = 12, // AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
|
||||
};
|
||||
|
||||
/**
|
||||
* The underlying audio API used by the audio stream.
|
||||
*/
|
||||
enum class AudioApi : int32_t {
|
||||
/**
|
||||
* Try to use AAudio. If not available then use OpenSL ES.
|
||||
*/
|
||||
Unspecified = kUnspecified,
|
||||
|
||||
/**
|
||||
* Use OpenSL ES.
|
||||
*/
|
||||
OpenSLES,
|
||||
|
||||
/**
|
||||
* Try to use AAudio. Fail if unavailable.
|
||||
*/
|
||||
AAudio
|
||||
};
|
||||
|
||||
/**
|
||||
* Specifies the quality of the sample rate conversion performed by Oboe.
|
||||
* Higher quality will require more CPU load.
|
||||
* Higher quality conversion will probably be implemented using a sinc based resampler.
|
||||
*/
|
||||
enum class SampleRateConversionQuality : int32_t {
|
||||
/**
|
||||
* No conversion by Oboe. Underlying APIs may still do conversion.
|
||||
*/
|
||||
None,
|
||||
/**
|
||||
* Fastest conversion but may not sound great.
|
||||
* This may be implemented using bilinear interpolation.
|
||||
*/
|
||||
Fastest,
|
||||
Low,
|
||||
Medium,
|
||||
High,
|
||||
/**
|
||||
* Highest quality conversion, which may be expensive in terms of CPU.
|
||||
*/
|
||||
Best,
|
||||
};
|
||||
|
||||
/**
|
||||
* The Usage attribute expresses *why* you are playing a sound, what is this sound used for.
|
||||
* This information is used by certain platforms or routing policies
|
||||
* to make more refined volume or routing decisions.
|
||||
*
|
||||
* Note that these match the equivalent values in AudioAttributes in the Android Java API.
|
||||
*
|
||||
* This attribute only has an effect on Android API 28+.
|
||||
*/
|
||||
enum class Usage : int32_t { // aaudio_usage_t
|
||||
/**
|
||||
* Use this for streaming media, music performance, video, podcasts, etcetera.
|
||||
*/
|
||||
Media = 1, // AAUDIO_USAGE_MEDIA
|
||||
|
||||
/**
|
||||
* Use this for voice over IP, telephony, etcetera.
|
||||
*/
|
||||
VoiceCommunication = 2, // AAUDIO_USAGE_VOICE_COMMUNICATION
|
||||
|
||||
/**
|
||||
* Use this for sounds associated with telephony such as busy tones, DTMF, etcetera.
|
||||
*/
|
||||
VoiceCommunicationSignalling = 3, // AAUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING
|
||||
|
||||
/**
|
||||
* Use this to demand the users attention.
|
||||
*/
|
||||
Alarm = 4, // AAUDIO_USAGE_ALARM
|
||||
|
||||
/**
|
||||
* Use this for notifying the user when a message has arrived or some
|
||||
* other background event has occured.
|
||||
*/
|
||||
Notification = 5, // AAUDIO_USAGE_NOTIFICATION
|
||||
|
||||
/**
|
||||
* Use this when the phone rings.
|
||||
*/
|
||||
NotificationRingtone = 6, // AAUDIO_USAGE_NOTIFICATION_RINGTONE
|
||||
|
||||
/**
|
||||
* Use this to attract the users attention when, for example, the battery is low.
|
||||
*/
|
||||
NotificationEvent = 10, // AAUDIO_USAGE_NOTIFICATION_EVENT
|
||||
|
||||
/**
|
||||
* Use this for screen readers, etcetera.
|
||||
*/
|
||||
AssistanceAccessibility = 11, // AAUDIO_USAGE_ASSISTANCE_ACCESSIBILITY
|
||||
|
||||
/**
|
||||
* Use this for driving or navigation directions.
|
||||
*/
|
||||
AssistanceNavigationGuidance = 12, // AAUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE
|
||||
|
||||
/**
|
||||
* Use this for user interface sounds, beeps, etcetera.
|
||||
*/
|
||||
AssistanceSonification = 13, // AAUDIO_USAGE_ASSISTANCE_SONIFICATION
|
||||
|
||||
/**
|
||||
* Use this for game audio and sound effects.
|
||||
*/
|
||||
Game = 14, // AAUDIO_USAGE_GAME
|
||||
|
||||
/**
|
||||
* Use this for audio responses to user queries, audio instructions or help utterances.
|
||||
*/
|
||||
Assistant = 16, // AAUDIO_USAGE_ASSISTANT
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The ContentType attribute describes *what* you are playing.
|
||||
* It expresses the general category of the content. This information is optional.
|
||||
* But in case it is known (for instance {@link Movie} for a
|
||||
* movie streaming service or {@link Speech} for
|
||||
* an audio book application) this information might be used by the audio framework to
|
||||
* enforce audio focus.
|
||||
*
|
||||
* Note that these match the equivalent values in AudioAttributes in the Android Java API.
|
||||
*
|
||||
* This attribute only has an effect on Android API 28+.
|
||||
*/
|
||||
enum ContentType : int32_t { // aaudio_content_type_t
|
||||
|
||||
/**
|
||||
* Use this for spoken voice, audio books, etcetera.
|
||||
*/
|
||||
Speech = 1, // AAUDIO_CONTENT_TYPE_SPEECH
|
||||
|
||||
/**
|
||||
* Use this for pre-recorded or live music.
|
||||
*/
|
||||
Music = 2, // AAUDIO_CONTENT_TYPE_MUSIC
|
||||
|
||||
/**
|
||||
* Use this for a movie or video soundtrack.
|
||||
*/
|
||||
Movie = 3, // AAUDIO_CONTENT_TYPE_MOVIE
|
||||
|
||||
/**
|
||||
* Use this for sound is designed to accompany a user action,
|
||||
* such as a click or beep sound made when the user presses a button.
|
||||
*/
|
||||
Sonification = 4, // AAUDIO_CONTENT_TYPE_SONIFICATION
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines the audio source.
|
||||
* An audio source defines both a default physical source of audio signal, and a recording
|
||||
* configuration.
|
||||
*
|
||||
* Note that these match the equivalent values in MediaRecorder.AudioSource in the Android Java API.
|
||||
*
|
||||
* This attribute only has an effect on Android API 28+.
|
||||
*/
|
||||
enum InputPreset : int32_t { // aaudio_input_preset_t
|
||||
/**
|
||||
* Use this preset when other presets do not apply.
|
||||
*/
|
||||
Generic = 1, // AAUDIO_INPUT_PRESET_GENERIC
|
||||
|
||||
/**
|
||||
* Use this preset when recording video.
|
||||
*/
|
||||
Camcorder = 5, // AAUDIO_INPUT_PRESET_CAMCORDER
|
||||
|
||||
/**
|
||||
* Use this preset when doing speech recognition.
|
||||
*/
|
||||
VoiceRecognition = 6, // AAUDIO_INPUT_PRESET_VOICE_RECOGNITION
|
||||
|
||||
/**
|
||||
* Use this preset when doing telephony or voice messaging.
|
||||
*/
|
||||
VoiceCommunication = 7, // AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION
|
||||
|
||||
/**
|
||||
* Use this preset to obtain an input with no effects.
|
||||
* Note that this input will not have automatic gain control
|
||||
* so the recorded volume may be very low.
|
||||
*/
|
||||
Unprocessed = 9, // AAUDIO_INPUT_PRESET_UNPROCESSED
|
||||
|
||||
/**
|
||||
* Use this preset for capturing audio meant to be processed in real time
|
||||
* and played back for live performance (e.g karaoke).
|
||||
* The capture path will minimize latency and coupling with playback path.
|
||||
*/
|
||||
VoicePerformance = 10, // AAUDIO_INPUT_PRESET_VOICE_PERFORMANCE
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* This attribute can be used to allocate a session ID to the audio stream.
|
||||
*
|
||||
* This attribute only has an effect on Android API 28+.
|
||||
*/
|
||||
enum SessionId {
|
||||
/**
|
||||
* Do not allocate a session ID.
|
||||
* Effects cannot be used with this stream.
|
||||
* Default.
|
||||
*/
|
||||
None = -1, // AAUDIO_SESSION_ID_NONE
|
||||
|
||||
/**
|
||||
* Allocate a session ID that can be used to attach and control
|
||||
* effects using the Java AudioEffects API.
|
||||
* Note that the use of this flag may result in higher latency.
|
||||
*
|
||||
* Note that this matches the value of AudioManager.AUDIO_SESSION_ID_GENERATE.
|
||||
*/
|
||||
Allocate = 0, // AAUDIO_SESSION_ID_ALLOCATE
|
||||
};
|
||||
|
||||
/**
|
||||
* The channel count of the audio stream. The underlying type is `int32_t`.
|
||||
* Use of this enum is convenient to avoid "magic"
|
||||
* numbers when specifying the channel count.
|
||||
*
|
||||
* For example, you can write
|
||||
* `builder.setChannelCount(ChannelCount::Stereo)`
|
||||
* rather than `builder.setChannelCount(2)`
|
||||
*
|
||||
*/
|
||||
enum ChannelCount : int32_t {
|
||||
/**
|
||||
* Audio channel count definition, use Mono or Stereo
|
||||
*/
|
||||
Unspecified = kUnspecified,
|
||||
|
||||
/**
|
||||
* Use this for mono audio
|
||||
*/
|
||||
Mono = 1,
|
||||
|
||||
/**
|
||||
* Use this for stereo audio.
|
||||
*/
|
||||
Stereo = 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* On API 16 to 26 OpenSL ES will be used. When using OpenSL ES the optimal values for sampleRate and
|
||||
* framesPerBurst are not known by the native code.
|
||||
* On API 17+ these values should be obtained from the AudioManager using this code:
|
||||
*
|
||||
* <pre><code>
|
||||
* // Note that this technique only works for built-in speakers and headphones.
|
||||
* AudioManager myAudioMgr = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
|
||||
* String sampleRateStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
|
||||
* int defaultSampleRate = Integer.parseInt(sampleRateStr);
|
||||
* String framesPerBurstStr = myAudioMgr.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
|
||||
* int defaultFramesPerBurst = Integer.parseInt(framesPerBurstStr);
|
||||
* </code></pre>
|
||||
*
|
||||
* It can then be passed down to Oboe through JNI.
|
||||
*
|
||||
* AAudio will get the optimal framesPerBurst from the HAL and will ignore this value.
|
||||
*/
|
||||
class DefaultStreamValues {
|
||||
|
||||
public:
|
||||
|
||||
/** The default sample rate to use when opening new audio streams */
|
||||
static int32_t SampleRate;
|
||||
/** The default frames per burst to use when opening new audio streams */
|
||||
static int32_t FramesPerBurst;
|
||||
/** The default channel count to use when opening new audio streams */
|
||||
static int32_t ChannelCount;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* The time at which the frame at `position` was presented
|
||||
*/
|
||||
struct FrameTimestamp {
|
||||
int64_t position; // in frames
|
||||
int64_t timestamp; // in nanoseconds
|
||||
};
|
||||
|
||||
class OboeGlobals {
|
||||
public:
|
||||
|
||||
static bool areWorkaroundsEnabled() {
|
||||
return mWorkaroundsEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable this when writing tests to reproduce bugs in AAudio or OpenSL ES
|
||||
* that have workarounds in Oboe.
|
||||
* @param enabled
|
||||
*/
|
||||
static void setWorkaroundsEnabled(bool enabled) {
|
||||
mWorkaroundsEnabled = enabled;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool mWorkaroundsEnabled;
|
||||
};
|
||||
} // namespace oboe
|
||||
|
||||
#endif // OBOE_DEFINITIONS_H
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_LATENCY_TUNER_
|
||||
#define OBOE_LATENCY_TUNER_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include "oboe/Definitions.h"
|
||||
#include "oboe/AudioStream.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* LatencyTuner can be used to dynamically tune the latency of an output stream.
|
||||
* It adjusts the stream's bufferSize by monitoring the number of underruns.
|
||||
*
|
||||
* This only affects the latency associated with the first level of buffering that is closest
|
||||
* to the application. It does not affect low latency in the HAL, or touch latency in the UI.
|
||||
*
|
||||
* Call tune() right before returning from your data callback function if using callbacks.
|
||||
* Call tune() right before calling write() if using blocking writes.
|
||||
*
|
||||
* If you want to see the ongoing results of this tuning process then call
|
||||
* stream->getBufferSize() periodically.
|
||||
*
|
||||
*/
|
||||
class LatencyTuner {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a new LatencyTuner object which will act on the given audio stream
|
||||
*
|
||||
* @param stream the stream who's latency will be tuned
|
||||
*/
|
||||
explicit LatencyTuner(AudioStream &stream);
|
||||
|
||||
/**
|
||||
* Construct a new LatencyTuner object which will act on the given audio stream.
|
||||
*
|
||||
* @param stream the stream who's latency will be tuned
|
||||
* @param the maximum buffer size which the tune() operation will set the buffer size to
|
||||
*/
|
||||
explicit LatencyTuner(AudioStream &stream, int32_t maximumBufferSize);
|
||||
|
||||
/**
|
||||
* Adjust the bufferSizeInFrames to optimize latency.
|
||||
* It will start with a low latency and then raise it if an underrun occurs.
|
||||
*
|
||||
* Latency tuning is only supported for AAudio.
|
||||
*
|
||||
* @return OK or negative error, ErrorUnimplemented for OpenSL ES
|
||||
*/
|
||||
Result tune();
|
||||
|
||||
/**
|
||||
* This may be called from another thread. Then tune() will call reset(),
|
||||
* which will lower the latency to the minimum and then allow it to rise back up
|
||||
* if there are glitches.
|
||||
*
|
||||
* This is typically called in response to a user decision to minimize latency. In other words,
|
||||
* call this from a button handler.
|
||||
*/
|
||||
void requestReset();
|
||||
|
||||
/**
|
||||
* @return true if the audio stream's buffer size is at the maximum value. If no maximum value
|
||||
* was specified when constructing the LatencyTuner then the value of
|
||||
* stream->getBufferCapacityInFrames is used
|
||||
*/
|
||||
bool isAtMaximumBufferSize();
|
||||
|
||||
/**
|
||||
* Set the minimum bufferSize in frames that is used when the tuner is reset.
|
||||
* You may wish to call requestReset() after calling this.
|
||||
* @param bufferSize
|
||||
*/
|
||||
void setMinimumBufferSize(int32_t bufferSize) {
|
||||
mMinimumBufferSize = bufferSize;
|
||||
}
|
||||
|
||||
int32_t getMinimumBufferSize() const {
|
||||
return mMinimumBufferSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the amount the bufferSize will be incremented while tuning.
|
||||
* By default, this will be one burst.
|
||||
*
|
||||
* Note that AAudio will quantize the buffer size to a multiple of the burstSize.
|
||||
* So the final buffer sizes may not be a multiple of this increment.
|
||||
*
|
||||
* @param sizeIncrement
|
||||
*/
|
||||
void setBufferSizeIncrement(int32_t sizeIncrement) {
|
||||
mBufferSizeIncrement = sizeIncrement;
|
||||
}
|
||||
|
||||
int32_t getBufferSizeIncrement() const {
|
||||
return mBufferSizeIncrement;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Drop the latency down to the minimum and then let it rise back up.
|
||||
* This is useful if a glitch caused the latency to increase and it hasn't gone back down.
|
||||
*
|
||||
* This should only be called in the same thread as tune().
|
||||
*/
|
||||
void reset();
|
||||
|
||||
enum class State {
|
||||
Idle,
|
||||
Active,
|
||||
AtMax,
|
||||
Unsupported
|
||||
} ;
|
||||
|
||||
// arbitrary number of calls to wait before bumping up the latency
|
||||
static constexpr int32_t kIdleCount = 8;
|
||||
static constexpr int32_t kDefaultNumBursts = 2;
|
||||
|
||||
AudioStream &mStream;
|
||||
State mState = State::Idle;
|
||||
int32_t mMaxBufferSize = 0;
|
||||
int32_t mPreviousXRuns = 0;
|
||||
int32_t mIdleCountDown = 0;
|
||||
int32_t mMinimumBufferSize;
|
||||
int32_t mBufferSizeIncrement;
|
||||
std::atomic<int32_t> mLatencyTriggerRequests{0}; // TODO user atomic requester from AAudio
|
||||
std::atomic<int32_t> mLatencyTriggerResponses{0};
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif // OBOE_LATENCY_TUNER_
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (C) 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_OBOE_H
|
||||
#define OBOE_OBOE_H
|
||||
|
||||
/**
|
||||
* \mainpage API reference
|
||||
*
|
||||
* All documentation is found in the <a href="namespaceoboe.html">oboe namespace section</a>
|
||||
*
|
||||
*/
|
||||
|
||||
#include "oboe/Definitions.h"
|
||||
#include "oboe/ResultWithValue.h"
|
||||
#include "oboe/LatencyTuner.h"
|
||||
#include "oboe/AudioStream.h"
|
||||
#include "oboe/AudioStreamBase.h"
|
||||
#include "oboe/AudioStreamBuilder.h"
|
||||
#include "oboe/Utilities.h"
|
||||
#include "oboe/Version.h"
|
||||
#include "oboe/StabilizedCallback.h"
|
||||
|
||||
#endif //OBOE_OBOE_H
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_RESULT_WITH_VALUE_H
|
||||
#define OBOE_RESULT_WITH_VALUE_H
|
||||
|
||||
#include "oboe/Definitions.h"
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* A ResultWithValue can store both the result of an operation (either OK or an error) and a value.
|
||||
*
|
||||
* It has been designed for cases where the caller needs to know whether an operation succeeded and,
|
||||
* if it did, a value which was obtained during the operation.
|
||||
*
|
||||
* For example, when reading from a stream the caller needs to know the result of the read operation
|
||||
* and, if it was successful, how many frames were read. Note that ResultWithValue can be evaluated
|
||||
* as a boolean so it's simple to check whether the result is OK.
|
||||
*
|
||||
* <code>
|
||||
* ResultWithValue<int32_t> resultOfRead = myStream.read(&buffer, numFrames, timeoutNanoseconds);
|
||||
*
|
||||
* if (resultOfRead) {
|
||||
* LOGD("Frames read: %d", resultOfRead.value());
|
||||
* } else {
|
||||
* LOGD("Error reading from stream: %s", resultOfRead.error());
|
||||
* }
|
||||
* </code>
|
||||
*/
|
||||
template <typename T>
|
||||
class ResultWithValue {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Construct a ResultWithValue containing an error result.
|
||||
*
|
||||
* @param error The error
|
||||
*/
|
||||
ResultWithValue(oboe::Result error)
|
||||
: mValue{}
|
||||
, mError(error) {}
|
||||
|
||||
/**
|
||||
* Construct a ResultWithValue containing an OK result and a value.
|
||||
*
|
||||
* @param value the value to store
|
||||
*/
|
||||
explicit ResultWithValue(T value)
|
||||
: mValue(value)
|
||||
, mError(oboe::Result::OK) {}
|
||||
|
||||
/**
|
||||
* Get the result.
|
||||
*
|
||||
* @return the result
|
||||
*/
|
||||
oboe::Result error() const {
|
||||
return mError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value
|
||||
* @return
|
||||
*/
|
||||
T value() const {
|
||||
return mValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if OK
|
||||
*/
|
||||
explicit operator bool() const { return mError == oboe::Result::OK; }
|
||||
|
||||
/**
|
||||
* Quick way to check for an error.
|
||||
*
|
||||
* The caller could write something like this:
|
||||
* <code>
|
||||
* if (!result) { printf("Got error %s\n", convertToText(result.error())); }
|
||||
* </code>
|
||||
*
|
||||
* @return true if an error occurred
|
||||
*/
|
||||
bool operator !() const { return mError != oboe::Result::OK; }
|
||||
|
||||
/**
|
||||
* Implicitly convert to a Result. This enables easy comparison with Result values. Example:
|
||||
*
|
||||
* <code>
|
||||
* ResultWithValue result = openStream();
|
||||
* if (result == Result::ErrorNoMemory){ // tell user they're out of memory }
|
||||
* </code>
|
||||
*/
|
||||
operator Result() const {
|
||||
return mError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ResultWithValue from a number. If the number is positive the ResultWithValue will
|
||||
* have a result of Result::OK and the value will contain the number. If the number is negative
|
||||
* the result will be obtained from the negative number (numeric error codes can be found in
|
||||
* AAudio.h) and the value will be null.
|
||||
*
|
||||
*/
|
||||
static ResultWithValue<T> createBasedOnSign(T numericResult){
|
||||
|
||||
// Ensure that the type is either an integer or float
|
||||
static_assert(std::is_arithmetic<T>::value,
|
||||
"createBasedOnSign can only be called for numeric types (int or float)");
|
||||
|
||||
if (numericResult >= 0){
|
||||
return ResultWithValue<T>(numericResult);
|
||||
} else {
|
||||
return ResultWithValue<T>(static_cast<Result>(numericResult));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const T mValue;
|
||||
const oboe::Result mError;
|
||||
};
|
||||
|
||||
/**
|
||||
* If the result is `OK` then return the value, otherwise return a human-readable error message.
|
||||
*/
|
||||
template <typename T>
|
||||
std::ostream& operator<<(std::ostream &strm, const ResultWithValue<T> &result) {
|
||||
if (!result) {
|
||||
strm << convertToText(result.error());
|
||||
} else {
|
||||
strm << result.value();
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
|
||||
#endif //OBOE_RESULT_WITH_VALUE_H
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_STABILIZEDCALLBACK_H
|
||||
#define OBOE_STABILIZEDCALLBACK_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "oboe/AudioStream.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
class StabilizedCallback : public AudioStreamCallback {
|
||||
|
||||
public:
|
||||
explicit StabilizedCallback(AudioStreamCallback *callback);
|
||||
|
||||
DataCallbackResult
|
||||
onAudioReady(AudioStream *oboeStream, void *audioData, int32_t numFrames) override;
|
||||
|
||||
void onErrorBeforeClose(AudioStream *oboeStream, Result error) override {
|
||||
return mCallback->onErrorBeforeClose(oboeStream, error);
|
||||
}
|
||||
|
||||
void onErrorAfterClose(AudioStream *oboeStream, Result error) override {
|
||||
|
||||
// Reset all fields now that the stream has been closed
|
||||
mFrameCount = 0;
|
||||
mEpochTimeNanos = 0;
|
||||
mOpsPerNano = 1;
|
||||
return mCallback->onErrorAfterClose(oboeStream, error);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
AudioStreamCallback *mCallback = nullptr;
|
||||
int64_t mFrameCount = 0;
|
||||
int64_t mEpochTimeNanos = 0;
|
||||
double mOpsPerNano = 1;
|
||||
|
||||
void generateLoad(int64_t durationNanos);
|
||||
};
|
||||
|
||||
/**
|
||||
* cpu_relax is an architecture specific method of telling the CPU that you don't want it to
|
||||
* do much work. asm volatile keeps the compiler from optimising these instructions out.
|
||||
*/
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
#define cpu_relax() asm volatile("rep; nop" ::: "memory");
|
||||
|
||||
#elif defined(__arm__) || defined(__mips__)
|
||||
#define cpu_relax() asm volatile("":::"memory")
|
||||
|
||||
#elif defined(__aarch64__)
|
||||
#define cpu_relax() asm volatile("yield" ::: "memory")
|
||||
|
||||
#else
|
||||
#error "cpu_relax is not defined for this architecture"
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif //OBOE_STABILIZEDCALLBACK_H
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright 2016 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_UTILITIES_H
|
||||
#define OBOE_UTILITIES_H
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <string>
|
||||
#include "oboe/Definitions.h"
|
||||
|
||||
namespace oboe {
|
||||
|
||||
/**
|
||||
* Convert an array of floats to an array of 16-bit integers.
|
||||
*
|
||||
* @param source the input array.
|
||||
* @param destination the output array.
|
||||
* @param numSamples the number of values to convert.
|
||||
*/
|
||||
void convertFloatToPcm16(const float *source, int16_t *destination, int32_t numSamples);
|
||||
|
||||
/**
|
||||
* Convert an array of 16-bit integers to an array of floats.
|
||||
*
|
||||
* @param source the input array.
|
||||
* @param destination the output array.
|
||||
* @param numSamples the number of values to convert.
|
||||
*/
|
||||
void convertPcm16ToFloat(const int16_t *source, float *destination, int32_t numSamples);
|
||||
|
||||
/**
|
||||
* @return the size of a sample of the given format in bytes or 0 if format is invalid
|
||||
*/
|
||||
int32_t convertFormatToSizeInBytes(AudioFormat format);
|
||||
|
||||
/**
|
||||
* The text is the ASCII symbol corresponding to the supplied Oboe enum value,
|
||||
* or an English message saying the value is unrecognized.
|
||||
* This is intended for developers to use when debugging.
|
||||
* It is not for displaying to users.
|
||||
*
|
||||
* @param input object to convert from. @see common/Utilities.cpp for concrete implementations
|
||||
* @return text representation of an Oboe enum value. There is no need to call free on this.
|
||||
*/
|
||||
template <typename FromType>
|
||||
const char * convertToText(FromType input);
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @return the value of a named system property in a string or empty string
|
||||
*/
|
||||
std::string getPropertyString(const char * name);
|
||||
|
||||
/**
|
||||
* @param name
|
||||
* @param defaultValue
|
||||
* @return integer value associated with a property or the default value
|
||||
*/
|
||||
int getPropertyInteger(const char * name, int defaultValue);
|
||||
|
||||
/**
|
||||
* Return the version of the SDK that is currently running.
|
||||
*
|
||||
* For example, on Android, this would return 27 for Oreo 8.1.
|
||||
* If the version number cannot be determined then this will return -1.
|
||||
*
|
||||
* @return version number or -1
|
||||
*/
|
||||
int getSdkVersion();
|
||||
|
||||
} // namespace oboe
|
||||
|
||||
#endif //OBOE_UTILITIES_H
|
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef OBOE_VERSIONINFO_H
|
||||
#define OBOE_VERSIONINFO_H
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* A note on use of preprocessor defines:
|
||||
*
|
||||
* This is one of the few times when it's suitable to use preprocessor defines rather than constexpr
|
||||
* Why? Because C++11 requires a lot of boilerplate code to convert integers into compile-time
|
||||
* string literals. The preprocessor, despite it's lack of type checking, is more suited to the task
|
||||
*
|
||||
* See: https://stackoverflow.com/questions/6713420/c-convert-integer-to-string-at-compile-time/26824971#26824971
|
||||
*
|
||||
*/
|
||||
|
||||
// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description.
|
||||
#define OBOE_VERSION_MAJOR 1
|
||||
|
||||
// Type: 8-bit unsigned int. Min value: 0 Max value: 255. See below for description.
|
||||
#define OBOE_VERSION_MINOR 5
|
||||
|
||||
// Type: 16-bit unsigned int. Min value: 0 Max value: 65535. See below for description.
|
||||
#define OBOE_VERSION_PATCH 0
|
||||
|
||||
#define OBOE_STRINGIFY(x) #x
|
||||
#define OBOE_TOSTRING(x) OBOE_STRINGIFY(x)
|
||||
|
||||
// Type: String literal. See below for description.
|
||||
#define OBOE_VERSION_TEXT \
|
||||
OBOE_TOSTRING(OBOE_VERSION_MAJOR) "." \
|
||||
OBOE_TOSTRING(OBOE_VERSION_MINOR) "." \
|
||||
OBOE_TOSTRING(OBOE_VERSION_PATCH)
|
||||
|
||||
// Type: 32-bit unsigned int. See below for description.
|
||||
#define OBOE_VERSION_NUMBER ((OBOE_VERSION_MAJOR << 24) | (OBOE_VERSION_MINOR << 16) | OBOE_VERSION_PATCH)
|
||||
|
||||
namespace oboe {
|
||||
|
||||
const char * getVersionText();
|
||||
|
||||
/**
|
||||
* Oboe versioning object
|
||||
*/
|
||||
struct Version {
|
||||
/**
|
||||
* This is incremented when we make breaking API changes. Based loosely on https://semver.org/.
|
||||
*/
|
||||
static constexpr uint8_t Major = OBOE_VERSION_MAJOR;
|
||||
|
||||
/**
|
||||
* This is incremented when we add backwards compatible functionality. Or set to zero when MAJOR is
|
||||
* incremented.
|
||||
*/
|
||||
static constexpr uint8_t Minor = OBOE_VERSION_MINOR;
|
||||
|
||||
/**
|
||||
* This is incremented when we make backwards compatible bug fixes. Or set to zero when MINOR is
|
||||
* incremented.
|
||||
*/
|
||||
static constexpr uint16_t Patch = OBOE_VERSION_PATCH;
|
||||
|
||||
/**
|
||||
* Version string in the form MAJOR.MINOR.PATCH.
|
||||
*/
|
||||
static constexpr const char * Text = OBOE_VERSION_TEXT;
|
||||
|
||||
/**
|
||||
* Integer representation of the current Oboe library version. This will always increase when the
|
||||
* version number changes so can be compared using integer comparison.
|
||||
*/
|
||||
static constexpr uint32_t Number = OBOE_VERSION_NUMBER;
|
||||
};
|
||||
|
||||
} // namespace oboe
|
||||
#endif //OBOE_VERSIONINFO_H
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -50,7 +50,7 @@ u8* ICache;
|
|||
void (*EntryPoints[ARAM_SIZE_MAX / 4])();
|
||||
|
||||
#ifdef _WIN32
|
||||
alignas(4096) static u8 ARM7_TCB[ICacheSize];
|
||||
static u8 *ARM7_TCB;
|
||||
#elif defined(__unix__) || defined(__SWITCH__)
|
||||
alignas(4096) static u8 ARM7_TCB[ICacheSize] __attribute__((section(".text")));
|
||||
#elif defined(__APPLE__)
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <aarch64/macro-assembler-aarch64.h>
|
||||
using namespace vixl::aarch64;
|
||||
//#include <aarch32/disasm-aarch32.h>
|
||||
#include "rec-ARM64/arm64_unwind.h"
|
||||
|
||||
namespace aicaarm {
|
||||
|
||||
|
@ -36,6 +37,8 @@ class Arm7Compiler;
|
|||
|
||||
#define MAX_REGS 8
|
||||
|
||||
static Arm64UnwindInfo unwinder;
|
||||
|
||||
class AArch64ArmRegAlloc : public ArmRegAlloc<MAX_REGS, AArch64ArmRegAlloc>
|
||||
{
|
||||
Arm7Compiler& assembler;
|
||||
|
@ -636,6 +639,9 @@ public:
|
|||
Label arm_dofiq;
|
||||
Label arm_exit;
|
||||
|
||||
// For stack unwinding purposes, we pretend that the entire code block is a single function
|
||||
unwinder.start(GetCursorAddress<void *>());
|
||||
|
||||
// arm_compilecode:
|
||||
arm_compilecode = (void (*)())recompiler::writeToExec(GetCursorAddress<void *>());
|
||||
call((void*)recompiler::compile);
|
||||
|
@ -643,12 +649,26 @@ public:
|
|||
|
||||
// arm_mainloop(regs, entry points)
|
||||
arm_mainloop = (arm_mainloop_t)recompiler::writeToExec(GetCursorAddress<void *>());
|
||||
|
||||
Stp(x25, x26, MemOperand(sp, -96, AddrMode::PreIndex));
|
||||
unwinder.allocStack(0, 96);
|
||||
unwinder.saveReg(0, x25, 96);
|
||||
unwinder.saveReg(0, x26, 88);
|
||||
Stp(x27, x28, MemOperand(sp, 16));
|
||||
unwinder.saveReg(0, x27, 80);
|
||||
unwinder.saveReg(0, x28, 72);
|
||||
Stp(x29, x30, MemOperand(sp, 32));
|
||||
unwinder.saveReg(0, x29, 64);
|
||||
unwinder.saveReg(0, x30, 56);
|
||||
Stp(x19, x20, MemOperand(sp, 48));
|
||||
unwinder.saveReg(0, x19, 48);
|
||||
unwinder.saveReg(0, x20, 40);
|
||||
Stp(x21, x22, MemOperand(sp, 64));
|
||||
unwinder.saveReg(0, x21, 32);
|
||||
unwinder.saveReg(0, x22, 24);
|
||||
Stp(x23, x24, MemOperand(sp, 80));
|
||||
unwinder.saveReg(0, x23, 16);
|
||||
unwinder.saveReg(0, x24, 8);
|
||||
|
||||
Mov(x28, x0); // arm7 registers
|
||||
Mov(x26, x1); // lookup base
|
||||
|
@ -682,6 +702,10 @@ public:
|
|||
Ret();
|
||||
|
||||
FinalizeCode();
|
||||
|
||||
size_t unwindSize = unwinder.end(recompiler::spaceLeft() - 128, (ptrdiff_t)recompiler::writeToExec(nullptr));
|
||||
verify(unwindSize <= 128);
|
||||
|
||||
vmem_platform_flush_cache(
|
||||
recompiler::writeToExec(GetBuffer()->GetStartAddress<void*>()), recompiler::writeToExec(GetBuffer()->GetEndAddress<void*>()),
|
||||
GetBuffer()->GetStartAddress<void*>(), GetBuffer()->GetEndAddress<void*>());
|
||||
|
@ -707,6 +731,7 @@ void arm7backend_compile(const std::vector<ArmOp>& block_ops, u32 cycles)
|
|||
|
||||
void arm7backend_flush()
|
||||
{
|
||||
unwinder.clear();
|
||||
Arm7Compiler assembler;
|
||||
assembler.generateMainLoop();
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,10 +20,6 @@
|
|||
// Based on Asmjit unwind info registration and stack walking code for Windows, Linux and macOS
|
||||
// https://gist.github.com/dpjudas/925d5c4ffef90bd8114be3b465069fff
|
||||
#include "build.h"
|
||||
|
||||
#if HOST_CPU == CPU_X64
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
#include "oslib/oslib.h"
|
||||
|
||||
extern "C"
|
||||
|
@ -32,6 +28,8 @@ extern "C"
|
|||
void __deregister_frame(const void*);
|
||||
}
|
||||
|
||||
#if HOST_CPU == CPU_X64
|
||||
|
||||
constexpr int dwarfRegId[16] = {
|
||||
0, // RAX
|
||||
2, // RCX
|
||||
|
@ -50,8 +48,30 @@ constexpr int dwarfRegId[16] = {
|
|||
14, // R14
|
||||
15, // R15
|
||||
};
|
||||
inline static int registerId(int x64Id) {
|
||||
return dwarfRegId[x64Id];
|
||||
}
|
||||
constexpr int dwarfRegRAId = 16;
|
||||
constexpr int dwarfRegXmmId = 17;
|
||||
constexpr int dwarfRegSP = dwarfRegId[4]; // RSP
|
||||
|
||||
#elif HOST_CPU == CPU_ARM64
|
||||
// https://developer.arm.com/documentation/ihi0057/latest
|
||||
//
|
||||
// register codes:
|
||||
// x0-x30 0 - 30
|
||||
// SP 31
|
||||
// d0-d31 64 - 95
|
||||
|
||||
inline static int registerId(int armId) {
|
||||
return armId;
|
||||
}
|
||||
constexpr int dwarfRegRAId = 30;
|
||||
constexpr int dwarfRegSP = 31;
|
||||
|
||||
#endif
|
||||
|
||||
#if HOST_CPU == CPU_X64 || HOST_CPU == CPU_ARM64
|
||||
|
||||
using ByteStream = std::vector<u8>;
|
||||
|
||||
|
@ -86,26 +106,16 @@ static void writeULEB128(ByteStream &stream, u32 v)
|
|||
|
||||
static void writeSLEB128(ByteStream &stream, int32_t v)
|
||||
{
|
||||
if (v >= 0)
|
||||
{
|
||||
writeULEB128(stream, v);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (v > -128)
|
||||
{
|
||||
write<u8>(stream, v & 0x7f);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
write<u8>(stream, v);
|
||||
bool more;
|
||||
do {
|
||||
u8 byte = v & 0x7f;
|
||||
v >>= 7;
|
||||
}
|
||||
}
|
||||
}
|
||||
more = !(((v == 0 && (byte & 0x40) == 0) ||
|
||||
(v == -1 && (byte & 0x40) != 0)));
|
||||
if (more)
|
||||
byte |= 0x80; // Mark this byte to show that more bytes will follow.
|
||||
write<u8>(stream, byte);
|
||||
} while (more);
|
||||
}
|
||||
|
||||
static void writePadding(ByteStream &stream)
|
||||
|
@ -129,8 +139,8 @@ static void writeCIE(ByteStream &stream, const ByteStream &cieInstructions, u8 r
|
|||
write<u8>(stream, 'z');
|
||||
write<u8>(stream, 'R'); // fde encoding
|
||||
write<u8>(stream, 0);
|
||||
writeULEB128(stream, 1);
|
||||
writeSLEB128(stream, -1);
|
||||
writeULEB128(stream, 1); // code alignment. Loc multiplier
|
||||
writeSLEB128(stream, -1); // data alignment. Stack offset multiplier.
|
||||
writeULEB128(stream, returnAddressReg);
|
||||
|
||||
writeULEB128(stream, 1); // LEB128 augmentation size
|
||||
|
@ -164,6 +174,8 @@ static void writeFDE(ByteStream &stream, const ByteStream &fdeInstructions, u32
|
|||
static void writeAdvanceLoc(ByteStream &fdeInstructions, u64 offset, u64 &lastOffset)
|
||||
{
|
||||
u64 delta = offset - lastOffset;
|
||||
if (delta == 0)
|
||||
return;
|
||||
if (delta < (1 << 6))
|
||||
{
|
||||
write<u8>(fdeInstructions, (1 << 6) | delta); // DW_CFA_advance_loc
|
||||
|
@ -205,13 +217,27 @@ static void writeRegisterStackLocation(ByteStream &instructions, int dwarfRegId,
|
|||
writeULEB128(instructions, stackLocation);
|
||||
}
|
||||
|
||||
void UnwindInfo::start(void *address) {
|
||||
static void writeRegisterStackLocationExtended(ByteStream &instructions, int dwarfRegId, int stackLocation)
|
||||
{
|
||||
write<u8>(instructions, 5); // DW_CFA_offset_extended
|
||||
writeULEB128(instructions, dwarfRegId);
|
||||
writeULEB128(instructions, stackLocation);
|
||||
}
|
||||
|
||||
void UnwindInfo::start(void *address)
|
||||
{
|
||||
startAddr = (u8 *)address;
|
||||
#if HOST_CPU == CPU_X64
|
||||
stackOffset = 8;
|
||||
#else
|
||||
stackOffset = 0;
|
||||
#endif
|
||||
lastOffset = 0;
|
||||
cieInstructions.clear();
|
||||
fdeInstructions.clear();
|
||||
writeDefineCFA(cieInstructions, dwarfRegId[Xbyak::Operand::RSP], stackOffset);
|
||||
writeDefineCFA(cieInstructions, dwarfRegSP, stackOffset);
|
||||
if (stackOffset > 0)
|
||||
// Return address pushed on stack
|
||||
writeRegisterStackLocation(cieInstructions, dwarfRegRAId, stackOffset);
|
||||
}
|
||||
|
||||
|
@ -220,12 +246,19 @@ void UnwindInfo::pushReg(u32 offset, int reg)
|
|||
stackOffset += 8;
|
||||
writeAdvanceLoc(fdeInstructions, offset, lastOffset);
|
||||
writeDefineStackOffset(fdeInstructions, stackOffset);
|
||||
writeRegisterStackLocation(fdeInstructions, dwarfRegId[reg], stackOffset);
|
||||
writeRegisterStackLocation(fdeInstructions, registerId(reg), stackOffset);
|
||||
}
|
||||
|
||||
void UnwindInfo::pushFPReg(u32 offset, int reg)
|
||||
void UnwindInfo::saveReg(u32 offset, int reg, int stackOffset)
|
||||
{
|
||||
// TODO
|
||||
writeAdvanceLoc(fdeInstructions, offset, lastOffset);
|
||||
writeRegisterStackLocation(fdeInstructions, registerId(reg), stackOffset);
|
||||
}
|
||||
|
||||
void UnwindInfo::saveExtReg(u32 offset, int reg, int stackOffset)
|
||||
{
|
||||
writeAdvanceLoc(fdeInstructions, offset, lastOffset);
|
||||
writeRegisterStackLocationExtended(fdeInstructions, registerId(reg), stackOffset);
|
||||
}
|
||||
|
||||
void UnwindInfo::allocStack(u32 offset, int size)
|
||||
|
@ -239,7 +272,7 @@ void UnwindInfo::endProlog(u32 offset)
|
|||
{
|
||||
}
|
||||
|
||||
size_t UnwindInfo::end(u32 offset)
|
||||
size_t UnwindInfo::end(u32 offset, ptrdiff_t rwRxOffset)
|
||||
{
|
||||
ByteStream unwindInfo;
|
||||
writeCIE(unwindInfo, cieInstructions, dwarfRegRAId);
|
||||
|
@ -257,7 +290,7 @@ size_t UnwindInfo::end(u32 offset)
|
|||
if (!unwindInfo.empty())
|
||||
{
|
||||
u64 *unwindfuncaddr = (u64 *)(unwindInfoDest + functionStart);
|
||||
unwindfuncaddr[0] = (ptrdiff_t)startAddr;
|
||||
unwindfuncaddr[0] = (uintptr_t)startAddr + rwRxOffset;
|
||||
unwindfuncaddr[1] = (ptrdiff_t)(endAddr - startAddr);
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
@ -313,5 +346,4 @@ void UnwindInfo::clear()
|
|||
registeredFrames.clear();
|
||||
}
|
||||
|
||||
#endif // HOST_CPU == CPU_X64
|
||||
|
||||
#endif
|
||||
|
|
|
@ -60,10 +60,11 @@ class UnwindInfo
|
|||
public:
|
||||
void start(void *address);
|
||||
void pushReg(u32 offset, int reg);
|
||||
void pushFPReg(u32 offset, int reg);
|
||||
void saveReg(u32 offset, int reg, int stackOffset);
|
||||
void saveExtReg(u32 offset, int reg, int stackOffset);
|
||||
void allocStack(u32 offset, int size);
|
||||
void endProlog(u32 offset);
|
||||
size_t end(u32 offset);
|
||||
size_t end(u32 offset, ptrdiff_t rwRxOffset = 0);
|
||||
|
||||
void clear();
|
||||
|
||||
|
@ -73,7 +74,7 @@ private:
|
|||
std::vector<RUNTIME_FUNCTION *> tables;
|
||||
std::vector<u16> codes;
|
||||
#endif
|
||||
#if defined(__unix__) || defined(__APPLE__)
|
||||
#if defined(__unix__) || defined(__APPLE__) || defined(__SWITCH__)
|
||||
int stackOffset = 0;
|
||||
u64 lastOffset = 0;
|
||||
std::vector<u8> cieInstructions;
|
||||
|
@ -82,18 +83,20 @@ private:
|
|||
#endif
|
||||
};
|
||||
|
||||
#if !defined(_WIN64) && !defined(__unix__) && !defined(__APPLE__)
|
||||
#if HOST_CPU != CPU_X64 && HOST_CPU != CPU_ARM64
|
||||
inline void UnwindInfo::start(void *address) {
|
||||
}
|
||||
inline void UnwindInfo::pushReg(u32 offset, int reg) {
|
||||
}
|
||||
inline void UnwindInfo::pushFPReg(u32 offset, int reg) {
|
||||
inline void UnwindInfo::saveReg(u32 offset, int reg, int stackOffset) {
|
||||
}
|
||||
inline void UnwindInfo::saveExtReg(u32 offset, int reg, int stackOffset) {
|
||||
}
|
||||
inline void UnwindInfo::allocStack(u32 offset, int size) {
|
||||
}
|
||||
inline void UnwindInfo::endProlog(u32 offset) {
|
||||
}
|
||||
inline size_t UnwindInfo::end(u32 offset) {
|
||||
inline size_t UnwindInfo::end(u32 offset, ptrdiff_t rwRxOffset) {
|
||||
return 0;
|
||||
}
|
||||
inline void UnwindInfo::clear() {
|
||||
|
|
|
@ -16,15 +16,8 @@
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CORE_REC_ARM64_ARM64_REGALLOC_H_
|
||||
#define CORE_REC_ARM64_ARM64_REGALLOC_H_
|
||||
|
||||
#ifdef OLD_REGALLOC
|
||||
#include "hw/sh4/dyna/regalloc.h"
|
||||
#else
|
||||
#pragma once
|
||||
#include "hw/sh4/dyna/ssa_regalloc.h"
|
||||
#endif
|
||||
#include <aarch64/macro-assembler-aarch64.h>
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
|
@ -64,14 +57,9 @@ struct Arm64RegAlloc : RegAlloc<eReg, eFReg>
|
|||
return Register::GetWRegFromCode(ereg);
|
||||
}
|
||||
|
||||
const VRegister& MapVRegister(const shil_param& param, u32 index = 0)
|
||||
const VRegister& MapVRegister(const shil_param& param)
|
||||
{
|
||||
#ifdef OLD_REGALLOC
|
||||
eFReg ereg = mapfv(param, index);
|
||||
#else
|
||||
verify(index == 0);
|
||||
eFReg ereg = mapf(param);
|
||||
#endif
|
||||
if (ereg == (eFReg)-1)
|
||||
die("VRegister not allocated");
|
||||
return VRegister::GetSRegFromCode(ereg);
|
||||
|
@ -79,5 +67,3 @@ struct Arm64RegAlloc : RegAlloc<eReg, eFReg>
|
|||
|
||||
Arm64Assembler *assembler;
|
||||
};
|
||||
|
||||
#endif /* CORE_REC_ARM64_ARM64_REGALLOC_H_ */
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2021 flyinghead
|
||||
|
||||
This file is part of Flycast.
|
||||
|
||||
Flycast is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Flycast is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "oslib/oslib.h"
|
||||
#include <aarch64/macro-assembler-aarch64.h>
|
||||
using namespace vixl::aarch64;
|
||||
|
||||
class Arm64UnwindInfo : public UnwindInfo
|
||||
{
|
||||
public:
|
||||
void saveReg(u32 offset, const Register& reg, int stackOffset) {
|
||||
UnwindInfo::saveReg(offset, dwarfRegisterId(reg), stackOffset);
|
||||
}
|
||||
void saveReg(u32 offset, const VRegister& reg, int stackOffset) {
|
||||
UnwindInfo::saveExtReg(offset, dwarfRegisterId(reg), stackOffset);
|
||||
}
|
||||
|
||||
private:
|
||||
int dwarfRegisterId(const CPURegister& reg)
|
||||
{
|
||||
verify(reg.Is64Bits());
|
||||
if (reg.IsFPRegister())
|
||||
return reg.GetCode() + 64;
|
||||
else
|
||||
return reg.GetCode();
|
||||
}
|
||||
};
|
|
@ -39,6 +39,7 @@ using namespace vixl::aarch64;
|
|||
#include "hw/sh4/sh4_rom.h"
|
||||
#include "arm64_regalloc.h"
|
||||
#include "hw/mem/_vmem.h"
|
||||
#include "arm64_unwind.h"
|
||||
|
||||
#undef do_sqw_nommu
|
||||
|
||||
|
@ -55,6 +56,7 @@ struct DynaRBI : RuntimeBlockInfo
|
|||
|
||||
static u32 cycle_counter;
|
||||
static u64 jmp_stack;
|
||||
static Arm64UnwindInfo unwinder;
|
||||
|
||||
static void (*mainloop)(void *context);
|
||||
static void (*handleException)();
|
||||
|
@ -91,6 +93,7 @@ void ngen_init()
|
|||
|
||||
void ngen_ResetBlocks()
|
||||
{
|
||||
unwinder.clear();
|
||||
mainloop = nullptr;
|
||||
|
||||
if (p_sh4rcb->cntx.CpuRunning)
|
||||
|
@ -1334,18 +1337,43 @@ public:
|
|||
|
||||
// void mainloop(void *context)
|
||||
mainloop = (void (*)(void *))CC_RW2RX(GetCursorAddress<uintptr_t>());
|
||||
// For stack unwinding purposes, we pretend that the entire code block is just one function, with the same
|
||||
// unwinding instructions everywhere. This isn't true until the end of the following prolog, but exceptions
|
||||
// can only be thrown by called functions so this is good enough.
|
||||
unwinder.start(CodeCache);
|
||||
|
||||
// Save registers
|
||||
Stp(x19, x20, MemOperand(sp, -160, PreIndex));
|
||||
unwinder.allocStack(0, 160);
|
||||
unwinder.saveReg(0, x19, 160);
|
||||
unwinder.saveReg(0, x20, 152);
|
||||
Stp(x21, x22, MemOperand(sp, 16));
|
||||
unwinder.saveReg(0, x21, 144);
|
||||
unwinder.saveReg(0, x22, 136);
|
||||
Stp(x23, x24, MemOperand(sp, 32));
|
||||
unwinder.saveReg(0, x23, 128);
|
||||
unwinder.saveReg(0, x24, 120);
|
||||
Stp(x25, x26, MemOperand(sp, 48));
|
||||
unwinder.saveReg(0, x25, 112);
|
||||
unwinder.saveReg(0, x26, 104);
|
||||
Stp(x27, x28, MemOperand(sp, 64));
|
||||
Stp(s14, s15, MemOperand(sp, 80));
|
||||
Stp(vixl::aarch64::s8, s9, MemOperand(sp, 96));
|
||||
Stp(s10, s11, MemOperand(sp, 112));
|
||||
Stp(s12, s13, MemOperand(sp, 128));
|
||||
unwinder.saveReg(0, x27, 96);
|
||||
unwinder.saveReg(0, x28, 88);
|
||||
Stp(d14, d15, MemOperand(sp, 80));
|
||||
unwinder.saveReg(0, d14, 80);
|
||||
unwinder.saveReg(0, d15, 72);
|
||||
Stp(d8, d9, MemOperand(sp, 96));
|
||||
unwinder.saveReg(0, d8, 64);
|
||||
unwinder.saveReg(0, d9, 56);
|
||||
Stp(d10, d11, MemOperand(sp, 112));
|
||||
unwinder.saveReg(0, d10, 48);
|
||||
unwinder.saveReg(0, d11, 40);
|
||||
Stp(d12, d13, MemOperand(sp, 128));
|
||||
unwinder.saveReg(0, d12, 32);
|
||||
unwinder.saveReg(0, d13, 24);
|
||||
Stp(x29, x30, MemOperand(sp, 144));
|
||||
unwinder.saveReg(0, x29, 16);
|
||||
unwinder.saveReg(0, x30, 8);
|
||||
|
||||
Sub(x0, x0, sizeof(Sh4Context));
|
||||
Label reenterLabel;
|
||||
|
@ -1354,6 +1382,7 @@ public:
|
|||
Ldr(x1, reinterpret_cast<uintptr_t>(&cycle_counter));
|
||||
// Push context, cycle_counter address
|
||||
Stp(x0, x1, MemOperand(sp, -16, PreIndex));
|
||||
unwinder.allocStack(0, 16);
|
||||
Mov(w0, SH4_TIMESLICE);
|
||||
Str(w0, MemOperand(x1));
|
||||
|
||||
|
@ -1412,10 +1441,10 @@ public:
|
|||
Add(sp, sp, 16);
|
||||
// Restore registers
|
||||
Ldp(x29, x30, MemOperand(sp, 144));
|
||||
Ldp(s12, s13, MemOperand(sp, 128));
|
||||
Ldp(s10, s11, MemOperand(sp, 112));
|
||||
Ldp(vixl::aarch64::s8, s9, MemOperand(sp, 96));
|
||||
Ldp(s14, s15, MemOperand(sp, 80));
|
||||
Ldp(d12, d13, MemOperand(sp, 128));
|
||||
Ldp(d10, d11, MemOperand(sp, 112));
|
||||
Ldp(d8, d9, MemOperand(sp, 96));
|
||||
Ldp(d14, d15, MemOperand(sp, 80));
|
||||
Ldp(x27, x28, MemOperand(sp, 64));
|
||||
Ldp(x25, x26, MemOperand(sp, 48));
|
||||
Ldp(x23, x24, MemOperand(sp, 32));
|
||||
|
@ -1489,6 +1518,9 @@ public:
|
|||
FinalizeCode();
|
||||
emit_Skip(GetBuffer()->GetSizeInBytes());
|
||||
|
||||
size_t unwindSize = unwinder.end(CODE_SIZE - 128, (ptrdiff_t)CC_RW2RX(0));
|
||||
verify(unwindSize <= 128);
|
||||
|
||||
arm64_no_update = GetLabelAddress<DynaCode *>(&no_update);
|
||||
handleException = (void (*)())CC_RW2RX(GetLabelAddress<uintptr_t>(&handleExceptionLabel));
|
||||
writeStoreQueue32 = GetLabelAddress<DynaCode *>(&writeStoreQueue32Label);
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include "hw/sh4/sh4_core.h"
|
||||
#include "hw/sh4/dyna/ngen.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/sh4/dyna/regalloc.h"
|
||||
|
||||
#define SHIL_MODE 2
|
||||
#include "hw/sh4/dyna/shil_canonical.h"
|
||||
|
|
|
@ -18,14 +18,8 @@
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
//#define OLD_REGALLOC
|
||||
|
||||
#include <xbyak/xbyak.h>
|
||||
#ifdef OLD_REGALLOC
|
||||
#include "hw/sh4/dyna/regalloc.h"
|
||||
#else
|
||||
#include "hw/sh4/dyna/ssa_regalloc.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
static Xbyak::Operand::Code alloc_regs[] = { Xbyak::Operand::RBX, Xbyak::Operand::RBP, Xbyak::Operand::RDI, Xbyak::Operand::RSI,
|
||||
|
@ -61,13 +55,9 @@ struct X64RegAlloc : RegAlloc<Xbyak::Operand::Code, s8>
|
|||
return Xbyak::Reg32(ereg);
|
||||
}
|
||||
|
||||
Xbyak::Xmm MapXRegister(const shil_param& param, u32 index = 0)
|
||||
Xbyak::Xmm MapXRegister(const shil_param& param)
|
||||
{
|
||||
#ifdef OLD_REGALLOC
|
||||
s8 ereg = mapfv(param, index);
|
||||
#else
|
||||
s8 ereg = mapf(param);
|
||||
#endif
|
||||
if (ereg == -1)
|
||||
die("VRegister not allocated");
|
||||
return Xbyak::Xmm(ereg);
|
||||
|
@ -75,16 +65,7 @@ struct X64RegAlloc : RegAlloc<Xbyak::Operand::Code, s8>
|
|||
|
||||
bool IsMapped(const Xbyak::Xmm &xmm, size_t opid)
|
||||
{
|
||||
#ifndef OLD_REGALLOC
|
||||
return regf_used((s8)xmm.getIdx());
|
||||
#else
|
||||
for (size_t sid = 0; sid < all_spans.size(); sid++)
|
||||
{
|
||||
if (all_spans[sid]->nregf == xmm.getIdx() && all_spans[sid]->contains(opid))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
BlockCompiler *compiler;
|
||||
|
|
|
@ -17,14 +17,7 @@
|
|||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
//#define OLD_REGALLOC
|
||||
|
||||
#ifdef OLD_REGALLOC
|
||||
#include "hw/sh4/dyna/regalloc.h"
|
||||
#else
|
||||
#include "hw/sh4/dyna/ssa_regalloc.h"
|
||||
#endif
|
||||
|
||||
class X86Compiler;
|
||||
|
||||
|
@ -47,13 +40,9 @@ struct X86RegAlloc : RegAlloc<Xbyak::Operand::Code, s8>
|
|||
return Xbyak::Reg32(ereg);
|
||||
}
|
||||
|
||||
Xbyak::Xmm MapXRegister(const shil_param& param, u32 index = 0)
|
||||
Xbyak::Xmm MapXRegister(const shil_param& param)
|
||||
{
|
||||
#ifdef OLD_REGALLOC
|
||||
s8 ereg = mapfv(param, index);
|
||||
#else
|
||||
s8 ereg = mapf(param);
|
||||
#endif
|
||||
if (ereg == -1)
|
||||
die("VRegister not allocated");
|
||||
return Xbyak::Xmm(ereg);
|
||||
|
@ -61,16 +50,7 @@ struct X86RegAlloc : RegAlloc<Xbyak::Operand::Code, s8>
|
|||
|
||||
bool IsMapped(const Xbyak::Xmm &xmm, size_t opid)
|
||||
{
|
||||
#ifndef OLD_REGALLOC
|
||||
return regf_used((s8)xmm.getIdx());
|
||||
#else
|
||||
for (size_t sid = 0; sid < all_spans.size(); sid++)
|
||||
{
|
||||
if (all_spans[sid]->nregf == xmm.getIdx() && all_spans[sid]->contains(opid))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
X86Compiler *compiler;
|
||||
|
|
|
@ -349,6 +349,7 @@ void do_swap_automation()
|
|||
glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo);
|
||||
glReadPixels(0, 0, gl.ofbo.width, gl.ofbo.height, GL_RGB, GL_UNSIGNED_BYTE, img);
|
||||
fwrite(img, 1, bytesz, video_file);
|
||||
delete[] img;
|
||||
fflush(video_file);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,12 +46,6 @@ void UnwindInfo::pushReg(u32 offset, int reg)
|
|||
codes.push_back(offset | (UWOP_PUSH_NONVOL << 8) | (reg << 12));
|
||||
}
|
||||
|
||||
void UnwindInfo::pushFPReg(u32 offset, int reg)
|
||||
{
|
||||
//codes.push_back(offset | (UWOP_SAVE_XMM128 << 8) | (reg << 12));
|
||||
die("not implemented");
|
||||
}
|
||||
|
||||
void UnwindInfo::allocStack(u32 offset, int size)
|
||||
{
|
||||
verify(size <= 128);
|
||||
|
@ -70,7 +64,7 @@ void UnwindInfo::endProlog(u32 offset)
|
|||
codes.push_back(0);
|
||||
}
|
||||
|
||||
size_t UnwindInfo::end(u32 offset)
|
||||
size_t UnwindInfo::end(u32 offset, ptrdiff_t rwRxOffset)
|
||||
{
|
||||
u8 *endAddr = startAddr + offset;
|
||||
if ((uintptr_t)endAddr & 3)
|
||||
|
|
|
@ -36,7 +36,7 @@ android {
|
|||
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DANDROID_STL=c++_shared", "-DANDROID_ARM_MODE=arm"
|
||||
arguments "-DANDROID_ARM_MODE=arm"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue