summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrew Lee <anwlee@google.com>2015-06-01 14:03:39 -0700
committerAndrew Lee <anwlee@google.com>2015-06-01 14:03:39 -0700
commit9c9958ca6c51676cab549d19311df59a1b4ccf1a (patch)
treeab29a0261224917a421cd590e9a7e935b166d25d
parentebb6c5111f287a8cf55f1dcac7d7c5b76d9260ff (diff)
downloadandroid_frameworks_ex-9c9958ca6c51676cab549d19311df59a1b4ccf1a.tar.gz
android_frameworks_ex-9c9958ca6c51676cab549d19311df59a1b4ccf1a.tar.bz2
android_frameworks_ex-9c9958ca6c51676cab549d19311df59a1b4ccf1a.zip
Remove variable-speed library.
Bug: 21444991 Change-Id: I3e1fc7f0773ddfa227cde6b5161102a087f47702
-rw-r--r--variablespeed/Android.mk40
-rw-r--r--variablespeed/jni/Android.mk44
-rw-r--r--variablespeed/jni/decode_buffer.cc86
-rw-r--r--variablespeed/jni/decode_buffer.h59
-rw-r--r--variablespeed/jni/hlogging.h32
-rw-r--r--variablespeed/jni/integral_types.h33
-rw-r--r--variablespeed/jni/jni_entry.cc96
-rw-r--r--variablespeed/jni/macros.h66
-rw-r--r--variablespeed/jni/no_synchronization.h50
-rw-r--r--variablespeed/jni/profile_timer.h52
-rw-r--r--variablespeed/jni/ring_buffer.cc154
-rw-r--r--variablespeed/jni/ring_buffer.h117
-rw-r--r--variablespeed/jni/sola_time_scaler.cc366
-rw-r--r--variablespeed/jni/sola_time_scaler.h165
-rw-r--r--variablespeed/jni/variablespeed.cc864
-rw-r--r--variablespeed/jni/variablespeed.h167
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/EngineParameters.java158
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java68
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java50
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java115
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java410
-rw-r--r--variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java92
-rw-r--r--variablespeed/tests/Android.mk28
-rw-r--r--variablespeed/tests/AndroidManifest.xml33
-rw-r--r--variablespeed/tests/assets/README.txt4
-rw-r--r--variablespeed/tests/assets/count_and_test.3gppbin7405 -> 0 bytes
-rw-r--r--variablespeed/tests/assets/fake_voicemail.mp3bin20817 -> 0 bytes
-rw-r--r--variablespeed/tests/assets/fake_voicemail2.mp3bin26336 -> 0 bytes
-rw-r--r--variablespeed/tests/assets/quick_test_recording.mp3bin30591 -> 0 bytes
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java59
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java67
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java62
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java581
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java44
-rw-r--r--variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java47
35 files changed, 0 insertions, 4209 deletions
diff --git a/variablespeed/Android.mk b/variablespeed/Android.mk
deleted file mode 100644
index d5acbe0..0000000
--- a/variablespeed/Android.mk
+++ /dev/null
@@ -1,40 +0,0 @@
-# Copyright (C) 2011 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.
-
-LOCAL_PATH := $(call my-dir)
-
-# Build the java code, shared library.
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_MODULE := android-ex-variablespeed
-
-LOCAL_SDK_VERSION := current
-
-LOCAL_STATIC_JAVA_LIBRARIES := \
- android-common \
- guava \
-
-LOCAL_SRC_FILES := \
- $(call all-java-files-under, src)
-
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
-# Build the native code, library.
-
-include $(CLEAR_VARS)
-
-include $(LOCAL_PATH)/jni/Android.mk
diff --git a/variablespeed/jni/Android.mk b/variablespeed/jni/Android.mk
deleted file mode 100644
index 98b9325..0000000
--- a/variablespeed/jni/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-# Copyright (C) 2011 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.
-#
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-# TODO: Remove the .cc extension, just .cpp.
-
-# Add in extra warnings.
-LOCAL_CFLAGS += -Wall
-LOCAL_CPPFLAGS += -Wall
-
-LOCAL_CPP_EXTENSION := .cc
-LOCAL_MODULE := libvariablespeed
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
- variablespeed.cc \
- ring_buffer.cc \
- sola_time_scaler.cc \
- jni_entry.cc \
- decode_buffer.cc \
-
-LOCAL_C_INCLUDES := \
- $(call include-path-for, wilhelm) \
-
-LOCAL_SHARED_LIBRARIES := \
- libOpenSLES \
- liblog \
-
-LOCAL_CLANG := true
-include $(BUILD_SHARED_LIBRARY)
diff --git a/variablespeed/jni/decode_buffer.cc b/variablespeed/jni/decode_buffer.cc
deleted file mode 100644
index a08cfd6..0000000
--- a/variablespeed/jni/decode_buffer.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <decode_buffer.h>
-
-namespace {
-
-static const size_t kNumberOfBytesPerSample = 2;
-
-} // namespace
-
-DecodeBuffer::DecodeBuffer(size_t sizeOfOneBuffer, size_t maxSize)
- : sizeOfOneBuffer_(sizeOfOneBuffer), maxSize_(maxSize),
- start_(0), end_(0), advancedCount_(0), data_() {
- Clear();
-}
-
-DecodeBuffer::~DecodeBuffer() {
- Clear();
-}
-
-size_t DecodeBuffer::GetSizeInBytes() const {
- return kNumberOfBytesPerSample * (end_ - start_);
-}
-
-bool DecodeBuffer::IsTooLarge() const {
- return GetSizeInBytes() > maxSize_;
-}
-
-void DecodeBuffer::AddData(int8_t* pointer, size_t lengthInBytes) {
- for (size_t i = 0; i < lengthInBytes / kNumberOfBytesPerSample; ++i) {
- PushValue(reinterpret_cast<int16*>(pointer)[i]);
- }
-}
-
-void DecodeBuffer::Clear() {
- while (data_.size() > 0) {
- delete[] data_.front();
- data_.erase(data_.begin());
- }
- start_ = 0;
- end_ = 0;
- advancedCount_ = 0;
-}
-
-size_t DecodeBuffer::GetTotalAdvancedCount() const {
- return advancedCount_;
-}
-
-void DecodeBuffer::AdvanceHeadPointerShorts(size_t numberOfShorts) {
- start_ += numberOfShorts;
- while (start_ > sizeOfOneBuffer_) {
- data_.push_back(data_.front());
- data_.erase(data_.begin());
- start_ -= sizeOfOneBuffer_;
- end_ -= sizeOfOneBuffer_;
- }
- advancedCount_ += numberOfShorts;
-}
-
-void DecodeBuffer::PushValue(int16 value) {
- size_t bufferIndex = end_ / sizeOfOneBuffer_;
- if (bufferIndex >= data_.size()) {
- data_.push_back(new int16[sizeOfOneBuffer_]);
- }
- data_.at(bufferIndex)[end_ % sizeOfOneBuffer_] = value;
- ++end_;
-}
-
-int16 DecodeBuffer::GetAtIndex(size_t index) {
- return data_.at((start_ + index) / sizeOfOneBuffer_)
- [(start_ + index) % sizeOfOneBuffer_];
-}
diff --git a/variablespeed/jni/decode_buffer.h b/variablespeed/jni/decode_buffer.h
deleted file mode 100644
index 2f1aa71..0000000
--- a/variablespeed/jni/decode_buffer.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_
-
-#include <integral_types.h>
-#include <macros.h>
-#include <stdlib.h>
-#include <vector>
-
-// DecodeBuffer is used to store arrays of int16 values for audio.
-//
-// This class is not thread-safe. You should provide your own
-// synchronization if you wish to use it from multiple threads.
-class DecodeBuffer {
- public:
- DecodeBuffer(size_t sizeOfOneBuffer, size_t maxSize);
- virtual ~DecodeBuffer();
- size_t GetSizeInBytes() const;
- void AddData(int8_t* pointer, size_t lengthInBytes);
- void Clear();
- void AdvanceHeadPointerShorts(size_t numberOfShorts);
- int16 GetAtIndex(size_t index);
- bool IsTooLarge() const;
- size_t GetTotalAdvancedCount() const;
-
- private:
- void PushValue(int16 value);
-
- size_t sizeOfOneBuffer_;
- size_t maxSize_;
- size_t start_;
- size_t end_;
- size_t advancedCount_;
- // This vector isn't ideal because we perform a number of queue-like
- // operations: namely removing from the front and appending at the back.
- // However we also need constant-time access to the elements of this
- // vector, and therefore it's not good enough to use a std::queue.
- // In practice this data structure choice doesn't seem to be a bottleneck.
- std::vector<int16*> data_;
-
- DISALLOW_COPY_AND_ASSIGN(DecodeBuffer);
-};
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_DECODE_BUFFER_H_
diff --git a/variablespeed/jni/hlogging.h b/variablespeed/jni/hlogging.h
deleted file mode 100644
index 056f5a3..0000000
--- a/variablespeed/jni/hlogging.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_
-
-#include <integral_types.h>
-#include <macros.h>
-#include <android/log.h>
-
-// Simple logging macros.
-#define LOG_TAG "VariableSpeed"
-#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
-#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
-#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
-#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_HLOGGING_H_
diff --git a/variablespeed/jni/integral_types.h b/variablespeed/jni/integral_types.h
deleted file mode 100644
index 8927d7c..0000000
--- a/variablespeed/jni/integral_types.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_
-
-#include <cstring> // for size_t
-
-// Standard typedefs
-typedef signed char schar;
-typedef signed char int8;
-typedef short int16; // NOLINT
-typedef int int32;
-typedef long long int64; // NOLINT
-typedef unsigned char uint8;
-typedef unsigned short uint16; // NOLINT
-typedef unsigned int uint32;
-typedef unsigned long long uint64; // NOLINT
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_INTEGRAL_TYPES_H_
diff --git a/variablespeed/jni/jni_entry.cc b/variablespeed/jni/jni_entry.cc
deleted file mode 100644
index 20bcf3c..0000000
--- a/variablespeed/jni/jni_entry.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include <jni.h>
-#include <variablespeed.h>
-
-// Quick #define to make sure I get all the JNI method calls right.
-#define JNI_METHOD(x, y) \
-JNIEXPORT y JNICALL \
-Java_com_android_ex_variablespeed_VariableSpeedNative_##x
-
-class MethodLog {
- public:
- explicit MethodLog(const char* name) : name_(name) {
- LOGV("+ %s", name);
- }
- virtual ~MethodLog() {
- LOGV("- %s", name_);
- }
-
- private:
- const char* name_;
-};
-
-extern "C" {
-JNI_METHOD(playFileDescriptor, void) (JNIEnv*, jclass, jint fd, jlong offset,
- jlong length) {
- MethodLog _("playFileDescriptor");
- AudioEngine::GetEngine()->PlayFileDescriptor(fd, offset, length);
-}
-
-JNI_METHOD(playUri, void) (JNIEnv* env, jclass, jstring uri) {
- MethodLog _("playUri");
- const char* utf8 = env->GetStringUTFChars(uri, NULL);
- CHECK(NULL != utf8);
- AudioEngine::GetEngine()->PlayUri(utf8);
-}
-
-JNI_METHOD(setVariableSpeed, void) (JNIEnv*, jclass, jfloat speed) {
- MethodLog _("setVariableSpeed");
- AudioEngine::GetEngine()->SetVariableSpeed(speed);
-}
-
-JNI_METHOD(startPlayback, void) (JNIEnv*, jclass) {
- MethodLog _("startPlayback");
- AudioEngine::GetEngine()->RequestStart();
-}
-
-JNI_METHOD(stopPlayback, void) (JNIEnv*, jclass) {
- MethodLog _("stopPlayback");
- AudioEngine::GetEngine()->RequestStop();
-}
-
-JNI_METHOD(getCurrentPosition, jint) (JNIEnv*, jclass) {
- return AudioEngine::GetEngine()->GetCurrentPosition();
-}
-
-JNI_METHOD(getTotalDuration, jint) (JNIEnv*, jclass) {
- return AudioEngine::GetEngine()->GetTotalDuration();
-}
-
-JNI_METHOD(initializeEngine, void) (JNIEnv*, jclass,
- jint targetFrames, jfloat windowDuration,
- jfloat windowOverlapDuration, jint maxPlayBufferCount,
- jfloat initialRate, jint decodeInitialSize, jint decodeMaxSize,
- jint startPositionMillis, jint audioStreamType) {
- MethodLog _("initializeEngine");
- AudioEngine *engine = new AudioEngine(targetFrames,
- windowDuration, windowOverlapDuration, maxPlayBufferCount, initialRate,
- decodeInitialSize, decodeMaxSize, startPositionMillis, audioStreamType);
- if (!AudioEngine::CompareAndSetEngine(NULL, engine)) {
- delete engine;
- }
-}
-
-JNI_METHOD(shutdownEngine, void) (JNIEnv*, jclass) {
- MethodLog _("shutdownEngine");
- AudioEngine::DeleteEngine();
-}
-} // extern "C"
diff --git a/variablespeed/jni/macros.h b/variablespeed/jni/macros.h
deleted file mode 100644
index c08d44b..0000000
--- a/variablespeed/jni/macros.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_
-
-#include <hlogging.h>
-
-inline float min(float a, float b) {
- return (a < b) ? a : b;
-}
-
-inline float max(float a, float b) {
- return (a > b) ? a : b;
-}
-
-template <class ForwardIterator>
- ForwardIterator min_element(ForwardIterator first, ForwardIterator last) {
- ForwardIterator lowest = first;
- if (first == last) return last;
- while (++first != last)
- if (*first < *lowest)
- lowest = first;
- return lowest;
-}
-
-// A macro to disallow the copy constructor and operator= functions
-// This should be used in the private: declarations for a class
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
- TypeName(const TypeName&); \
- void operator=(const TypeName&)
-
-#define CHECK(x) { \
- if (!(x)) { \
- LOGE("assertion failed: " #x); \
- LOGE("file: %s line: %d", __FILE__, __LINE__); \
- int* frob = NULL; \
- *frob = 5; \
- } \
-}
-
-template <class Dest, class Source>
-inline Dest bit_cast(const Source& source) {
- // Compile time assertion: sizeof(Dest) == sizeof(Source)
- // A compile error here means your Dest and Source have different sizes.
- typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1]; // NOLINT
-
- Dest dest;
- memcpy(&dest, &source, sizeof(dest));
- return dest;
-}
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_MACROS_H_
diff --git a/variablespeed/jni/no_synchronization.h b/variablespeed/jni/no_synchronization.h
deleted file mode 100644
index 751ebd9..0000000
--- a/variablespeed/jni/no_synchronization.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_
-
-#include <macros.h>
-
-// We don't need any synchronization at the moment.
-// The sola_time_scaler (which is the code that uses this mutex class) is
-// currently being used in a single-threaded manner, driven from the main
-// PlayFromThisSource method in variablespeed.
-// As such no locking is actually required, and so this class contains a
-// fake mutex that does nothing.
-
-class Mutex {
- public:
- Mutex() {}
- virtual ~Mutex() {}
- void Lock() {}
- void Unlock() {}
-
- private:
- DISALLOW_COPY_AND_ASSIGN(Mutex);
-};
-
-class MutexLock {
- public:
- explicit MutexLock(Mutex* mu) : mu_(mu) {}
- virtual ~MutexLock() {}
-
- private:
- Mutex* const mu_;
- DISALLOW_COPY_AND_ASSIGN(MutexLock);
-};
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_NO_SYNCHRONIZATION_H_
diff --git a/variablespeed/jni/profile_timer.h b/variablespeed/jni/profile_timer.h
deleted file mode 100644
index 6844cb2..0000000
--- a/variablespeed/jni/profile_timer.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_
-
-#include <hlogging.h>
-#include <time.h>
-
-#include <string>
-
-// Simple profiler for debugging method call duration.
-class Timer {
- public:
- Timer() : startTime_(clock()) {
- }
-
- virtual ~Timer() {
- PrintElapsed("destructor");
- }
-
- void PrintElapsed(const char* message) {
- clock_t endTime(clock());
- LOGD("Timer(%s): %d ms", message,
- static_cast<int>((endTime - startTime_) * 1000 / CLOCKS_PER_SEC));
- }
-
- size_t GetElapsed() {
- clock_t endTime(clock());
- return (endTime - startTime_) * 1000 / CLOCKS_PER_SEC;
- }
-
- private:
- clock_t startTime_;
-
- DISALLOW_COPY_AND_ASSIGN(Timer);
-};
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_PROFILE_TIMER_H_
diff --git a/variablespeed/jni/ring_buffer.cc b/variablespeed/jni/ring_buffer.cc
deleted file mode 100644
index 61c1490..0000000
--- a/variablespeed/jni/ring_buffer.cc
+++ /dev/null
@@ -1,154 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "ring_buffer.h"
-
-#include <algorithm>
-
-#include "integral_types.h"
-
-namespace video_editing {
-
-void RingBuffer::Init(int size, int num_channels, int num_readers) {
- size_ = size;
- num_channels_ = num_channels;
- num_readers_ = num_readers;
- temp_read_buffer_size_ = 1024;
- initialized_ = true;
- Reset();
-}
-
-RingBuffer::RingBuffer()
- : initialized_(false), samples_(NULL),
- num_readers_(0), temp_read_buffer_(NULL) {
-}
-
-RingBuffer::~RingBuffer() {
- delete[] samples_;
- delete[] temp_read_buffer_;
-}
-
-void RingBuffer::Reset() {
- delete[] samples_;
- samples_ = new float[size_ * num_channels_];
- memset(samples_, 0,
- size_ * num_channels_ * sizeof(samples_[0]));
-
- temp_read_buffer_size_ = 1024;
- delete[] temp_read_buffer_;
- temp_read_buffer_ = new float[temp_read_buffer_size_ * num_channels_];
- memset(temp_read_buffer_, 0,
- temp_read_buffer_size_ * num_channels_ * sizeof(samples_[0]));
- readers_.clear();
- for (int i = 0; i < num_readers_; ++i) {
- readers_.push_back(0LL);
- }
- head_logical_ = 0LL;
- head_ = 0;
-}
-
-int RingBuffer::available(int reader) const {
- return head_logical_ - readers_[reader];
-}
-
-int RingBuffer::overhead() const {
- int64 tail = GetTail();
- return tail + size_ - head_logical_;
-}
-
-int64 RingBuffer::GetTail() const {
- return *std::min_element(readers_.begin(), readers_.end());
-}
-
-int64 RingBuffer::Tell(int reader) const {
- return readers_[reader];
-}
-
-void RingBuffer::Seek(int reader, int64 position) {
- readers_[reader] = position;
-}
-
-void RingBuffer::Write(const float* samples, int num_frames) {
- if (!num_frames) {
- return;
- }
- if (head_ + num_frames <= size_) {
- memcpy(samples_ + head_ * num_channels_, samples,
- num_frames * num_channels_ * sizeof(samples[0]));
- head_ += num_frames;
- } else {
- int overhead = size_ - head_;
- memcpy(samples_ + head_ * num_channels_, samples,
- num_channels_ * overhead * sizeof(samples[0]));
- head_ = num_frames - overhead;
- memcpy(samples_, samples + overhead * num_channels_,
- num_channels_ * head_ * sizeof(samples[0]));
- }
- head_logical_ += num_frames;
-}
-
-void RingBuffer::Copy(int reader, float* destination, int num_frames) const {
- int pos = Tell(reader) % size_;
- if (pos + num_frames <= size_) {
- memcpy(destination, samples_ + pos * num_channels_,
- num_channels_ * num_frames * sizeof(destination[0]));
- } else {
- int wrapped = size_ - pos;
- memcpy(destination, samples_ + pos * num_channels_,
- num_channels_ * wrapped * sizeof(destination[0]));
- int remaining = num_frames - wrapped;
- memcpy(destination + wrapped * num_channels_, samples_,
- num_channels_ * remaining * sizeof(destination[0]));
- }
-}
-
-float* RingBuffer::GetPointer(int reader, int num_frames) {
- int pos = Tell(reader) % size_;
- if (pos + num_frames <= size_) {
- return samples_ + pos * num_channels_;
- } else {
- if (num_frames > temp_read_buffer_size_) {
- temp_read_buffer_size_ = num_frames;
- delete[] temp_read_buffer_;
- temp_read_buffer_ =
- new float[temp_read_buffer_size_ * num_channels_]; // NOLINT
- }
- Copy(reader, temp_read_buffer_, num_frames);
- return temp_read_buffer_;
- }
-}
-
-void RingBuffer::MergeBack(int reader, const float* source, int num_frames) {
- // If the source pointer is not the temporary buffer,
- // data updates were performed in place, so there is nothing to do.
- // Otherwise, copy samples from the temp buffer back to the ring buffer.
- if (source == temp_read_buffer_) {
- int pos = Tell(reader) % size_;
- if (pos + num_frames <= size_) {
- memcpy(samples_ + (pos * num_channels_), source,
- num_channels_ * num_frames * sizeof(source[0]));
- } else {
- int wrapped = size_ - pos;
- memcpy(samples_ + (pos * num_channels_), source,
- num_channels_ * wrapped * sizeof(source[0]));
- int remaining = num_frames - wrapped;
- memcpy(samples_, source + (wrapped * num_channels_),
- num_channels_ * remaining * sizeof(source[0]));
- }
- }
-}
-
-} // namespace video_editing
diff --git a/variablespeed/jni/ring_buffer.h b/variablespeed/jni/ring_buffer.h
deleted file mode 100644
index 8afe436..0000000
--- a/variablespeed/jni/ring_buffer.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_
-
-#include <vector>
-
-#include "integral_types.h"
-#include "macros.h"
-
-// Circular buffer of multichannel audio, maintaining the position of the
-// writing head and several reading head, and providing a method for
-// accessing a contiguous block (through direct reference or the copy in a
-// temporary read buffer in case the requested block crosses the buffer
-// boundaries).
-//
-// This code is not thread-safe.
-
-namespace video_editing {
-
-class RingBuffer {
- public:
- RingBuffer();
- virtual ~RingBuffer();
-
- // Initializes a RingBuffer.
- // @param size: size of the buffer in frames.
- // @param num_channels: number of channels of the original audio.
- // @param num_readers: number of reading heads.
- void Init(int size, int num_channels, int num_readers);
-
- // Gets the position of a reading head.
- // @param reader reading head index.
- // @returns position pointed to by the #reader reading head.
- int64 Tell(int reader) const;
-
- // Moves a reading head.
- // @param reader reading head index.
- // @param position target position.
- void Seek(int reader, int64 position);
-
- // Reads samples for a reading head.
- // @param reader reading head index.
- // @param num_frames number of frames to read.
- // @param destination float buffer to which the samples will be written.
- void Copy(int reader, float* destination, int num_frames) const;
-
- // Writes samples.
- // @param samples float buffer containing the samples.
- // @param num_frames number of frames to write.
- void Write(const float* samples, int num_frames);
-
- // Flushes the content of the buffer and reset the position of the heads.
- void Reset();
-
- // Returns the number of frames we can still write.
- int overhead() const;
-
- // Returns the number of frames we can read for a given reader.
- // @param reader reading head index.
- int available(int reader) const;
-
- // Returns a pointer to num_frames x num_channels contiguous samples for
- // a given reader. In most cases this directly returns a pointer to the
- // data in the ring buffer. However, if the required block wraps around the
- // boundaries of the ring buffer, the data is copied to a temporary buffer
- // owned by the RingBuffer object.
- // @param reader reading head index.
- // @param num_frames number of frames to read.
- // @returns pointer to a continuous buffer containing num_frames.
- float* GetPointer(int reader, int num_frames);
-
- // Merges updated data back into the ring buffer, if it was updated in
- // the temporary buffer. This operation follows a GetPointer() that
- // was used to obtain data to rewrite. The buffer address supplied
- // here must match the one returned by GetPointer().
- // @param reader reading head index.
- // @param source pointer to a continuous buffer containing num_frames.
- // @param num_frames number of frames to copy back to the ring buffer.
- void MergeBack(int reader, const float* source, int num_frames);
-
- private:
- // Returns the position of the laziest reader.
- int64 GetTail() const;
-
- bool initialized_;
- float* samples_;
- std::vector<int64> readers_;
- int size_;
- int num_channels_;
- int num_readers_;
- int64 head_logical_;
- int head_;
-
- float* temp_read_buffer_;
- int temp_read_buffer_size_;
-
- DISALLOW_COPY_AND_ASSIGN(RingBuffer);
-};
-
-} // namespace video_editing
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_RING_BUFFER_H_
diff --git a/variablespeed/jni/sola_time_scaler.cc b/variablespeed/jni/sola_time_scaler.cc
deleted file mode 100644
index 1ff1236..0000000
--- a/variablespeed/jni/sola_time_scaler.cc
+++ /dev/null
@@ -1,366 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "sola_time_scaler.h"
-
-#include <math.h>
-#include <hlogging.h>
-#include <algorithm>
-
-#include "ring_buffer.h"
-
-#define FLAGS_sola_ring_buffer 2.0
-#define FLAGS_sola_enable_correlation true
-
-
-namespace video_editing {
-
-// Returns a cross-correlation score for the specified buffers.
-int SolaAnalyzer::Correlate(const float* buffer1, const float* buffer2,
- int num_frames) {
- CHECK(initialized_);
-
- int score = 0;
- num_frames *= num_channels_;
- while (num_frames-- > 0) {
- // Increment the score if the sign bits match.
- score += ((bit_cast<int32>(*buffer1++) ^ bit_cast<int32>(*buffer2++)) >= 0)
- ? 1 : 0;
- }
- return score;
-}
-
-// Trivial SolaAnalyzer class to bypass correlation.
-class SolaBypassAnalyzer : public SolaAnalyzer {
- public:
- SolaBypassAnalyzer() { }
- virtual int Correlate(const float*, const float*, int num_frames) {
- return num_frames * num_channels_;
- }
-};
-
-
-// Default constructor.
-SolaTimeScaler::SolaTimeScaler()
- : input_buffer_(NULL), output_buffer_(NULL), analyzer_(NULL) {
- sample_rate_ = 0;
- num_channels_ = 0;
-
- draining_ = false;
- initialized_ = false;
-}
-
-SolaTimeScaler::~SolaTimeScaler() {
- delete input_buffer_;
- delete output_buffer_;
- delete analyzer_;
-}
-
-// Injects a SolaAnalyzer instance for analyzing signal frames.
-void SolaTimeScaler::set_analyzer(SolaAnalyzer* analyzer) {
- MutexLock lock(&mutex_); // lock out processing while updating
- delete analyzer_;
- analyzer_ = analyzer;
-}
-
-// Initializes a SOLA timescaler.
-void SolaTimeScaler::Init(double sample_rate,
- int num_channels,
- double initial_speed,
- double window_duration,
- double overlap_duration) {
- MutexLock lock(&mutex_); // lock out processing while updating
-
- sample_rate_ = sample_rate;
- num_channels_ = num_channels;
- speed_ = initial_speed;
- window_duration_ = window_duration;
- overlap_duration_ = overlap_duration;
-
- initialized_ = true;
- GenerateParameters();
- Reset();
-}
-
-// Adjusts the rate scaling factor.
-void SolaTimeScaler::set_speed(double speed) {
- MutexLock lock(&mutex_); // lock out processing while updating
-
- speed_ = speed;
- GenerateParameters();
-}
-
-// Generates processing parameters from the current settings.
-void SolaTimeScaler::GenerateParameters() {
- if (speed_ < 0.1) {
- LOGE("Requested speed %fx limited to 0.1x", speed_);
- speed_ = 0.1;
- } else if (speed_ > 8.0) {
- LOGE("Requested speed %fx limited to 8.0x", speed_);
- speed_ = 8.0;
- }
-
- ratio_ = 1.0 / speed_;
-
- num_window_frames_ = nearbyint(sample_rate_ * window_duration_);
-
- // Limit the overlap to half the window size, and round up to an odd number.
- // Half of overlap window (rounded down) is also a useful number.
- overlap_duration_ = min(overlap_duration_, window_duration_ / 2.0);
- num_overlap_frames_ = nearbyint(sample_rate_ * overlap_duration_);
- num_overlap_frames_ |= 1;
- half_overlap_frames_ = num_overlap_frames_ >> 1;
-
- if (speed_ >= 1.) {
- // For compression (speed up), adjacent input windows overlap in the output.
- input_window_offset_ = num_window_frames_;
- target_merge_offset_ = nearbyint(num_window_frames_ * ratio_);
- } else {
- // For expansion (slow down), each input window start point overlaps the
- // previous, and they are placed adjacently in the output
- // (+/- half the overlap size).
- input_window_offset_ = nearbyint(num_window_frames_ * speed_);
- target_merge_offset_ = num_window_frames_;
- }
-
- // Make sure we copy enough extra data to be able to perform a
- // frame correlation over the range of target merge point +/- half overlap,
- // even when the previous merge point was adjusted backwards a half overlap.
- max_frames_to_merge_ = max(num_window_frames_,
- target_merge_offset_ + (2 * num_overlap_frames_));
- min_output_to_hold_=
- max_frames_to_merge_ + num_overlap_frames_ - target_merge_offset_;
-}
-
-// The input buffer has one writer and reader.
-// The output buffer has one reader/updater, and one reader/consumer.
-static const int kInputReader = 0;
-static const int kOutputAnalysis = 0;
-static const int kOutputConsumer = 1;
-
-void SolaTimeScaler::Reset() {
- CHECK(initialized_);
- double duration = max(FLAGS_sola_ring_buffer, 20. * window_duration_);
- draining_ = false;
-
- delete input_buffer_;
- input_buffer_ = new RingBuffer();
- input_buffer_->Init(static_cast<int>
- (sample_rate_ * duration), num_channels_, 1);
-
- delete output_buffer_;
- output_buffer_ = new RingBuffer();
- output_buffer_->Init(static_cast<int>
- (sample_rate_ * ratio_ * duration), num_channels_, 2);
-
- if (analyzer_ == NULL) {
- if (FLAGS_sola_enable_correlation) {
- analyzer_ = new SolaAnalyzer();
- } else {
- analyzer_ = new SolaBypassAnalyzer();
- }
- }
- analyzer_->Init(sample_rate_, num_channels_);
-}
-
-// Returns the number of frames that the input buffer can accept.
-int SolaTimeScaler::input_limit() const {
- CHECK(initialized_);
- return input_buffer_->overhead();
-}
-
-// Returns the number of available output frames.
-int SolaTimeScaler::available() {
- CHECK(initialized_);
-
- int available = output_buffer_->available(kOutputConsumer);
- if (available > min_output_to_hold_) {
- available -= min_output_to_hold_;
- } else if (draining_) {
- Process();
- available = output_buffer_->available(kOutputConsumer);
- if (available > min_output_to_hold_) {
- available -= min_output_to_hold_;
- }
- } else {
- available = 0;
- }
- return available;
-}
-
-void SolaTimeScaler::Drain() {
- CHECK(initialized_);
-
- draining_ = true;
-}
-
-
-// Feeds audio to the timescaler, and processes as much data as possible.
-int SolaTimeScaler::InjectSamples(float* buffer, int num_frames) {
- CHECK(initialized_);
-
- // Do not write more frames than the buffer can accept.
- num_frames = min(input_limit(), num_frames);
- if (!num_frames) {
- return 0;
- }
-
- // Copy samples to the input buffer and then process whatever can be consumed.
- input_buffer_->Write(buffer, num_frames);
- Process();
- return num_frames;
-}
-
-// Retrieves audio data from the timescaler.
-int SolaTimeScaler::RetrieveSamples(float* buffer, int num_frames) {
- CHECK(initialized_);
-
- // Do not read more frames than available.
- num_frames = min(available(), num_frames);
- if (!num_frames) {
- return 0;
- }
-
- output_buffer_->Copy(kOutputConsumer, buffer, num_frames);
- output_buffer_->Seek(kOutputConsumer,
- output_buffer_->Tell(kOutputConsumer) + num_frames);
-
- return num_frames;
-}
-
-// Munges input samples to produce output.
-bool SolaTimeScaler::Process() {
- CHECK(initialized_);
- bool generated_data = false;
-
- // We can only process data if there is sufficient input available
- // (or we are draining the latency), and there is sufficient room
- // for output to be merged.
- while (((input_buffer_->available(kInputReader) > max_frames_to_merge_) ||
- draining_) && (output_buffer_->overhead() >= max_frames_to_merge_)) {
- MutexLock lock(&mutex_); // lock out updates while processing each window
-
- // Determine the number of samples to merge into the output.
- int input_count =
- min(input_buffer_->available(kInputReader), max_frames_to_merge_);
- if (input_count == 0) {
- break;
- }
- // The input reader always points to the next window to process.
- float* input_pointer = input_buffer_->GetPointer(kInputReader, input_count);
-
- // The analysis reader always points to the ideal target merge point,
- // minus half an overlap window (ie, the starting point for correlation).
- // That means the available data from that point equals the number
- // of samples that must be cross-faded.
- int output_merge_cnt = output_buffer_->available(kOutputAnalysis);
- float* output_pointer =
- output_buffer_->GetPointer(kOutputAnalysis, output_merge_cnt);
-
- // If there is not enough data to do a proper correlation,
- // just merge at the ideal target point. Otherwise,
- // find the best correlation score, working from the center out.
- int merge_offset = min(output_merge_cnt, half_overlap_frames_);
-
- if ((output_merge_cnt >= (2 * num_overlap_frames_)) &&
- (input_count >= num_overlap_frames_)) {
- int best_offset = merge_offset;
- int best_score = 0;
- int score;
- for (int i = 0; i <= half_overlap_frames_; ++i) {
- score = analyzer_->Correlate(input_pointer,
- output_pointer + ((merge_offset + i) * num_channels_),
- num_overlap_frames_);
- if (score > best_score) {
- best_score = score;
- best_offset = merge_offset + i;
- if (score == (num_overlap_frames_ * num_channels_)) {
- break; // It doesn't get better than perfect.
- }
- }
- if (i > 0) {
- score = analyzer_->Correlate(input_pointer,
- output_pointer + ((merge_offset - i) * num_channels_),
- num_overlap_frames_);
- if (score > best_score) {
- best_score = score;
- best_offset = merge_offset - i;
- if (score == (num_overlap_frames_ * num_channels_)) {
- break; // It doesn't get better than perfect.
- }
- }
- }
- }
- merge_offset = best_offset;
- } else if ((output_merge_cnt > 0) && !draining_) {
- LOGE("no correlation performed");
- }
-
- // Crossfade the overlap between input and output, and then
- // copy in the remaining input.
- int crossfade_count = max(0, (output_merge_cnt - merge_offset));
- crossfade_count = min(crossfade_count, input_count);
- int remaining_count = input_count - crossfade_count;
-
- float* merge_pointer = output_pointer + (merge_offset * num_channels_);
- float flt_count = static_cast<float>(crossfade_count);
- for (int i = 0; i < crossfade_count; ++i) {
- // Linear cross-fade, for now.
- float input_scale = static_cast<float>(i) / flt_count;
- float output_scale = 1. - input_scale;
- for (int j = 0; j < num_channels_; ++j) {
- *merge_pointer = (*merge_pointer * output_scale) +
- (*input_pointer++ * input_scale);
- ++merge_pointer;
- }
- }
- // Copy the merged buffer back into the output, if necessary, and
- // append the rest of the window.
- output_buffer_->MergeBack(kOutputAnalysis,
- output_pointer, output_merge_cnt);
- output_buffer_->Write(input_pointer, remaining_count);
-
- // Advance the output analysis pointer to the next target merge point,
- // minus half an overlap window. The target merge point is always
- // calculated as a delta from the previous ideal target, not the actual
- // target, to avoid drift.
- int output_advance = target_merge_offset_;
- if (output_merge_cnt < half_overlap_frames_) {
- // On the first window, back up the pointer for the next correlation.
- // Thereafter, that compensation is preserved.
- output_advance -= half_overlap_frames_;
- }
-
- // Don't advance beyond the available data, when finishing up.
- if (draining_) {
- output_advance =
- min(output_advance, output_buffer_->available(kOutputAnalysis));
- }
- output_buffer_->Seek(kOutputAnalysis,
- output_buffer_->Tell(kOutputAnalysis) + output_advance);
-
- // Advance the input pointer beyond the frames that are no longer needed.
- input_buffer_->Seek(kInputReader, input_buffer_->Tell(kInputReader) +
- min(input_count, input_window_offset_));
-
- if ((crossfade_count + remaining_count) > 0) {
- generated_data = true;
- }
- } // while (more to process)
- return generated_data;
-}
-
-} // namespace video_editing
diff --git a/variablespeed/jni/sola_time_scaler.h b/variablespeed/jni/sola_time_scaler.h
deleted file mode 100644
index c903f61..0000000
--- a/variablespeed/jni/sola_time_scaler.h
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_
-
-#include <android/log.h>
-
-#include <no_synchronization.h>
-
-#include <list>
-#include <vector>
-
-#include "macros.h"
-
-// Time-domain audio playback rate scaler using phase-aligned Synchronized
-// OverLap Add (SOLA).
-
-namespace video_editing {
-
-class RingBuffer;
-
-// The default SolaAnalyzer implements a sign-bit cross-correlation
-// function for determining the best fit between two signals.
-class SolaAnalyzer {
- public:
- SolaAnalyzer() : initialized_(false) { }
- virtual ~SolaAnalyzer() { }
-
- // Initializes a SolaAnalyzer.
- // @param sample_rate sample rate of the audio signal.
- // @param num_channels number of interleaved channels in the signal.
- void Init(int sample_rate, int num_channels) {
- sample_rate_ = sample_rate;
- num_channels_ = num_channels;
- initialized_ = true;
- }
-
- // Returns a cross-correlation score for the specified buffers.
- // The correlation is performed over all channels of a multi-channel signal.
- // @param buffer1 pointer to interleaved input samples
- // @param buffer2 pointer to interleaved input samples
- // @param num_frames number of input frames (that is to say, number of
- // samples / number of channels)
- // @param returns a correlation score in the range zero to num_frames
- virtual int Correlate(const float* buffer1, const float* buffer2,
- int num_frames);
-
- protected:
- bool initialized_;
- int sample_rate_;
- int num_channels_;
-
- DISALLOW_COPY_AND_ASSIGN(SolaAnalyzer);
-};
-
-
-class SolaTimeScaler {
- public:
- // Default constructor.
- SolaTimeScaler();
- virtual ~SolaTimeScaler();
-
- // Injects a SolaAnalyzer instance for analyzing signal frames.
- // The scaler takes ownership of this instance.
- // This is normally called once, before Init().
- // @param analyzer SolaAnalyzer instance
- void set_analyzer(SolaAnalyzer* analyzer);
-
- // Initializes a SOLA timescaler.
- // @param sample_rate sample rate of the signal to process
- // @param num_channels number of channels of the signal to process
- // @param initial_speed starting rate scaling factor
- // @param window_duration processing window size, in seconds
- // @param overlap_duration correlation overlap size, in seconds
- void Init(double sample_rate, int num_channels, double initial_speed,
- double window_duration, double overlap_duration);
-
- // Adjusts the rate scaling factor.
- // This may be called concurrently with processing, and will
- // take effect on the next processing window.
- // @param speed rate scaling factor
- void set_speed(double speed);
-
- // Indicates that we are done with the input and won't call Process anymore
- // This processes all the data reamining in the analysis buffer.
- void Drain();
-
- // Flushes the buffers associated with the scaler.
- void Reset();
-
- // Feeds audio to the timescaler, and processes as much data as possible.
- // @param buffer pointer to interleaved float input samples
- // @param num_frames number of frames (num_samples / num_channels)
- // @returns number of frames actually accepted
- int InjectSamples(float* buffer, int num_frames);
-
- // Retrieves audio data from the timescaler.
- // @param buffer pointer to buffer to receive interleaved float output
- // @param num_frames maximum desired number of frames
- // @returns number of frames actually returned
- int RetrieveSamples(float* buffer, int num_frames);
-
- // Returns the number of frames that the input buffer can accept.
- // @returns number of frames for the next Process() call
- int input_limit() const;
-
- // Returns the number of available output frames.
- // @returns number of frames that can be retrieved
- int available();
-
- int num_channels() const { return num_channels_; }
-
- private:
- mutable Mutex mutex_; // allows concurrent produce/consume/param change
- bool initialized_; // set true when input parameters have been set
- bool draining_; // set true to drain latency
-
- // Input parameters.
- int num_channels_; // channel valence of audio stream
- double sample_rate_; // sample rate of audio stream
- double window_duration_; // the nominal time quantum for processing
- double overlap_duration_; // the maximum slip for correlating windows
- double speed_; // varispeed rate
-
- // Derived parameters.
- double ratio_; // inverse of speed
- int num_window_frames_; // window_duration_ expressed as frame count
- int num_overlap_frames_; // overlap_duration_ expressed as frame count
- int half_overlap_frames_; // half of the overlap
- int input_window_offset_; // frame delta between input windows
- int target_merge_offset_; // ideal frame delta between output windows
- int max_frames_to_merge_; // ideal frame count to merge to output
- int min_output_to_hold_; // number of output frames needed for next merge
-
- RingBuffer* input_buffer_;
- RingBuffer* output_buffer_;
- SolaAnalyzer* analyzer_;
-
- // Generates processing parameters from the current settings.
- void GenerateParameters();
-
- // Munges input samples to produce output.
- // @returns true if any output samples were generated
- bool Process();
-
- DISALLOW_COPY_AND_ASSIGN(SolaTimeScaler);
-};
-
-} // namespace video_editing
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_SOLA_TIME_SCALER_H_
diff --git a/variablespeed/jni/variablespeed.cc b/variablespeed/jni/variablespeed.cc
deleted file mode 100644
index 8e161fc..0000000
--- a/variablespeed/jni/variablespeed.cc
+++ /dev/null
@@ -1,864 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <variablespeed.h>
-
-#include <unistd.h>
-#include <stdlib.h>
-
-#include <sola_time_scaler.h>
-#include <ring_buffer.h>
-
-#include <hlogging.h>
-
-#include <vector>
-
-#include <sys/system_properties.h>
-
-// ****************************************************************************
-// Constants, utility methods, structures and other miscellany used throughout
-// this file.
-
-namespace {
-
-// These variables are used to determine the size of the buffer queue used by
-// the decoder.
-// This is not the same as the large buffer used to hold the uncompressed data
-// - for that see the member variable decodeBuffer_.
-// The choice of 1152 corresponds to the number of samples per mp3 frame, so is
-// a good choice of size for a decoding buffer in the absence of other
-// information (we don't know exactly what formats we will be working with).
-const size_t kNumberOfBuffersInQueue = 4;
-const size_t kNumberOfSamplesPerBuffer = 1152;
-const size_t kBufferSizeInBytes = 2 * kNumberOfSamplesPerBuffer;
-const size_t kSampleSizeInBytes = 4;
-
-// When calculating play buffer size before pushing to audio player.
-const size_t kNumberOfBytesPerInt16 = 2;
-
-// How long to sleep during the main play loop and the decoding callback loop.
-// In due course this should be replaced with the better signal and wait on
-// condition rather than busy-looping.
-const int kSleepTimeMicros = 1000;
-
-// Used in detecting errors with the OpenSL ES framework.
-const SLuint32 kPrefetchErrorCandidate =
- SL_PREFETCHEVENT_STATUSCHANGE | SL_PREFETCHEVENT_FILLLEVELCHANGE;
-
-// Structure used when we perform a decoding callback.
-typedef struct CallbackContext_ {
- // Pointer to local storage buffers for decoded audio data.
- int8_t* pDataBase;
- // Pointer to the current buffer within local storage.
- int8_t* pData;
- // Used to read the sample rate and channels from the decoding stream during
- // the first decoding callback.
- SLMetadataExtractionItf decoderMetadata;
- // The play interface used for reading duration.
- SLPlayItf playItf;
-} CallbackContext;
-
-// Local storage for decoded audio data.
-int8_t pcmData[kNumberOfBuffersInQueue * kBufferSizeInBytes];
-
-#define CheckSLResult(message, result) \
- CheckSLResult_Real(message, result, __LINE__)
-
-// Helper function for debugging - checks the OpenSL result for success.
-void CheckSLResult_Real(const char* message, SLresult result, int line) {
- // This can be helpful when debugging.
- // LOGD("sl result %d for %s", result, message);
- if (SL_RESULT_SUCCESS != result) {
- LOGE("slresult was %d at %s file variablespeed line %d",
- static_cast<int>(result), message, line);
- }
- CHECK(SL_RESULT_SUCCESS == result);
-}
-
-// Whether logging should be enabled. Only used if LOG_OPENSL_API_CALL is
-// defined to use it.
-bool gLogEnabled = false;
-// The property to set in order to enable logging.
-const char *const kLogTagVariableSpeed = "log.tag.VariableSpeed";
-
-bool ShouldLog() {
- char buffer[PROP_VALUE_MAX];
- __system_property_get(kLogTagVariableSpeed, buffer);
- return strlen(buffer) > 0;
-}
-
-} // namespace
-
-// ****************************************************************************
-// Static instance of audio engine, and methods for getting, setting and
-// deleting it.
-
-// The single global audio engine instance.
-AudioEngine* AudioEngine::audioEngine_ = NULL;
-android::Mutex publishEngineLock_;
-
-AudioEngine* AudioEngine::GetEngine() {
- android::Mutex::Autolock autoLock(publishEngineLock_);
- if (audioEngine_ == NULL) {
- LOGE("you haven't initialized the audio engine");
- CHECK(false);
- return NULL;
- }
- return audioEngine_;
-}
-
-void AudioEngine::SetEngine(AudioEngine* engine) {
- if (audioEngine_ != NULL) {
- LOGE("you have already set the audio engine");
- CHECK(false);
- return;
- }
- audioEngine_ = engine;
-}
-
-bool AudioEngine::CompareAndSetEngine(AudioEngine* expect, AudioEngine* update) {
- android::Mutex::Autolock autoLock(publishEngineLock_);
- if (audioEngine_ == expect) {
- DeleteEngine();
- audioEngine_ = update;
- return true;
- }
- return false;
-}
-
-void AudioEngine::DeleteEngine() {
- if (audioEngine_ != NULL) {
- delete audioEngine_;
- audioEngine_ = NULL;
- }
-}
-
-// ****************************************************************************
-// The callbacks from the engine require static callback functions.
-// Here are the static functions - they just delegate to instance methods on
-// the engine.
-
-static void PlayingBufferQueueCb(SLAndroidSimpleBufferQueueItf, void*) {
- AudioEngine::GetEngine()->PlayingBufferQueueCallback();
-}
-
-static void PrefetchEventCb(SLPrefetchStatusItf caller, void*, SLuint32 event) {
- AudioEngine::GetEngine()->PrefetchEventCallback(caller, event);
-}
-
-static void DecodingBufferQueueCb(SLAndroidSimpleBufferQueueItf queueItf,
- void *context) {
- AudioEngine::GetEngine()->DecodingBufferQueueCallback(queueItf, context);
-}
-
-static void DecodingEventCb(SLPlayItf caller, void*, SLuint32 event) {
- AudioEngine::GetEngine()->DecodingEventCallback(caller, event);
-}
-
-// ****************************************************************************
-// Macros for making working with OpenSL easier.
-
-// Log based on the value of a property.
-#define LOG_OPENSL_API_CALL(string) (gLogEnabled && LOGV(string))
-
-// The regular macro: log an api call, make the api call, check the result.
-#define OpenSL(obj, method, ...) \
-{ \
- LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")"); \
- SLresult result = (*obj)->method(obj, __VA_ARGS__); \
- CheckSLResult("OpenSL " #method "(" #obj ", " #__VA_ARGS__ ")", result); \
-}
-
-// Special case call for api call that has void return value, can't be checked.
-#define VoidOpenSL(obj, method) \
-{ \
- LOG_OPENSL_API_CALL("OpenSL (void) " #method "(" #obj ")"); \
- (*obj)->method(obj); \
-}
-
-// Special case for api call with checked result but takes no arguments.
-#define OpenSL0(obj, method) \
-{ \
- LOG_OPENSL_API_CALL("OpenSL " #method "(" #obj ")"); \
- SLresult result = (*obj)->method(obj); \
- CheckSLResult("OpenSL " #method "(" #obj ")", result); \
-}
-
-// Special case for api call whose result we want to store, not check.
-// We have to encapsulate the two calls in braces, so that this expression
-// evaluates to the last expression not the first.
-#define ReturnOpenSL(obj, method, ...) \
-( \
- LOG_OPENSL_API_CALL("OpenSL (int) " \
- #method "(" #obj ", " #__VA_ARGS__ ")"), \
- (*obj)->method(obj, __VA_ARGS__) \
-) \
-
-// ****************************************************************************
-// Static utility methods.
-
-// Set the audio stream type for the player.
-//
-// Must be called before it is realized.
-//
-// The caller must have requested the SL_IID_ANDROIDCONFIGURATION interface when
-// creating the player.
-static void setAudioStreamType(SLObjectItf audioPlayer, SLint32 audioStreamType) {
- SLAndroidConfigurationItf playerConfig;
- OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
- // The STREAM_XXX constants defined by android.media.AudioManager match the
- // corresponding SL_ANDROID_STREAM_XXX constants defined by
- // include/SLES/OpenSLES_AndroidConfiguration.h, so we can just pass the
- // value across.
- OpenSL(playerConfig, SetConfiguration, SL_ANDROID_KEY_STREAM_TYPE,
- &audioStreamType, sizeof(audioStreamType));
-}
-
-// Must be called with callbackLock_ held.
-static void ReadSampleRateAndChannelCount(CallbackContext *pContext,
- SLuint32 *sampleRateOut, SLuint32 *channelsOut) {
- SLMetadataExtractionItf decoderMetadata = pContext->decoderMetadata;
- SLuint32 itemCount;
- OpenSL(decoderMetadata, GetItemCount, &itemCount);
- SLuint32 i, keySize, valueSize;
- SLMetadataInfo *keyInfo, *value;
- for (i = 0; i < itemCount; ++i) {
- keyInfo = value = NULL;
- keySize = valueSize = 0;
- OpenSL(decoderMetadata, GetKeySize, i, &keySize);
- keyInfo = static_cast<SLMetadataInfo*>(malloc(keySize));
- if (keyInfo) {
- OpenSL(decoderMetadata, GetKey, i, keySize, keyInfo);
- if (keyInfo->encoding == SL_CHARACTERENCODING_ASCII
- || keyInfo->encoding == SL_CHARACTERENCODING_UTF8) {
- OpenSL(decoderMetadata, GetValueSize, i, &valueSize);
- value = static_cast<SLMetadataInfo*>(malloc(valueSize));
- if (value) {
- OpenSL(decoderMetadata, GetValue, i, valueSize, value);
- if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_SAMPLERATE) == 0) {
- SLuint32 sampleRate = *(reinterpret_cast<SLuint32*>(value->data));
- LOGD("sample Rate: %d", sampleRate);
- *sampleRateOut = sampleRate;
- } else if (strcmp((char*) keyInfo->data, ANDROID_KEY_PCMFORMAT_NUMCHANNELS) == 0) {
- SLuint32 channels = *(reinterpret_cast<SLuint32*>(value->data));
- LOGD("channels: %d", channels);
- *channelsOut = channels;
- }
- free(value);
- }
- }
- free(keyInfo);
- }
- }
-}
-
-// Must be called with callbackLock_ held.
-static void RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
- SLAndroidSimpleBufferQueueItf decoderQueue, CallbackContext* context) {
- // Register a callback on the decoder queue, so that we will be called
- // throughout the decoding process (and can then extract the decoded audio
- // for the next bit of the pipeline).
- OpenSL(decoderQueue, RegisterCallback, DecodingBufferQueueCb, context);
-
- // Enqueue buffers to map the region of memory allocated to store the
- // decoded data.
- for (size_t i = 0; i < kNumberOfBuffersInQueue; i++) {
- OpenSL(decoderQueue, Enqueue, context->pData, kBufferSizeInBytes);
- context->pData += kBufferSizeInBytes;
- }
- context->pData = context->pDataBase;
-}
-
-// ****************************************************************************
-// Constructor and Destructor.
-
-AudioEngine::AudioEngine(size_t targetFrames, float windowDuration,
- float windowOverlapDuration, size_t maxPlayBufferCount, float initialRate,
- size_t decodeInitialSize, size_t decodeMaxSize, size_t startPositionMillis,
- int audioStreamType)
- : decodeBuffer_(decodeInitialSize, decodeMaxSize),
- playingBuffers_(), freeBuffers_(), timeScaler_(NULL),
- floatBuffer_(NULL), injectBuffer_(NULL),
- mSampleRate(0), mChannels(0),
- targetFrames_(targetFrames),
- windowDuration_(windowDuration),
- windowOverlapDuration_(windowOverlapDuration),
- maxPlayBufferCount_(maxPlayBufferCount), initialRate_(initialRate),
- startPositionMillis_(startPositionMillis),
- audioStreamType_(audioStreamType),
- totalDurationMs_(0), decoderCurrentPosition_(0), startRequested_(false),
- stopRequested_(false), finishedDecoding_(false) {
- // Determine whether we should log calls.
- gLogEnabled = ShouldLog();
-}
-
-AudioEngine::~AudioEngine() {
- // destroy the time scaler
- if (timeScaler_ != NULL) {
- delete timeScaler_;
- timeScaler_ = NULL;
- }
-
- // delete all outstanding playing and free buffers
- android::Mutex::Autolock autoLock(playBufferLock_);
- while (playingBuffers_.size() > 0) {
- delete[] playingBuffers_.front();
- playingBuffers_.pop();
- }
- while (freeBuffers_.size() > 0) {
- delete[] freeBuffers_.top();
- freeBuffers_.pop();
- }
-
- delete[] floatBuffer_;
- floatBuffer_ = NULL;
- delete[] injectBuffer_;
- injectBuffer_ = NULL;
-}
-
-// ****************************************************************************
-// Regular AudioEngine class methods.
-
-void AudioEngine::SetVariableSpeed(float speed) {
- // TODO: Mutex for shared time scaler accesses.
- if (HasSampleRateAndChannels()) {
- GetTimeScaler()->set_speed(speed);
- } else {
- // This is being called at a point where we have not yet processed enough
- // data to determine the sample rate and number of channels.
- // Ignore the call. See http://b/5140693.
- LOGD("set varaible speed called, sample rate and channels not ready yet");
- }
-}
-
-void AudioEngine::RequestStart() {
- android::Mutex::Autolock autoLock(lock_);
- startRequested_ = true;
-}
-
-void AudioEngine::ClearRequestStart() {
- android::Mutex::Autolock autoLock(lock_);
- startRequested_ = false;
-}
-
-bool AudioEngine::GetWasStartRequested() {
- android::Mutex::Autolock autoLock(lock_);
- return startRequested_;
-}
-
-void AudioEngine::RequestStop() {
- android::Mutex::Autolock autoLock(lock_);
- stopRequested_ = true;
-}
-
-int AudioEngine::GetCurrentPosition() {
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- double result = decodeBuffer_.GetTotalAdvancedCount();
- // TODO: This is horrible, but should be removed soon once the outstanding
- // issue with get current position on decoder is fixed.
- android::Mutex::Autolock autoLock2(callbackLock_);
- return static_cast<int>(
- (result * 1000) / mSampleRate / mChannels + startPositionMillis_);
-}
-
-int AudioEngine::GetTotalDuration() {
- android::Mutex::Autolock autoLock(lock_);
- return static_cast<int>(totalDurationMs_);
-}
-
-video_editing::SolaTimeScaler* AudioEngine::GetTimeScaler() {
- if (timeScaler_ == NULL) {
- CHECK(HasSampleRateAndChannels());
- android::Mutex::Autolock autoLock(callbackLock_);
- timeScaler_ = new video_editing::SolaTimeScaler();
- timeScaler_->Init(mSampleRate, mChannels, initialRate_, windowDuration_,
- windowOverlapDuration_);
- }
- return timeScaler_;
-}
-
-bool AudioEngine::EnqueueNextBufferOfAudio(
- SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
- size_t channels;
- {
- android::Mutex::Autolock autoLock(callbackLock_);
- channels = mChannels;
- }
- size_t frameSizeInBytes = kSampleSizeInBytes * channels;
- size_t frameCount = 0;
- while (frameCount < targetFrames_) {
- size_t framesLeft = targetFrames_ - frameCount;
- // If there is data already in the time scaler, retrieve it.
- if (GetTimeScaler()->available() > 0) {
- size_t retrieveCount = min(GetTimeScaler()->available(), framesLeft);
- int count = GetTimeScaler()->RetrieveSamples(
- floatBuffer_ + frameCount * channels, retrieveCount);
- if (count <= 0) {
- LOGD("error: count was %d", count);
- break;
- }
- frameCount += count;
- continue;
- }
- // If there is no data in the time scaler, then feed some into it.
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- size_t framesInDecodeBuffer =
- decodeBuffer_.GetSizeInBytes() / frameSizeInBytes;
- size_t framesScalerCanHandle = GetTimeScaler()->input_limit();
- size_t framesToInject = min(framesInDecodeBuffer,
- min(targetFrames_, framesScalerCanHandle));
- if (framesToInject <= 0) {
- // No more frames left to inject.
- break;
- }
- for (size_t i = 0; i < framesToInject * channels; ++i) {
- injectBuffer_[i] = decodeBuffer_.GetAtIndex(i);
- }
- int count = GetTimeScaler()->InjectSamples(injectBuffer_, framesToInject);
- if (count <= 0) {
- LOGD("error: count was %d", count);
- break;
- }
- decodeBuffer_.AdvanceHeadPointerShorts(count * channels);
- }
- if (frameCount <= 0) {
- // We must have finished playback.
- if (GetEndOfDecoderReached()) {
- // If we've finished decoding, clear the buffer - so we will terminate.
- ClearDecodeBuffer();
- }
- return false;
- }
-
- // Get a free playing buffer.
- int16* playBuffer;
- {
- android::Mutex::Autolock autoLock(playBufferLock_);
- if (freeBuffers_.size() > 0) {
- // If we have a free buffer, recycle it.
- playBuffer = freeBuffers_.top();
- freeBuffers_.pop();
- } else {
- // Otherwise allocate a new one.
- playBuffer = new int16[targetFrames_ * channels];
- }
- }
-
- // Try to play the buffer.
- for (size_t i = 0; i < frameCount * channels; ++i) {
- playBuffer[i] = floatBuffer_[i];
- }
- size_t sizeOfPlayBufferInBytes =
- frameCount * channels * kNumberOfBytesPerInt16;
- SLresult result = ReturnOpenSL(audioPlayerQueue, Enqueue, playBuffer,
- sizeOfPlayBufferInBytes);
- if (result == SL_RESULT_SUCCESS) {
- android::Mutex::Autolock autoLock(playBufferLock_);
- playingBuffers_.push(playBuffer);
- } else {
- LOGE("could not enqueue audio buffer");
- delete[] playBuffer;
- }
-
- return (result == SL_RESULT_SUCCESS);
-}
-
-bool AudioEngine::GetEndOfDecoderReached() {
- android::Mutex::Autolock autoLock(lock_);
- return finishedDecoding_;
-}
-
-void AudioEngine::SetEndOfDecoderReached() {
- android::Mutex::Autolock autoLock(lock_);
- finishedDecoding_ = true;
-}
-
-bool AudioEngine::PlayFileDescriptor(int fd, int64 offset, int64 length) {
- SLDataLocator_AndroidFD loc_fd = {
- SL_DATALOCATOR_ANDROIDFD, fd, offset, length };
- SLDataFormat_MIME format_mime = {
- SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED };
- SLDataSource audioSrc = { &loc_fd, &format_mime };
- return PlayFromThisSource(audioSrc);
-}
-
-bool AudioEngine::PlayUri(const char* uri) {
- // Source of audio data for the decoding
- SLDataLocator_URI decUri = { SL_DATALOCATOR_URI,
- const_cast<SLchar*>(reinterpret_cast<const SLchar*>(uri)) };
- SLDataFormat_MIME decMime = {
- SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_UNSPECIFIED };
- SLDataSource decSource = { &decUri, &decMime };
- return PlayFromThisSource(decSource);
-}
-
-bool AudioEngine::IsDecodeBufferEmpty() {
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- return decodeBuffer_.GetSizeInBytes() <= 0;
-}
-
-void AudioEngine::ClearDecodeBuffer() {
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- decodeBuffer_.Clear();
-}
-
-static size_t ReadDuration(SLPlayItf playItf) {
- SLmillisecond durationInMsec = SL_TIME_UNKNOWN;
- OpenSL(playItf, GetDuration, &durationInMsec);
- if (durationInMsec == SL_TIME_UNKNOWN) {
- LOGE("can't get duration");
- return 0;
- }
- LOGD("duration: %d", static_cast<int>(durationInMsec));
- return durationInMsec;
-}
-
-static size_t ReadPosition(SLPlayItf playItf) {
- SLmillisecond positionInMsec = SL_TIME_UNKNOWN;
- OpenSL(playItf, GetPosition, &positionInMsec);
- if (positionInMsec == SL_TIME_UNKNOWN) {
- LOGE("can't get position");
- return 0;
- }
- LOGW("decoder position: %d", static_cast<int>(positionInMsec));
- return positionInMsec;
-}
-
-static void CreateAndRealizeEngine(SLObjectItf &engine,
- SLEngineItf &engineInterface) {
- SLEngineOption EngineOption[] = { {
- SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE } };
- SLresult result = slCreateEngine(&engine, 1, EngineOption, 0, NULL, NULL);
- CheckSLResult("create engine", result);
- OpenSL(engine, Realize, SL_BOOLEAN_FALSE);
- OpenSL(engine, GetInterface, SL_IID_ENGINE, &engineInterface);
-}
-
-SLuint32 AudioEngine::GetSLSampleRate() {
- android::Mutex::Autolock autoLock(callbackLock_);
- return mSampleRate * 1000;
-}
-
-SLuint32 AudioEngine::GetSLChannels() {
- android::Mutex::Autolock autoLock(callbackLock_);
- switch (mChannels) {
- case 2:
- return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
- case 1:
- return SL_SPEAKER_FRONT_CENTER;
- default:
- LOGE("unknown channels %d, using 2", mChannels);
- return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
- }
-}
-
-SLuint32 AudioEngine::GetChannelCount() {
- android::Mutex::Autolock autoLock(callbackLock_);
- return mChannels;
-}
-
-static void CreateAndRealizeAudioPlayer(SLuint32 slSampleRate,
- SLuint32 channelCount, SLuint32 slChannels, SLint32 audioStreamType, SLObjectItf &outputMix,
- SLObjectItf &audioPlayer, SLEngineItf &engineInterface) {
- // Define the source and sink for the audio player: comes from a buffer queue
- // and goes to the output mix.
- SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
- SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
- SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, channelCount, slSampleRate,
- SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
- slChannels, SL_BYTEORDER_LITTLEENDIAN};
- SLDataSource playingSrc = {&loc_bufq, &format_pcm};
- SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMix};
- SLDataSink audioSnk = {&loc_outmix, NULL};
-
- // Create the audio player, which will play from the buffer queue and send to
- // the output mix.
- const size_t playerInterfaceCount = 2;
- const SLInterfaceID iids[playerInterfaceCount] = {
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
- const SLboolean reqs[playerInterfaceCount] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
- OpenSL(engineInterface, CreateAudioPlayer, &audioPlayer, &playingSrc,
- &audioSnk, playerInterfaceCount, iids, reqs);
- setAudioStreamType(audioPlayer, audioStreamType);
- OpenSL(audioPlayer, Realize, SL_BOOLEAN_FALSE);
-}
-
-bool AudioEngine::HasSampleRateAndChannels() {
- android::Mutex::Autolock autoLock(callbackLock_);
- return mChannels != 0 && mSampleRate != 0;
-}
-
-bool AudioEngine::PlayFromThisSource(const SLDataSource& audioSrc) {
- ClearDecodeBuffer();
-
- SLObjectItf engine;
- SLEngineItf engineInterface;
- CreateAndRealizeEngine(engine, engineInterface);
-
- // Define the source and sink for the decoding player: comes from the source
- // this method was called with, is sent to another buffer queue.
- SLDataLocator_AndroidSimpleBufferQueue decBuffQueue;
- decBuffQueue.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
- decBuffQueue.numBuffers = kNumberOfBuffersInQueue;
- // A valid value seems required here but is currently ignored.
- SLDataFormat_PCM pcm = {SL_DATAFORMAT_PCM, 1, SL_SAMPLINGRATE_44_1,
- SL_PCMSAMPLEFORMAT_FIXED_16, 16,
- SL_SPEAKER_FRONT_LEFT, SL_BYTEORDER_LITTLEENDIAN};
- SLDataSink decDest = { &decBuffQueue, &pcm };
-
- // Create the decoder with the given source and sink.
- const size_t decoderInterfaceCount = 5;
- SLObjectItf decoder;
- const SLInterfaceID decodePlayerInterfaces[decoderInterfaceCount] = {
- SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_PREFETCHSTATUS, SL_IID_SEEK,
- SL_IID_METADATAEXTRACTION, SL_IID_ANDROIDCONFIGURATION };
- const SLboolean decodePlayerRequired[decoderInterfaceCount] = {
- SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
- SLDataSource sourceCopy(audioSrc);
- OpenSL(engineInterface, CreateAudioPlayer, &decoder, &sourceCopy, &decDest,
- decoderInterfaceCount, decodePlayerInterfaces, decodePlayerRequired);
- // Not sure if this is necessary, but just in case.
- setAudioStreamType(decoder, audioStreamType_);
- OpenSL(decoder, Realize, SL_BOOLEAN_FALSE);
-
- // Get the play interface from the decoder, and register event callbacks.
- // Get the buffer queue, prefetch and seek interfaces.
- SLPlayItf decoderPlay = NULL;
- SLAndroidSimpleBufferQueueItf decoderQueue = NULL;
- SLPrefetchStatusItf decoderPrefetch = NULL;
- SLSeekItf decoderSeek = NULL;
- SLMetadataExtractionItf decoderMetadata = NULL;
- OpenSL(decoder, GetInterface, SL_IID_PLAY, &decoderPlay);
- OpenSL(decoderPlay, SetCallbackEventsMask, SL_PLAYEVENT_HEADATEND);
- OpenSL(decoderPlay, RegisterCallback, DecodingEventCb, NULL);
- OpenSL(decoder, GetInterface, SL_IID_PREFETCHSTATUS, &decoderPrefetch);
- OpenSL(decoder, GetInterface, SL_IID_SEEK, &decoderSeek);
- OpenSL(decoder, GetInterface, SL_IID_METADATAEXTRACTION, &decoderMetadata);
- OpenSL(decoder, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &decoderQueue);
-
- // Initialize the callback structure, used during the decoding.
- CallbackContext callbackContext;
- {
- android::Mutex::Autolock autoLock(callbackLock_);
- callbackContext.pDataBase = pcmData;
- callbackContext.pData = pcmData;
- callbackContext.decoderMetadata = decoderMetadata;
- callbackContext.playItf = decoderPlay;
- RegisterCallbackContextAndAddEnqueueBuffersToDecoder(
- decoderQueue, &callbackContext);
- }
-
- // Initialize the callback for prefetch errors, if we can't open the
- // resource to decode.
- OpenSL(decoderPrefetch, SetCallbackEventsMask, kPrefetchErrorCandidate);
- OpenSL(decoderPrefetch, RegisterCallback, PrefetchEventCb, &decoderPrefetch);
-
- // Seek to the start position.
- OpenSL(decoderSeek, SetPosition, startPositionMillis_, SL_SEEKMODE_ACCURATE);
-
- // Start decoding immediately.
- OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
-
- // These variables hold the audio player and its output.
- // They will only be constructed once the decoder has invoked the callback,
- // and given us the correct sample rate, number of channels and duration.
- SLObjectItf outputMix = NULL;
- SLObjectItf audioPlayer = NULL;
- SLPlayItf audioPlayerPlay = NULL;
- SLAndroidSimpleBufferQueueItf audioPlayerQueue = NULL;
-
- // The main loop - until we're told to stop: if there is audio data coming
- // out of the decoder, feed it through the time scaler.
- // As it comes out of the time scaler, feed it into the audio player.
- while (!Finished()) {
- if (GetWasStartRequested() && HasSampleRateAndChannels()) {
- // Build the audio player.
- // TODO: What happens if I maliciously call start lots of times?
- floatBuffer_ = new float[targetFrames_ * mChannels];
- injectBuffer_ = new float[targetFrames_ * mChannels];
- OpenSL(engineInterface, CreateOutputMix, &outputMix, 0, NULL, NULL);
- OpenSL(outputMix, Realize, SL_BOOLEAN_FALSE);
- CreateAndRealizeAudioPlayer(GetSLSampleRate(), GetChannelCount(),
- GetSLChannels(), audioStreamType_, outputMix, audioPlayer,
- engineInterface);
- OpenSL(audioPlayer, GetInterface, SL_IID_PLAY, &audioPlayerPlay);
- OpenSL(audioPlayer, GetInterface, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
- &audioPlayerQueue);
- OpenSL(audioPlayerQueue, RegisterCallback, PlayingBufferQueueCb, NULL);
- ClearRequestStart();
- OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_PLAYING);
- }
- EnqueueMoreAudioIfNecessary(audioPlayerQueue);
- usleep(kSleepTimeMicros);
- }
-
- // Delete the audio player and output mix, iff they have been created.
- if (audioPlayer != NULL) {
- OpenSL(audioPlayerPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
- OpenSL0(audioPlayerQueue, Clear);
- OpenSL(audioPlayerQueue, RegisterCallback, NULL, NULL);
- VoidOpenSL(audioPlayer, AbortAsyncOperation);
- VoidOpenSL(audioPlayer, Destroy);
- VoidOpenSL(outputMix, Destroy);
- audioPlayer = NULL;
- audioPlayerPlay = NULL;
- audioPlayerQueue = NULL;
- outputMix = NULL;
- }
-
- // Delete the decoder.
- OpenSL(decoderPlay, SetPlayState, SL_PLAYSTATE_STOPPED);
- OpenSL(decoderPrefetch, RegisterCallback, NULL, NULL);
- // This is returning slresult 13 if I do no playback.
- // Repro is to comment out all before this line, and all after enqueueing
- // my buffers.
- // OpenSL0(decoderQueue, Clear);
- OpenSL(decoderQueue, RegisterCallback, NULL, NULL);
- decoderSeek = NULL;
- decoderPrefetch = NULL;
- decoderQueue = NULL;
- OpenSL(decoderPlay, RegisterCallback, NULL, NULL);
- VoidOpenSL(decoder, AbortAsyncOperation);
- VoidOpenSL(decoder, Destroy);
- decoderPlay = NULL;
-
- // Delete the engine.
- VoidOpenSL(engine, Destroy);
- engineInterface = NULL;
-
- return true;
-}
-
-bool AudioEngine::Finished() {
- if (GetWasStopRequested()) {
- return true;
- }
- android::Mutex::Autolock autoLock(playBufferLock_);
- return playingBuffers_.size() <= 0 &&
- IsDecodeBufferEmpty() &&
- GetEndOfDecoderReached();
-}
-
-bool AudioEngine::GetWasStopRequested() {
- android::Mutex::Autolock autoLock(lock_);
- return stopRequested_;
-}
-
-bool AudioEngine::GetHasReachedPlayingBuffersLimit() {
- android::Mutex::Autolock autoLock(playBufferLock_);
- return playingBuffers_.size() >= maxPlayBufferCount_;
-}
-
-void AudioEngine::EnqueueMoreAudioIfNecessary(
- SLAndroidSimpleBufferQueueItf audioPlayerQueue) {
- bool keepEnqueueing = true;
- while (audioPlayerQueue != NULL &&
- !GetWasStopRequested() &&
- !IsDecodeBufferEmpty() &&
- !GetHasReachedPlayingBuffersLimit() &&
- keepEnqueueing) {
- keepEnqueueing = EnqueueNextBufferOfAudio(audioPlayerQueue);
- }
-}
-
-bool AudioEngine::DecodeBufferTooFull() {
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- return decodeBuffer_.IsTooLarge();
-}
-
-// ****************************************************************************
-// Code for handling the static callbacks.
-
-void AudioEngine::PlayingBufferQueueCallback() {
- // The head playing buffer is done, move it to the free list.
- android::Mutex::Autolock autoLock(playBufferLock_);
- if (playingBuffers_.size() > 0) {
- freeBuffers_.push(playingBuffers_.front());
- playingBuffers_.pop();
- }
-}
-
-void AudioEngine::PrefetchEventCallback(
- SLPrefetchStatusItf caller, SLuint32 event) {
- // If there was a problem during decoding, then signal the end.
- SLpermille level = 0;
- SLuint32 status;
- OpenSL(caller, GetFillLevel, &level);
- OpenSL(caller, GetPrefetchStatus, &status);
- if ((kPrefetchErrorCandidate == (event & kPrefetchErrorCandidate)) &&
- (level == 0) &&
- (status == SL_PREFETCHSTATUS_UNDERFLOW)) {
- LOGI("prefetcheventcallback error while prefetching data");
- SetEndOfDecoderReached();
- }
- if (SL_PREFETCHSTATUS_SUFFICIENTDATA == event) {
- // android::Mutex::Autolock autoLock(prefetchLock_);
- // prefetchCondition_.broadcast();
- }
-}
-
-void AudioEngine::DecodingBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf queueItf, void *context) {
- if (GetWasStopRequested()) {
- return;
- }
-
- CallbackContext *pCntxt;
- {
- android::Mutex::Autolock autoLock(callbackLock_);
- pCntxt = reinterpret_cast<CallbackContext*>(context);
- }
- {
- android::Mutex::Autolock autoLock(decodeBufferLock_);
- decodeBuffer_.AddData(pCntxt->pData, kBufferSizeInBytes);
- }
-
- if (!HasSampleRateAndChannels()) {
- android::Mutex::Autolock autoLock(callbackLock_);
- ReadSampleRateAndChannelCount(pCntxt, &mSampleRate, &mChannels);
- }
-
- {
- android::Mutex::Autolock autoLock(lock_);
- if (totalDurationMs_ == 0) {
- totalDurationMs_ = ReadDuration(pCntxt->playItf);
- }
- // TODO: This isn't working, it always reports zero.
- // ReadPosition(pCntxt->playItf);
- }
-
- OpenSL(queueItf, Enqueue, pCntxt->pData, kBufferSizeInBytes);
-
- // Increase data pointer by buffer size
- pCntxt->pData += kBufferSizeInBytes;
- if (pCntxt->pData >= pCntxt->pDataBase +
- (kNumberOfBuffersInQueue * kBufferSizeInBytes)) {
- pCntxt->pData = pCntxt->pDataBase;
- }
-
- // If we get too much data into the decoder,
- // sleep until the playback catches up.
- while (!GetWasStopRequested() && DecodeBufferTooFull()) {
- usleep(kSleepTimeMicros);
- }
-}
-
-void AudioEngine::DecodingEventCallback(SLPlayItf, SLuint32 event) {
- if (SL_PLAYEVENT_HEADATEND & event) {
- SetEndOfDecoderReached();
- }
-}
diff --git a/variablespeed/jni/variablespeed.h b/variablespeed/jni/variablespeed.h
deleted file mode 100644
index 74710e5..0000000
--- a/variablespeed/jni/variablespeed.h
+++ /dev/null
@@ -1,167 +0,0 @@
-/*
- * Copyright (C) 2011 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 FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_
-#define FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_
-
-#include <jni.h>
-
-#include <SLES/OpenSLES.h>
-#include <SLES/OpenSLES_Android.h>
-#include <SLES/OpenSLES_AndroidConfiguration.h>
-
-#include <integral_types.h>
-#include <utils/threads.h>
-
-#include <profile_timer.h>
-#include <decode_buffer.h>
-
-#include <queue>
-#include <stack>
-
-namespace video_editing {
- class SolaTimeScaler;
-}
-
-// This is the audio engine class.
-// It forms the bulk of the variablespeed library.
-// It should not be used directly, but rather used indirectly from the java
-// native methods.
-class AudioEngine {
- public:
- AudioEngine(size_t targetFrames, float windowDuration,
- float windowOverlapDuration, size_t maxPlayBufferCount,
- float initialRate, size_t decodeInitialSize, size_t decodeMaxSize,
- size_t startPositionMillis, int audioStreamType);
- virtual ~AudioEngine();
-
- bool PlayUri(const char* uri);
- bool PlayFileDescriptor(int fd, int64 offset, int64 length);
- void SetVariableSpeed(float speed);
- void RequestStart();
- void RequestStop();
- int GetCurrentPosition();
- int GetTotalDuration();
-
- void DecodingBufferQueueCallback(
- SLAndroidSimpleBufferQueueItf queueItf, void *context);
- void DecodingEventCallback(SLPlayItf caller, SLuint32 event);
- void PrefetchEventCallback(SLPrefetchStatusItf caller, SLuint32 event);
- void PlayingBufferQueueCallback();
-
- static AudioEngine* GetEngine();
- static void SetEngine(AudioEngine* engine);
- static bool CompareAndSetEngine(AudioEngine* expect, AudioEngine* update);
- static void DeleteEngine();
-
- private:
- bool PlayFromThisSource(const SLDataSource& audioSrc);
- void EnqueueMoreAudioIfNecessary(SLAndroidSimpleBufferQueueItf bufferQueue);
- bool EnqueueNextBufferOfAudio(SLAndroidSimpleBufferQueueItf bufferQueue);
- void PrefetchDurationSampleRateAndChannels(
- SLPlayItf playItf, SLPrefetchStatusItf prefetchItf);
- video_editing::SolaTimeScaler* GetTimeScaler();
- bool Finished();
- bool GetWasStartRequested();
- bool GetWasStopRequested();
- void ClearRequestStart();
- void SetEndOfDecoderReached();
- bool GetEndOfDecoderReached();
- bool DecodeBufferTooFull();
- void ClearDecodeBuffer();
- bool IsDecodeBufferEmpty();
- bool GetHasReachedPlayingBuffersLimit();
- bool HasSampleRateAndChannels();
- SLuint32 GetSLSampleRate();
- SLuint32 GetSLChannels();
- SLuint32 GetChannelCount();
-
- // The single global audio engine instance.
- static AudioEngine* audioEngine_;
-
- // Protects access to the shared decode buffer.
- android::Mutex decodeBufferLock_;
- // Buffer into which we put the audio data as we decode.
- // Protected by decodeBufferLock_.
- DecodeBuffer decodeBuffer_;
-
- // Protects access to the playingBuffers_ and freeBuffers_.
- android::Mutex playBufferLock_;
- // The buffers we're using for playback.
- std::queue<int16*> playingBuffers_;
- std::stack<int16*> freeBuffers_;
-
- // The time scaler.
- video_editing::SolaTimeScaler* timeScaler_;
-
- // The frame buffer, used for converting between PCM data and float for
- // time scaler.
- float* floatBuffer_;
- float* injectBuffer_;
-
- // Required when we create the audio player.
- // Set during the first callback from the decoder.
- // Guarded by callbackLock_.
- SLuint32 mSampleRate;
- SLuint32 mChannels;
-
- size_t targetFrames_;
- float windowDuration_;
- float windowOverlapDuration_;
- size_t maxPlayBufferCount_;
- float initialRate_;
- size_t startPositionMillis_;
- // The type of audio stream as defined by the STREAM_XXX constants in
- // android.media.AudioManager. These constant values actually match the
- // corresponding SL_ANDROID_STREAM_XXX constants defined by
- // include/SLES/OpenSLES_AndroidConfiguration.h
- int audioStreamType_;
-
- // The prefetch callback signal, for letting the prefetch callback method
- // indicate when it is done.
- android::Mutex prefetchLock_;
- android::Condition prefetchCondition_;
-
- // Protects access to the CallbackContext object.
- // I don't believe this to be necessary, I think that it's thread-confined,
- // but it also won't do any harm.
- android::Mutex callbackLock_;
-
- // Protects access to the shared member variables below.
- android::Mutex lock_;
- // Protected by lock_.
- // Stores the total duration of the track.
- SLmillisecond totalDurationMs_;
- // Protected by lock_.
- // Stores the current position of the decoder head.
- SLmillisecond decoderCurrentPosition_;
- // Protected by lock_.
- // Set externally via RequestStart(), this determines when we begin to
- // playback audio.
- // Until this is set to true, our audio player will remain stopped.
- bool startRequested_;
- // Protected by lock_.
- // Set externally via RequestStop(), this tells us top stop playing
- // and therefore shut everything down.
- bool stopRequested_;
- // Protected by lock_.
- // This is set to true once we reach the end of the decoder stream.
- bool finishedDecoding_;
-
- DISALLOW_COPY_AND_ASSIGN(AudioEngine);
-};
-
-#endif // FRAMEWORKS_EX_VARIABLESPEED_JNI_VARIABLESPEED_H_
diff --git a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java b/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java
deleted file mode 100644
index 1cc4012..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/EngineParameters.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.media.AudioManager;
-
-import javax.annotation.concurrent.Immutable;
-import javax.annotation.concurrent.NotThreadSafe;
-
-/**
- * Encapsulates the parameters required to configure the audio engine.
- * <p>
- * You should not need to use this class directly, it exists for the benefit of
- * this package and the classes contained therein.
- */
-@Immutable
-/*package*/ final class EngineParameters {
- private final int mTargetFrames;
- private final int mMaxPlayBufferCount;
- private final float mWindowDuration;
- private final float mWindowOverlapDuration;
- private final float mInitialRate;
- private final int mDecodeBufferInitialSize;
- private final int mDecodeBufferMaxSize;
- private final int mStartPositionMillis;
- private final int mAudioStreamType;
-
- public int getTargetFrames() {
- return mTargetFrames;
- }
-
- public int getMaxPlayBufferCount() {
- return mMaxPlayBufferCount;
- }
-
- public float getWindowDuration() {
- return mWindowDuration;
- }
-
- public float getWindowOverlapDuration() {
- return mWindowOverlapDuration;
- }
-
- public float getInitialRate() {
- return mInitialRate;
- }
-
- public int getDecodeBufferInitialSize() {
- return mDecodeBufferInitialSize;
- }
-
- public int getDecodeBufferMaxSize() {
- return mDecodeBufferMaxSize;
- }
-
- public int getStartPositionMillis() {
- return mStartPositionMillis;
- }
-
- public int getAudioStreamType() {
- return mAudioStreamType;
- }
-
- private EngineParameters(int targetFrames, int maxPlayBufferCount, float windowDuration,
- float windowOverlapDuration, float initialRate, int decodeBufferInitialSize,
- int decodeBufferMaxSize, int startPositionMillis, int audioStreamType) {
- mTargetFrames = targetFrames;
- mMaxPlayBufferCount = maxPlayBufferCount;
- mWindowDuration = windowDuration;
- mWindowOverlapDuration = windowOverlapDuration;
- mInitialRate = initialRate;
- mDecodeBufferInitialSize = decodeBufferInitialSize;
- mDecodeBufferMaxSize = decodeBufferMaxSize;
- mStartPositionMillis = startPositionMillis;
- mAudioStreamType = audioStreamType;
- }
-
- /**
- * We use the builder pattern to construct an {@link EngineParameters}
- * object.
- * <p>
- * This class is not thread safe, you should confine its use to one thread
- * or provide your own synchronization.
- */
- @NotThreadSafe
- public static class Builder {
- private int mTargetFrames = 1000;
- private int mMaxPlayBufferCount = 2;
- private float mWindowDuration = 0.08f;
- private float mWindowOverlapDuration = 0.008f;
- private float mInitialRate = 1.0f;
- private int mDecodeBufferInitialSize = 5 * 1024;
- private int mDecodeBufferMaxSize = 20 * 1024;
- private int mStartPositionMillis = 0;
- private int mAudioStreamType = AudioManager.STREAM_MUSIC;
-
- public EngineParameters build() {
- return new EngineParameters(mTargetFrames, mMaxPlayBufferCount,
- mWindowDuration, mWindowOverlapDuration, mInitialRate,
- mDecodeBufferInitialSize, mDecodeBufferMaxSize, mStartPositionMillis,
- mAudioStreamType);
- }
-
- public Builder maxPlayBufferCount(int maxPlayBufferCount) {
- mMaxPlayBufferCount = maxPlayBufferCount;
- return this;
- }
-
- public Builder windowDuration(int windowDuration) {
- mWindowDuration = windowDuration;
- return this;
- }
-
- public Builder windowOverlapDuration(int windowOverlapDuration) {
- mWindowOverlapDuration = windowOverlapDuration;
- return this;
- }
-
- public Builder initialRate(float initialRate) {
- mInitialRate = initialRate;
- return this;
- }
-
- public Builder decodeBufferInitialSize(int decodeBufferInitialSize) {
- mDecodeBufferInitialSize = decodeBufferInitialSize;
- return this;
- }
-
- public Builder decodeBufferMaxSize(int decodeBufferMaxSize) {
- mDecodeBufferMaxSize = decodeBufferMaxSize;
- return this;
- }
-
- public Builder startPositionMillis(int startPositionMillis) {
- mStartPositionMillis = startPositionMillis;
- return this;
- }
-
- public Builder audioStreamType(int audioStreamType) {
- mAudioStreamType = audioStreamType;
- return this;
- }
- }
-}
diff --git a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java
deleted file mode 100644
index 1c6a8cb..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerDataSource.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-import java.io.IOException;
-
-import javax.annotation.concurrent.Immutable;
-
-/**
- * Encapsulates the data source for a media player.
- * <p>
- * Is used to make the setting of the data source for a
- * {@link android.media.MediaPlayer} easier, or the calling of the correct
- * {@link VariableSpeedNative} method done correctly. You should not use this class
- * directly, it is for the benefit of the {@link VariableSpeed} implementation.
- */
-@Immutable
-/*package*/ class MediaPlayerDataSource {
- private final Context mContext;
- private final Uri mUri;
- private final String mPath;
-
- public MediaPlayerDataSource(Context context, Uri intentUri) {
- mContext = context;
- mUri = intentUri;
- mPath = null;
- }
-
- public MediaPlayerDataSource(String path) {
- mContext = null;
- mUri = null;
- mPath = path;
- }
-
- public void setAsSourceFor(MediaPlayer mediaPlayer) throws IOException {
- if (mContext != null) {
- mediaPlayer.setDataSource(mContext, mUri);
- } else {
- mediaPlayer.setDataSource(mPath);
- }
- }
-
- public void playNative() throws IOException {
- if (mContext != null) {
- VariableSpeedNative.playFromContext(mContext, mUri);
- } else {
- VariableSpeedNative.playUri(mPath);
- }
- }
-}
diff --git a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java
deleted file mode 100644
index 3b7b576..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/MediaPlayerProxy.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-import java.io.IOException;
-
-/**
- * Interface that supports a subset of the operations on {@link android.media.MediaPlayer}.
- *
- * <p>This subset is arbitrarily defined - at the moment it is the subset that the voicemail
- * playback requires.</p>
- *
- * <p>This interface exists to make alternate implementations to the standard media player
- * swappable, as well as making it much easier to test code that directly uses a media player.
- */
-public interface MediaPlayerProxy {
- void setOnErrorListener(MediaPlayer.OnErrorListener listener);
- void setOnCompletionListener(MediaPlayer.OnCompletionListener listener);
- void release();
- void reset();
- void setDataSource(String path) throws IllegalStateException, IOException;
- void setDataSource(Context context, Uri intentUri) throws IllegalStateException, IOException;
- void prepare() throws IOException;
- int getDuration();
- void seekTo(int startPosition);
- void start();
- boolean isReadyToPlay();
- boolean isPlaying();
- int getCurrentPosition();
- void pause();
- void setAudioStreamType(int streamType);
-}
diff --git a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java b/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java
deleted file mode 100644
index c9a9741..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/SingleThreadedMediaPlayerProxy.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-
-import java.io.IOException;
-
-/**
- * Simple wrapper around a {@link MediaPlayerProxy}, guaranteeing that every call made to the
- * MediaPlayerProxy is single-threaded.
- */
-public class SingleThreadedMediaPlayerProxy implements MediaPlayerProxy {
- private final MediaPlayerProxy mDelegate;
-
- public SingleThreadedMediaPlayerProxy(MediaPlayerProxy delegate) {
- mDelegate = delegate;
- }
-
- @Override
- public synchronized void setOnErrorListener(MediaPlayer.OnErrorListener listener) {
- mDelegate.setOnErrorListener(listener);
- }
-
- @Override
- public synchronized void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
- mDelegate.setOnCompletionListener(listener);
- }
-
- @Override
- public synchronized void release() {
- mDelegate.release();
- }
-
- @Override
- public synchronized void reset() {
- mDelegate.reset();
- }
-
- @Override
- public synchronized void setDataSource(String path) throws IllegalStateException, IOException {
- mDelegate.setDataSource(path);
- }
-
- @Override
- public synchronized void setDataSource(Context context, Uri intentUri)
- throws IllegalStateException, IOException {
- mDelegate.setDataSource(context, intentUri);
- }
-
- @Override
- public synchronized void prepare() throws IOException {
- mDelegate.prepare();
- }
-
- @Override
- public synchronized int getDuration() {
- return mDelegate.getDuration();
- }
-
- @Override
- public synchronized void seekTo(int startPosition) {
- mDelegate.seekTo(startPosition);
- }
-
- @Override
- public synchronized void start() {
- mDelegate.start();
- }
-
- @Override
- public synchronized boolean isReadyToPlay() {
- return mDelegate.isReadyToPlay();
- }
-
- @Override
- public synchronized boolean isPlaying() {
- return mDelegate.isPlaying();
- }
-
- @Override
- public synchronized int getCurrentPosition() {
- return mDelegate.getCurrentPosition();
- }
-
- public void setVariableSpeed(float rate) {
- ((VariableSpeed) mDelegate).setVariableSpeed(rate);
- }
-
- @Override
- public synchronized void pause() {
- mDelegate.pause();
- }
-
- @Override
- public void setAudioStreamType(int streamType) {
- mDelegate.setAudioStreamType(streamType);
- }
-}
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
deleted file mode 100644
index e44a375..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeed.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import com.google.common.base.Preconditions;
-
-import android.content.Context;
-import android.media.MediaPlayer;
-import android.net.Uri;
-import android.util.Log;
-
-import java.io.IOException;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.concurrent.GuardedBy;
-import javax.annotation.concurrent.ThreadSafe;
-
-/**
- * This class behaves in a similar fashion to the MediaPlayer, but by using
- * native code it is able to use variable-speed playback.
- * <p>
- * This class is thread-safe. It's not yet perfect though, see the unit tests
- * for details - there is insufficient testing for the concurrent logic. You are
- * probably best advised to use thread confinment until the unit tests are more
- * complete with regards to threading.
- * <p>
- * The easiest way to ensure that calls to this class are not made concurrently
- * (besides only ever accessing it from one thread) is to wrap it in a
- * {@link SingleThreadedMediaPlayerProxy}, designed just for this purpose.
- */
-@ThreadSafe
-public class VariableSpeed implements MediaPlayerProxy {
- private static final String TAG = "VariableSpeed";
-
- private final Executor mExecutor;
- private final Object lock = new Object();
- @GuardedBy("lock") private MediaPlayerDataSource mDataSource;
- @GuardedBy("lock") private boolean mIsPrepared;
- @GuardedBy("lock") private boolean mHasDuration;
- @GuardedBy("lock") private boolean mHasStartedPlayback;
- @GuardedBy("lock") private CountDownLatch mEngineInitializedLatch;
- @GuardedBy("lock") private CountDownLatch mPlaybackFinishedLatch;
- @GuardedBy("lock") private boolean mHasBeenReleased = true;
- @GuardedBy("lock") private boolean mIsReadyToReUse = true;
- @GuardedBy("lock") private boolean mSkipCompletionReport;
- @GuardedBy("lock") private int mStartPosition;
- @GuardedBy("lock") private float mCurrentPlaybackRate = 1.0f;
- @GuardedBy("lock") private int mDuration;
- @GuardedBy("lock") private MediaPlayer.OnCompletionListener mCompletionListener;
- @GuardedBy("lock") private int mAudioStreamType;
-
- private VariableSpeed(Executor executor) throws UnsupportedOperationException {
- Preconditions.checkNotNull(executor);
- mExecutor = executor;
- try {
- VariableSpeedNative.loadLibrary();
- } catch (UnsatisfiedLinkError e) {
- throw new UnsupportedOperationException("could not load library", e);
- } catch (SecurityException e) {
- throw new UnsupportedOperationException("could not load library", e);
- }
- reset();
- }
-
- public static MediaPlayerProxy createVariableSpeed(Executor executor)
- throws UnsupportedOperationException {
- return new SingleThreadedMediaPlayerProxy(new VariableSpeed(executor));
- }
-
- @Override
- public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- mCompletionListener = listener;
- }
- }
-
- @Override
- public void setOnErrorListener(MediaPlayer.OnErrorListener listener) {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- // TODO: I haven't actually added any error listener code.
- }
- }
-
- @Override
- public void release() {
- synchronized (lock) {
- if (mHasBeenReleased) {
- return;
- }
- mHasBeenReleased = true;
- }
- stopCurrentPlayback();
- boolean requiresShutdown = false;
- synchronized (lock) {
- requiresShutdown = hasEngineBeenInitialized();
- }
- if (requiresShutdown) {
- VariableSpeedNative.shutdownEngine();
- }
- synchronized (lock) {
- mIsReadyToReUse = true;
- }
- }
-
- private boolean hasEngineBeenInitialized() {
- return mEngineInitializedLatch.getCount() <= 0;
- }
-
- private boolean hasPlaybackFinished() {
- return mPlaybackFinishedLatch.getCount() <= 0;
- }
-
- /**
- * Stops the current playback, returns once it has stopped.
- */
- private void stopCurrentPlayback() {
- boolean isPlaying;
- CountDownLatch engineInitializedLatch;
- CountDownLatch playbackFinishedLatch;
- synchronized (lock) {
- isPlaying = mHasStartedPlayback && !hasPlaybackFinished();
- engineInitializedLatch = mEngineInitializedLatch;
- playbackFinishedLatch = mPlaybackFinishedLatch;
- if (isPlaying) {
- mSkipCompletionReport = true;
- }
- }
- if (isPlaying) {
- waitForLatch(engineInitializedLatch);
- VariableSpeedNative.stopPlayback();
- waitForLatch(playbackFinishedLatch);
- }
- }
-
- private void waitForLatch(CountDownLatch latch) {
- try {
- boolean success = latch.await(1, TimeUnit.SECONDS);
- if (!success) {
- reportException(new TimeoutException("waited too long"));
- }
- } catch (InterruptedException e) {
- // Preserve the interrupt status, though this is unexpected.
- Thread.currentThread().interrupt();
- reportException(e);
- }
- }
-
- @Override
- public void setDataSource(Context context, Uri intentUri) {
- checkNotNull(context, "context");
- checkNotNull(intentUri, "intentUri");
- innerSetDataSource(new MediaPlayerDataSource(context, intentUri));
- }
-
- @Override
- public void setDataSource(String path) {
- checkNotNull(path, "path");
- innerSetDataSource(new MediaPlayerDataSource(path));
- }
-
- private void innerSetDataSource(MediaPlayerDataSource source) {
- checkNotNull(source, "source");
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mDataSource == null, "cannot setDataSource more than once");
- mDataSource = source;
- }
- }
-
- @Override
- public void reset() {
- boolean requiresRelease;
- synchronized (lock) {
- requiresRelease = !mHasBeenReleased;
- }
- if (requiresRelease) {
- release();
- }
- synchronized (lock) {
- check(mHasBeenReleased && mIsReadyToReUse, "to re-use, must call reset after release");
- mDataSource = null;
- mIsPrepared = false;
- mHasDuration = false;
- mHasStartedPlayback = false;
- mEngineInitializedLatch = new CountDownLatch(1);
- mPlaybackFinishedLatch = new CountDownLatch(1);
- mHasBeenReleased = false;
- mIsReadyToReUse = false;
- mSkipCompletionReport = false;
- mStartPosition = 0;
- mDuration = 0;
- }
- }
-
- @Override
- public void prepare() throws IOException {
- MediaPlayerDataSource dataSource;
- int audioStreamType;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mDataSource != null, "must setDataSource before you prepare");
- check(!mIsPrepared, "cannot prepare more than once");
- mIsPrepared = true;
- dataSource = mDataSource;
- audioStreamType = mAudioStreamType;
- }
- // NYI This should become another executable that we can wait on.
- MediaPlayer mediaPlayer = new MediaPlayer();
- mediaPlayer.setAudioStreamType(audioStreamType);
- dataSource.setAsSourceFor(mediaPlayer);
- mediaPlayer.prepare();
- synchronized (lock) {
- check(!mHasDuration, "can't have duration, this is impossible");
- mHasDuration = true;
- mDuration = mediaPlayer.getDuration();
- }
- mediaPlayer.release();
- }
-
- @Override
- public int getDuration() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mHasDuration, "you haven't called prepare, can't get the duration");
- return mDuration;
- }
- }
-
- @Override
- public void seekTo(int startPosition) {
- boolean currentlyPlaying;
- MediaPlayerDataSource dataSource;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mHasDuration, "you can't seek until you have prepared");
- currentlyPlaying = mHasStartedPlayback && !hasPlaybackFinished();
- mStartPosition = Math.min(startPosition, mDuration);
- dataSource = mDataSource;
- }
- if (currentlyPlaying) {
- stopAndStartPlayingAgain(dataSource);
- }
- }
-
- private void stopAndStartPlayingAgain(MediaPlayerDataSource source) {
- stopCurrentPlayback();
- reset();
- innerSetDataSource(source);
- try {
- prepare();
- } catch (IOException e) {
- reportException(e);
- return;
- }
- start();
- return;
- }
-
- private void reportException(Exception e) {
- Log.e(TAG, "playback error:", e);
- }
-
- @Override
- public void start() {
- MediaPlayerDataSource restartWithThisDataSource = null;
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- check(mIsPrepared, "must have prepared before you can start");
- if (!mHasStartedPlayback) {
- // Playback has not started. Start it.
- mHasStartedPlayback = true;
- EngineParameters engineParameters = new EngineParameters.Builder()
- .initialRate(mCurrentPlaybackRate)
- .startPositionMillis(mStartPosition)
- .audioStreamType(mAudioStreamType)
- .build();
- VariableSpeedNative.initializeEngine(engineParameters);
- VariableSpeedNative.startPlayback();
- mEngineInitializedLatch.countDown();
- mExecutor.execute(new PlaybackRunnable(mDataSource));
- } else {
- // Playback has already started. Restart it, without holding the
- // lock.
- restartWithThisDataSource = mDataSource;
- }
- }
- if (restartWithThisDataSource != null) {
- stopAndStartPlayingAgain(restartWithThisDataSource);
- }
- }
-
- /** A Runnable capable of driving the native audio playback methods. */
- private final class PlaybackRunnable implements Runnable {
- private final MediaPlayerDataSource mInnerSource;
-
- public PlaybackRunnable(MediaPlayerDataSource source) {
- mInnerSource = source;
- }
-
- @Override
- public void run() {
- try {
- mInnerSource.playNative();
- } catch (IOException e) {
- Log.e(TAG, "error playing audio", e);
- }
- MediaPlayer.OnCompletionListener completionListener;
- boolean skipThisCompletionReport;
- synchronized (lock) {
- completionListener = mCompletionListener;
- skipThisCompletionReport = mSkipCompletionReport;
- mPlaybackFinishedLatch.countDown();
- }
- if (!skipThisCompletionReport && completionListener != null) {
- completionListener.onCompletion(null);
- }
- }
- }
-
- @Override
- public boolean isReadyToPlay() {
- synchronized (lock) {
- return !mHasBeenReleased && mHasDuration;
- }
- }
-
- @Override
- public boolean isPlaying() {
- synchronized (lock) {
- return isReadyToPlay() && mHasStartedPlayback && !hasPlaybackFinished();
- }
- }
-
- @Override
- public int getCurrentPosition() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- if (!mHasStartedPlayback) {
- return 0;
- }
- if (!hasEngineBeenInitialized()) {
- return 0;
- }
- if (!hasPlaybackFinished()) {
- return VariableSpeedNative.getCurrentPosition();
- }
- return mDuration;
- }
- }
-
- @Override
- public void pause() {
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- }
- stopCurrentPlayback();
- }
-
- public void setVariableSpeed(float rate) {
- // TODO: are there situations in which the engine has been destroyed, so
- // that this will segfault?
- synchronized (lock) {
- check(!mHasBeenReleased, "has been released, reset before use");
- // TODO: This too is wrong, once we've started preparing the variable speed set
- // will not be enough.
- if (mHasStartedPlayback) {
- VariableSpeedNative.setVariableSpeed(rate);
- }
- mCurrentPlaybackRate = rate;
- }
- }
-
- private void check(boolean condition, String exception) {
- if (!condition) {
- throw new IllegalStateException(exception);
- }
- }
-
- private void checkNotNull(Object argument, String argumentName) {
- if (argument == null) {
- throw new IllegalArgumentException(argumentName + " must not be null");
- }
- }
-
- @Override
- public void setAudioStreamType(int audioStreamType) {
- synchronized (lock) {
- mAudioStreamType = audioStreamType;
- }
- }
-}
diff --git a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java b/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java
deleted file mode 100644
index 07195db..0000000
--- a/variablespeed/src/com/android/ex/variablespeed/VariableSpeedNative.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import com.android.common.io.MoreCloseables;
-
-import android.content.Context;
-import android.content.res.AssetFileDescriptor;
-import android.net.Uri;
-
-import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.lang.reflect.Field;
-
-/**
- * Provides all the native calls through to the underlying audio library.
- * <p>
- * You should not use this class directly. Prefer to use the {@link VariableSpeed}
- * class instead.
- */
-/*package*/ class VariableSpeedNative {
- /*package*/ static void loadLibrary() throws UnsatisfiedLinkError, SecurityException {
- System.loadLibrary("variablespeed");
- }
-
- /*package*/ static boolean playFromContext(Context context, Uri uri)
- throws FileNotFoundException {
- AssetFileDescriptor afd = context.getContentResolver().openAssetFileDescriptor(uri, "r");
- try {
- FileDescriptor fileDescriptor = afd.getFileDescriptor();
- Field descriptorField = fileDescriptor.getClass().getDeclaredField("descriptor");
- descriptorField.setAccessible(true);
- int fd = descriptorField.getInt(fileDescriptor);
- VariableSpeedNative.playFileDescriptor(fd, afd.getStartOffset(), afd.getLength());
- return true;
- } catch (SecurityException e) {
- // Fall through.
- } catch (NoSuchFieldException e) {
- // Fall through.
- } catch (IllegalArgumentException e) {
- // Fall through.
- } catch (IllegalAccessException e) {
- // Fall through.
- } finally {
- MoreCloseables.closeQuietly(afd);
- }
- return false;
- }
-
- /*package*/ static native void playUri(String uri);
-
- /*package*/ static native void playFileDescriptor(int fd, long offset, long length);
-
- /*package*/ static native void setVariableSpeed(float speed);
-
- /*package*/ static native void startPlayback();
-
- /*package*/ static native void stopPlayback();
-
- /*package*/ static native void shutdownEngine();
-
- /*package*/ static native int getCurrentPosition();
-
- /*package*/ static native int getTotalDuration();
-
- /*package*/ static void initializeEngine(EngineParameters params) {
- initializeEngine(params.getTargetFrames(),
- params.getWindowDuration(), params.getWindowOverlapDuration(),
- params.getMaxPlayBufferCount(), params.getInitialRate(),
- params.getDecodeBufferInitialSize(), params.getDecodeBufferMaxSize(),
- params.getStartPositionMillis(), params.getAudioStreamType());
- }
-
- private static native void initializeEngine(int targetFrames,
- float windowDuration, float windowOverlapDuration, int maxPlayBufferCount,
- float initialRate, int decodeBufferInitialSize, int decodeBufferMaxSize,
- int startPositionMillis, int audioStreamType);
-}
diff --git a/variablespeed/tests/Android.mk b/variablespeed/tests/Android.mk
deleted file mode 100644
index fe386a2..0000000
--- a/variablespeed/tests/Android.mk
+++ /dev/null
@@ -1,28 +0,0 @@
-# Copyright (C) 2011 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.
-
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_CERTIFICATE := shared
-LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_MODULE_TAGS := tests
-LOCAL_PACKAGE_NAME := AndroidExVariablespeedTests
-LOCAL_SDK_VERSION := current
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-ex-variablespeed
-LOCAL_REQUIRED_MODULES := libvariablespeed
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/variablespeed/tests/AndroidManifest.xml b/variablespeed/tests/AndroidManifest.xml
deleted file mode 100644
index a7d2ed1..0000000
--- a/variablespeed/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 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.
--->
-
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.ex.variablespeed.tests"
->
- <application>
- <uses-library
- android:name="android.test.runner"
- />
- </application>
- <instrumentation
- android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.ex.variablespeed.tests"
- android:label="Android Variablespeed Library Tests"
- />
- <!-- The tests need these permissions to add test voicemail entries. -->
- <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
-</manifest>
diff --git a/variablespeed/tests/assets/README.txt b/variablespeed/tests/assets/README.txt
deleted file mode 100644
index 3e69968..0000000
--- a/variablespeed/tests/assets/README.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Files quick_test_recording.mp3 and count_and_test.3gpp are copyright 2011 by
-Hugo Hudson and are licensed under a
-Creative Commons Attribution 3.0 Unported License:
- http://creativecommons.org/licenses/by/3.0/
diff --git a/variablespeed/tests/assets/count_and_test.3gpp b/variablespeed/tests/assets/count_and_test.3gpp
deleted file mode 100644
index c71a423..0000000
--- a/variablespeed/tests/assets/count_and_test.3gpp
+++ /dev/null
Binary files differ
diff --git a/variablespeed/tests/assets/fake_voicemail.mp3 b/variablespeed/tests/assets/fake_voicemail.mp3
deleted file mode 100644
index 23fc415..0000000
--- a/variablespeed/tests/assets/fake_voicemail.mp3
+++ /dev/null
Binary files differ
diff --git a/variablespeed/tests/assets/fake_voicemail2.mp3 b/variablespeed/tests/assets/fake_voicemail2.mp3
deleted file mode 100644
index 854186d..0000000
--- a/variablespeed/tests/assets/fake_voicemail2.mp3
+++ /dev/null
Binary files differ
diff --git a/variablespeed/tests/assets/quick_test_recording.mp3 b/variablespeed/tests/assets/quick_test_recording.mp3
deleted file mode 100644
index ad7cb9c..0000000
--- a/variablespeed/tests/assets/quick_test_recording.mp3
+++ /dev/null
Binary files differ
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java b/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java
deleted file mode 100644
index ef2648c..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableCompletionListener.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.media.MediaPlayer;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-// TODO: There is sufficent similarity between this and the awaitable error listener that I should
-// extract a common base class.
-/** Implementation of {@link MediaPlayer.OnErrorListener} that we can wait for in tests. */
-@ThreadSafe
-public class AwaitableCompletionListener implements MediaPlayer.OnCompletionListener {
- private final BlockingQueue<Object> mQueue = new LinkedBlockingQueue<Object>();
-
- @Override
- public void onCompletion(MediaPlayer mp) {
- try {
- mQueue.put(new Object());
- } catch (InterruptedException e) {
- // This should not happen in practice, the queue is unbounded so this method will not
- // block.
- // If this thread is using interrupt to shut down, preserve interrupt status and return.
- Thread.currentThread().interrupt();
- }
- }
-
- public void awaitOneCallback(long timeout, TimeUnit unit) throws InterruptedException,
- TimeoutException {
- if (mQueue.poll(timeout, unit) == null) {
- throw new TimeoutException();
- }
- }
-
- public void assertNoMoreCallbacks() {
- if (mQueue.peek() != null) {
- throw new IllegalStateException("there was an unexpected callback on the queue");
- }
- }
-}
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java b/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java
deleted file mode 100644
index bf5fb42..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/AwaitableErrorListener.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.media.MediaPlayer;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.annotation.concurrent.ThreadSafe;
-
-/** Implementation of {@link MediaPlayer.OnCompletionListener} that we can wait for in tests. */
-@ThreadSafe
-public class AwaitableErrorListener implements MediaPlayer.OnErrorListener {
- private final BlockingQueue<Object> mQueue = new LinkedBlockingQueue<Object>();
- private volatile boolean mOnErrorReturnValue = true;
-
- @Override
- public boolean onError(MediaPlayer mp, int what, int extra) {
- addAnObjectToTheQueue();
- return mOnErrorReturnValue;
- }
-
- public void setOnErrorReturnValue(boolean value) {
- mOnErrorReturnValue = value;
- }
-
- private void addAnObjectToTheQueue() {
- try {
- mQueue.put(new Object());
- } catch (InterruptedException e) {
- // This should not happen in practice, the queue is unbounded so this method will not
- // block.
- // If this thread is using interrupt to shut down, preserve interrupt status and return.
- Thread.currentThread().interrupt();
- }
- }
-
- public void awaitOneCallback(long timeout, TimeUnit unit) throws InterruptedException,
- TimeoutException {
- if (mQueue.poll(timeout, unit) == null) {
- throw new TimeoutException();
- }
- }
-
- public void assertNoMoreCallbacks() {
- if (mQueue.peek() != null) {
- throw new IllegalStateException("there was an unexpected callback on the queue");
- }
- }
-}
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java b/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java
deleted file mode 100644
index 429f2cc..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/DynamicProxy.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
-
-/**
- * Contains a utility method for adapting a given interface against a real implementation.
- * <p>
- * This class is thead-safe.
- */
-public class DynamicProxy {
- /**
- * Dynamically adapts a given interface against a delegate object.
- * <p>
- * For the given {@code clazz} object, which should be an interface, we return a new dynamic
- * proxy object implementing that interface, which will forward all method calls made on the
- * interface onto the delegate object.
- * <p>
- * In practice this means that you can make it appear as though {@code delegate} implements the
- * {@code clazz} interface, without this in practice being the case. As an example, if you
- * create an interface representing the {@link android.media.MediaPlayer}, you could pass this
- * interface in as the first argument, and a real {@link android.media.MediaPlayer} in as the
- * second argument, and now calls to the interface will be automatically sent on to the real
- * media player. The reason you may be interested in doing this in the first place is that this
- * allows you to test classes that have dependencies that are final or cannot be easily mocked.
- */
- // This is safe, because we know that proxy instance implements the interface.
- @SuppressWarnings("unchecked")
- public static <T> T dynamicProxy(Class<T> clazz, final Object delegate) {
- InvocationHandler invoke = new InvocationHandler() {
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- try {
- return delegate.getClass()
- .getMethod(method.getName(), method.getParameterTypes())
- .invoke(delegate, args);
- } catch (InvocationTargetException e) {
- throw e.getCause();
- }
- }
- };
- return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] { clazz }, invoke);
- }
-}
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java b/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java
deleted file mode 100644
index 37a0cce..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/MediaPlayerProxyTestCase.java
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import com.google.common.io.Closeables;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.res.AssetManager;
-import android.net.Uri;
-import android.provider.VoicemailContract;
-import android.test.InstrumentationTestCase;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Base test for checking implementations of {@link MediaPlayerProxy}.
- * <p>
- * The purpose behind this class is to collect tests that implementations of
- * MediaPlayerProxy should support.
- * <p>
- * This allows tests to show that the built-in {@link android.media.MediaPlayer} is performing
- * correctly with respect to the contract it provides, i.e. test my understanding of that contract.
- * <p>
- * It allows us to test the current {@link VariableSpeed} implementation, and make sure that this
- * too corresponds with the MediaPlayer implementation.
- * <p>
- * These tests cannot be run on their own - you must provide a concrete subclass of this test case -
- * and in that subclass you will provide an implementation of the abstract
- * {@link #createTestMediaPlayer()} method to construct the player you would like to test. Every
- * test will construct the player in {@link #setUp()} and release it in {@link #tearDown()}.
- */
-public abstract class MediaPlayerProxyTestCase extends InstrumentationTestCase {
- private static final float ERROR_TOLERANCE_MILLIS = 1000f;
-
- /** The phone number to use when inserting test data into the content provider. */
- private static final String CONTACT_NUMBER = "01234567890";
-
- /**
- * A map from filename + mime type to the uri we can use to play from the content provider.
- * <p>
- * This is lazily filled in by the {@link #getTestContentUri(String, String)} method.
- * <p>
- * This map is keyed from the concatenation of filename and mime type with a "+" separator, it's
- * not perfect but it doesn't matter in this test code.
- */
- private final Map<String, Uri> mContentUriMap = new HashMap<String, Uri>();
-
- /** The system under test. */
- private MediaPlayerProxy mPlayer;
-
- private AwaitableCompletionListener mCompletionListener;
- private AwaitableErrorListener mErrorListener;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mPlayer = createTestMediaPlayer();
- mCompletionListener = new AwaitableCompletionListener();
- mErrorListener = new AwaitableErrorListener();
- }
-
- @Override
- protected void tearDown() throws Exception {
- mCompletionListener = null;
- mErrorListener = null;
- mPlayer.release();
- mPlayer = null;
- cleanupContentUriIfNecessary();
- super.tearDown();
- }
-
- public abstract MediaPlayerProxy createTestMediaPlayer() throws Exception;
-
- /** Annotation to indicate that test should throw an {@link IllegalStateException}. */
- @Retention(RetentionPolicy.RUNTIME)
- public @interface ShouldThrowIllegalStateException {
- }
-
- @Override
- protected void runTest() throws Throwable {
- // Tests annotated with ShouldThrowIllegalStateException will fail if they don't.
- // Tests not annotated this way are run as normal.
- if (getClass().getMethod(getName()).isAnnotationPresent(
- ShouldThrowIllegalStateException.class)) {
- try {
- super.runTest();
- fail("Expected this method to throw an IllegalStateException, but it didn't");
- } catch (IllegalStateException e) {
- // Expected.
- }
- } else {
- super.runTest();
- }
- }
-
- public void testReleaseMultipleTimesHasNoEffect() throws Exception {
- mPlayer.release();
- mPlayer.release();
- }
-
- public void testResetOnNewlyCreatedObject() throws Exception {
- mPlayer.reset();
- }
-
- public void testSetDataSource() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- }
-
- @ShouldThrowIllegalStateException
- public void testSetDataSourceTwice_ShouldFailWithIllegalState() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- }
-
- @ShouldThrowIllegalStateException
- public void testSetDataSourceAfterRelease_ShouldFailWithIllegalState() throws Exception {
- mPlayer.release();
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- }
-
- public void testPrepare() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- }
-
- @ShouldThrowIllegalStateException
- public void testPrepareBeforeSetDataSource_ShouldFail() throws Exception {
- mPlayer.prepare();
- }
-
- @ShouldThrowIllegalStateException
- public void testPrepareTwice_ShouldFailWithIllegalState() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.prepare();
- }
-
- public void testStartThenImmediatelyRelease() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- }
-
- public void testPlayABitThenRelease() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- }
-
- public void testPlayFully() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testGetDuration() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- int duration = mPlayer.getDuration();
- assertTrue("duration was " + duration, duration > 0);
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- assertEquals(duration, mPlayer.getDuration());
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- assertEquals(duration, mPlayer.getDuration());
- }
-
- @ShouldThrowIllegalStateException
- public void testGetDurationAfterRelease_ShouldFail() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.release();
- mPlayer.getDuration();
- }
-
- @ShouldThrowIllegalStateException
- public void testGetPositionAfterRelease_ShouldFail() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.release();
- mPlayer.getCurrentPosition();
- }
-
- public void testGetCurrentPosition_ZeroBeforePlaybackBegins() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- assertEquals(0, mPlayer.getCurrentPosition());
- mPlayer.prepare();
- assertEquals(0, mPlayer.getCurrentPosition());
- }
-
- public void testGetCurrentPosition_DuringPlayback() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- assertEquals(2000, mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS);
- }
-
- public void testGetCurrentPosition_FinishedPlaying() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- assertEquals(mPlayer.getDuration(), mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS);
- }
-
- public void testGetCurrentPosition_DuringPlaybackWithSeek() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.seekTo(1500);
- mPlayer.start();
- Thread.sleep(1500);
- assertEquals(3000, mPlayer.getCurrentPosition(), ERROR_TOLERANCE_MILLIS);
- }
-
- public void testSeekHalfWayBeforePlaying() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- assertTrue(mPlayer.getDuration() > 0);
- mPlayer.seekTo(mPlayer.getDuration() / 2);
- mPlayer.start();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testHalfWaySeekWithStutteringAudio() throws Exception {
- // The audio contained in this file has a stutter if we seek to half way and play.
- // It shouldn't have.
- setDataSourceFromContentProvider(mPlayer, "fake_voicemail2.mp3", "audio/mp3");
- mPlayer.prepare();
- assertTrue(mPlayer.getDuration() > 0);
- mPlayer.seekTo(mPlayer.getDuration() / 2);
- mPlayer.start();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testResetWithoutReleaseAndThenReUse() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.reset();
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.seekTo(mPlayer.getDuration() / 2);
- mPlayer.start();
- Thread.sleep(1000);
- }
-
- public void testResetAfterPlaybackThenReUse() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.prepare();
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- mPlayer.reset();
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- }
-
- public void testResetDuringPlaybackThenReUse() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- mPlayer.reset();
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- }
-
- public void testFinishPlayingThenSeekToHalfWayThenPlayAgain() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- mPlayer.seekTo(mPlayer.getDuration() / 2);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testPause_DuringPlayback() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- assertTrue(mPlayer.isPlaying());
- Thread.sleep(2000);
- assertTrue(mPlayer.isPlaying());
- mPlayer.pause();
- assertFalse(mPlayer.isPlaying());
- }
-
- public void testPause_DoesNotInvokeCallback() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mPlayer.pause();
- Thread.sleep(200);
- mCompletionListener.assertNoMoreCallbacks();
- }
-
- public void testReset_DoesNotInvokeCallback() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mPlayer.reset();
- Thread.sleep(200);
- mCompletionListener.assertNoMoreCallbacks();
- }
-
- public void testPause_MultipleTimes() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.start();
- Thread.sleep(2000);
- mPlayer.pause();
- mPlayer.pause();
- }
-
- public void testDoubleStartWaitingForFinish() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testTwoFastConsecutiveStarts() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- Thread.sleep(200);
- mCompletionListener.assertNoMoreCallbacks();
- }
-
- public void testThreeFastConsecutiveStarts() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mPlayer.start();
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- Thread.sleep(4000);
- mCompletionListener.assertNoMoreCallbacks();
- }
-
- public void testSeekDuringPlayback() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- Thread.sleep(2000);
- mPlayer.seekTo(0);
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- Thread.sleep(200);
- mCompletionListener.assertNoMoreCallbacks();
- }
-
- public void testPlaySingleChannelLowSampleRate3gppFile() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "count_and_test.3gpp", "audio/3gpp");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testPlayTwoDifferentTypesWithSameMediaPlayer() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- mPlayer.reset();
- setDataSourceFromContentProvider(mPlayer, "count_and_test.3gpp", "audio/3gpp");
- mPlayer.prepare();
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testIllegalPreparingDoesntFireErrorListener() throws Exception {
- mPlayer.setOnErrorListener(mErrorListener);
- try {
- mPlayer.prepare();
- fail("This should have thrown an IllegalStateException");
- } catch (IllegalStateException e) {
- // Good, expected.
- }
- mErrorListener.assertNoMoreCallbacks();
- }
-
- public void testSetDataSourceForMissingFile_ThrowsIOExceptionInPrepare() throws Exception {
- mPlayer.setOnErrorListener(mErrorListener);
- mPlayer.setDataSource("/this/file/does/not/exist/");
- try {
- mPlayer.prepare();
- fail("Should have thrown IOException");
- } catch (IOException e) {
- // Good, expected.
- }
- // Synchronous prepare does not report errors to the error listener.
- mErrorListener.assertNoMoreCallbacks();
- }
-
- public void testRepeatedlySeekingDuringPlayback() throws Exception {
- // Start playback then seek repeatedly during playback to the same point.
- // The real media player should play a stuttering audio, hopefully my player does too.
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- Thread.sleep(500);
- for (int i = 0; i < 40; ++i) {
- Thread.sleep(200);
- mPlayer.seekTo(2000);
- }
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testRepeatedlySeekingDuringPlaybackRandomAndVeryFast() throws Exception {
- setDataSourceFromContentProvider(mPlayer, "quick_test_recording.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- Thread.sleep(500);
- for (int i = 0; i < 40; ++i) {
- Thread.sleep(250);
- mPlayer.seekTo(1500 + (int) (Math.random() * 1000));
- }
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- }
-
- public void testSeekToEndThenPlayThenRateChangeCrash() throws Exception {
- // Unit test for this bug: http://b/5140693
- // This test proves that the bug is fixed.
- setDataSourceFromContentProvider(mPlayer, "fake_voicemail.mp3", "audio/mp3");
- mPlayer.prepare();
- mPlayer.seekTo(mPlayer.getDuration() - 1);
- mPlayer.setOnCompletionListener(mCompletionListener);
- mPlayer.start();
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- // Prior to the fix, this next line was causing a crash.
- // The reason behind this was due to our having seeked so close to the end of the file
- // that insufficient data was being read, and thus we weren't able to yet determine the
- // sample rate and number of channels, which was causing an assertion failure when trying
- // to create the time scaler.
- setVariableSpeedRateIfSupported(1.0f);
- }
-
- public void testVariableSpeedRateChangeAtDifferentTimes() throws Exception {
- // Just check that we can set the rate at any point during playback.
- setVariableSpeedRateIfSupported(1.05f);
- setDataSourceFromContentProvider(mPlayer, "fake_voicemail.mp3", "audio/mp3");
- setVariableSpeedRateIfSupported(1.10f);
- mPlayer.prepare();
- setVariableSpeedRateIfSupported(1.15f);
- mPlayer.seekTo(mPlayer.getDuration() / 2);
- setVariableSpeedRateIfSupported(1.20f);
- mPlayer.setOnCompletionListener(mCompletionListener);
- setVariableSpeedRateIfSupported(1.25f);
- mPlayer.start();
- setVariableSpeedRateIfSupported(1.30f);
- mCompletionListener.awaitOneCallback(10, TimeUnit.SECONDS);
- setVariableSpeedRateIfSupported(1.35f);
- }
-
- /**
- * If we have a variable speed media player proxy, set the variable speed rate.
- * <p>
- * If we don't have a variable speed media player proxy, this method will be a no-op.
- */
- private void setVariableSpeedRateIfSupported(float rate) {
- if (mPlayer instanceof SingleThreadedMediaPlayerProxy) {
- ((SingleThreadedMediaPlayerProxy) mPlayer).setVariableSpeed(rate);
- } else if (mPlayer instanceof VariableSpeed) {
- ((VariableSpeed) mPlayer).setVariableSpeed(rate);
- }
- }
-
- /**
- * Gets the {@link Uri} for the test audio content we should play.
- * <p>
- * If this is the first time we've called this method, for a given file type and mime type, then
- * we'll have to insert some data into the content provider so that we can play it.
- * <p>
- * This is not thread safe, but doesn't need to be because all unit tests are executed from a
- * single thread, sequentially.
- */
- private Uri getTestContentUri(String assetFilename, String assetMimeType) throws IOException {
- String key = keyFor(assetFilename, assetMimeType);
- if (mContentUriMap.containsKey(key)) {
- return mContentUriMap.get(key);
- }
- ContentValues values = new ContentValues();
- values.put(VoicemailContract.Voicemails.DATE, String.valueOf(System.currentTimeMillis()));
- values.put(VoicemailContract.Voicemails.NUMBER, CONTACT_NUMBER);
- values.put(VoicemailContract.Voicemails.MIME_TYPE, assetMimeType);
- String packageName = getInstrumentation().getTargetContext().getPackageName();
- Uri uri = getContentResolver().insert(
- VoicemailContract.Voicemails.buildSourceUri(packageName), values);
- AssetManager assets = getAssets();
- OutputStream outputStream = null;
- InputStream inputStream = null;
- try {
- inputStream = assets.open(assetFilename);
- outputStream = getContentResolver().openOutputStream(uri);
- copyBetweenStreams(inputStream, outputStream);
- mContentUriMap.put(key, uri);
- return uri;
- } finally {
- Closeables.closeQuietly(outputStream);
- Closeables.closeQuietly(inputStream);
- }
- }
-
- private String keyFor(String assetFilename, String assetMimeType) {
- return assetFilename + "+" + assetMimeType;
- }
-
- public void copyBetweenStreams(InputStream in, OutputStream out) throws IOException {
- byte[] buffer = new byte[1024];
- int bytesRead;
- while ((bytesRead = in.read(buffer)) != -1) {
- out.write(buffer, 0, bytesRead);
- }
- }
-
- private void cleanupContentUriIfNecessary() {
- for (Uri uri : mContentUriMap.values()) {
- getContentResolver().delete(uri, null, null);
- }
- mContentUriMap.clear();
- }
-
- private void setDataSourceFromContentProvider(MediaPlayerProxy player, String assetFilename,
- String assetMimeType) throws IOException {
- player.setDataSource(getInstrumentation().getTargetContext(),
- getTestContentUri(assetFilename, assetMimeType));
- }
-
- private ContentResolver getContentResolver() {
- return getInstrumentation().getContext().getContentResolver();
- }
-
- private AssetManager getAssets() {
- return getInstrumentation().getContext().getAssets();
- }
-}
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java b/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java
deleted file mode 100644
index 7f12671..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/RealMediaPlayerTest.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.media.MediaPlayer;
-
-import java.util.concurrent.Callable;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Tests that MediaPlayerProxyTestCase contains reasonable tests with a real {@link MediaPlayer}.
- */
-public class RealMediaPlayerTest extends MediaPlayerProxyTestCase {
- @Override
- public MediaPlayerProxy createTestMediaPlayer() throws Exception {
- // We have to construct the MediaPlayer on the main thread (or at least on a thread with an
- // associated looper) otherwise we don't get sent the messages when callbacks should be
- // invoked. I've raised a bug for this: http://b/4602011.
- Callable<MediaPlayer> callable = new Callable<MediaPlayer>() {
- @Override
- public MediaPlayer call() throws Exception {
- return new MediaPlayer();
- }
- };
- FutureTask<MediaPlayer> future = new FutureTask<MediaPlayer>(callable);
- getInstrumentation().runOnMainSync(future);
- return DynamicProxy.dynamicProxy(MediaPlayerProxy.class, future.get(1, TimeUnit.SECONDS));
- }
-}
diff --git a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java b/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java
deleted file mode 100644
index 62cabeb..0000000
--- a/variablespeed/tests/src/com/android/ex/variablespeed/VariableSpeedTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.ex.variablespeed;
-
-import android.util.Log;
-
-import java.util.concurrent.Executors;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.TimeUnit;
-
-/** Tests for the {@link VariableSpeed} class. */
-public class VariableSpeedTest extends MediaPlayerProxyTestCase {
- private static final String TAG = "VariableSpeedTest";
-
- private ScheduledExecutorService mExecutor = Executors.newScheduledThreadPool(2);
-
- @Override
- protected void tearDown() throws Exception {
- // I explicitly want to do super's tear-down first, because I need to get it to reset
- // the media player before I can be confident that I can shut down the executor service.
- super.tearDown();
- mExecutor.shutdown();
- if (!mExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
- Log.e(TAG, "Couldn't shut down Executor during test, check your cleanup code!");
- }
- mExecutor = null;
- }
-
- @Override
- public MediaPlayerProxy createTestMediaPlayer() throws Exception {
- return VariableSpeed.createVariableSpeed(mExecutor);
- }
-}