Browse Source

集成录音插件

hwh97 6 years ago
commit
79ecf689cf
100 changed files with 16094 additions and 0 deletions
  1. 7 0
      .gitignore
  2. 116 0
      .idea/codeStyles/Project.xml
  3. 19 0
      .idea/libraries/Dart_SDK.xml
  4. 9 0
      .idea/libraries/Flutter_Plugins.xml
  5. 9 0
      .idea/libraries/Flutter_for_Android.xml
  6. 7 0
      .idea/misc.xml
  7. 10 0
      .idea/modules.xml
  8. 6 0
      .idea/runConfigurations/example_lib_main_dart.xml
  9. 218 0
      .idea/workspace.xml
  10. 10 0
      .metadata
  11. 3 0
      CHANGELOG.md
  12. 1 0
      LICENSE
  13. 14 0
      README.md
  14. 8 0
      android/.gitignore
  15. 1 0
      android/.idea/.name
  16. BIN
      android/.idea/caches/build_file_checksums.ser
  17. 116 0
      android/.idea/codeStyles/Project.xml
  18. 3 0
      android/.idea/dictionaries/hwhong.xml
  19. 18 0
      android/.idea/gradle.xml
  20. 6 0
      android/.idea/kotlinc.xml
  21. 9 0
      android/.idea/misc.xml
  22. 8 0
      android/.idea/modules.xml
  23. 12 0
      android/.idea/runConfigurations.xml
  24. 54 0
      android/build.gradle
  25. 2 0
      android/gradle.properties
  26. 5 0
      android/gradle/wrapper/gradle-wrapper.properties
  27. BIN
      android/libs/Msc.jar
  28. BIN
      android/libs/arm64-v8a/libmsc.so
  29. BIN
      android/libs/armeabi-v7a/libmsc.so
  30. BIN
      android/libs/flutter.jar
  31. 1 0
      android/settings.gradle
  32. 8 0
      android/src/main/AndroidManifest.xml
  33. 624 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/DubbingLibPlugin.kt
  34. 41 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/api/DownloadHelper.kt
  35. 13 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/api/DownloadService.kt
  36. 181 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AACAudioEncoder.java
  37. 143 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AndroidAudioDecoder.java
  38. 39 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AudioDecoder.java
  39. 17 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AudioEncoder.java
  40. 30 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/MD5Util.java
  41. 222 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/VideoAudioMixer.java
  42. 19 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/FinalResult.java
  43. 36 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadSentenceResult.java
  44. 27 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadSyllableResult.java
  45. 45 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadWordResult.java
  46. 54 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/Result.java
  47. 272 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/XmlResultParser.java
  48. 101 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Phone.java
  49. 44 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Sentence.java
  50. 56 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Syll.java
  51. 57 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Word.java
  52. 118 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/util/ResultFormatUtil.java
  53. 38 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/util/ResultTranslateUtil.java
  54. 172 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/CommonFunction.java
  55. 48 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/Constant.java
  56. 531 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/DecodeEngine.java
  57. 12 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/DecodeOperateInterface.java
  58. 202 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/FileFunction.java
  59. 127 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/LogFunction.java
  60. 8 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/Variable.java
  61. 2562 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/sound/pcm/resampling/ssrc/SSRC.java
  62. 155 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/util/I0Bessel.java
  63. 2604 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/util/SplitRadixFft.java
  64. 215 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/AudioComposer.java
  65. 66 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/FileFunction.java
  66. 155 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/I0Bessel.java
  67. 2557 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/SSRC.java
  68. 2626 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/SplitRadixFft.java
  69. 3 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/bean/SimpleWord.kt
  70. 5 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/bean/VideoData.kt
  71. 7 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/callback/MixinHandlerCallback.kt
  72. 11 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/listener/DownloadListener.kt
  73. 111 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/util/AudioEvaluatorUtil.kt
  74. 141 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/util/DownloadUtil.kt
  75. 54 0
      android/src/main/kotlin/cn/i2edu/dubbing_lib/util/PausableThreadPool.java
  76. 19 0
      dubbing_lib.iml
  77. 73 0
      example/.gitignore
  78. 10 0
      example/.metadata
  79. 16 0
      example/README.md
  80. 67 0
      example/android/app/build.gradle
  81. 7 0
      example/android/app/src/debug/AndroidManifest.xml
  82. 33 0
      example/android/app/src/main/AndroidManifest.xml
  83. 13 0
      example/android/app/src/main/kotlin/cn/i2edu/dubbing_lib_example/MainActivity.kt
  84. 12 0
      example/android/app/src/main/res/drawable/launch_background.xml
  85. BIN
      example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  86. BIN
      example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  87. BIN
      example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  88. BIN
      example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  89. BIN
      example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  90. 8 0
      example/android/app/src/main/res/values/styles.xml
  91. 7 0
      example/android/app/src/profile/AndroidManifest.xml
  92. 31 0
      example/android/build.gradle
  93. 2 0
      example/android/gradle.properties
  94. 6 0
      example/android/gradle/wrapper/gradle-wrapper.properties
  95. 15 0
      example/android/settings.gradle
  96. 26 0
      example/ios/Flutter/AppFrameworkInfo.plist
  97. 1 0
      example/ios/Flutter/Debug.xcconfig
  98. 1 0
      example/ios/Flutter/Release.xcconfig
  99. 511 0
      example/ios/Runner.xcodeproj/project.pbxproj
  100. 7 0
      example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata

+ 7 - 0
.gitignore

@@ -0,0 +1,7 @@
+.DS_Store
+.dart_tool/
+
+.packages
+.pub/
+
+build/

+ 116 - 0
.idea/codeStyles/Project.xml

@@ -0,0 +1,116 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 19 - 0
.idea/libraries/Dart_SDK.xml

@@ -0,0 +1,19 @@
+<component name="libraryTable">
+  <library name="Dart SDK">
+    <CLASSES>
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/async" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/collection" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/convert" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/core" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/developer" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/html" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/io" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/isolate" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/math" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/mirrors" />
+      <root url="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/dart-sdk/lib/typed_data" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>

+ 9 - 0
.idea/libraries/Flutter_Plugins.xml

@@ -0,0 +1,9 @@
+<component name="libraryTable">
+  <library name="Flutter Plugins" type="FlutterPluginsLibraryType">
+    <CLASSES>
+      <root url="file://$PROJECT_DIR$" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>

+ 9 - 0
.idea/libraries/Flutter_for_Android.xml

@@ -0,0 +1,9 @@
+<component name="libraryTable">
+  <library name="Flutter for Android">
+    <CLASSES>
+      <root url="jar://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/artifacts/engine/android-arm/flutter.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>

+ 7 - 0
.idea/misc.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Android API 14 Platform" project-jdk-type="Android SDK" />
+  <component name="ProjectType">
+    <option name="id" value="io.flutter" />
+  </component>
+</project>

+ 10 - 0
.idea/modules.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/dubbing_lib.iml" filepath="$PROJECT_DIR$/dubbing_lib.iml" />
+      <module fileurl="file://$PROJECT_DIR$/android/dubbing_lib_android.iml" filepath="$PROJECT_DIR$/android/dubbing_lib_android.iml" />
+      <module fileurl="file://$PROJECT_DIR$/example/android/dubbing_lib_example_android.iml" filepath="$PROJECT_DIR$/example/android/dubbing_lib_example_android.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/runConfigurations/example_lib_main_dart.xml

@@ -0,0 +1,6 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="example/lib/main.dart" type="FlutterRunConfigurationType" factoryName="Flutter">
+    <option name="filePath" value="$PROJECT_DIR$/example/lib/main.dart" />
+    <method />
+  </configuration>
+</component>

+ 218 - 0
.idea/workspace.xml

@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ChangeListManager">
+    <list default="true" id="96a4f947-f66a-4efc-b495-ae979b3315bb" name="Default Changelist" comment="" />
+    <ignored path="$PROJECT_DIR$/.dart_tool/" />
+    <ignored path="$PROJECT_DIR$/.idea/" />
+    <ignored path="$PROJECT_DIR$/.pub/" />
+    <ignored path="$PROJECT_DIR$/build/" />
+    <ignored path="$PROJECT_DIR$/example/.pub/" />
+    <ignored path="$PROJECT_DIR$/example/build/" />
+    <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="FileEditorManager">
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://$PROJECT_DIR$/lib/dubbing_lib.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="195">
+              <caret line="15" column="41" lean-forward="true" selection-start-line="15" selection-start-column="41" selection-end-line="15" selection-end-column="41" />
+              <folding>
+                <element signature="e#1407#1412#0" />
+                <element signature="e#1482#1941#0" />
+                <element signature="e#1403#1413#0" />
+              </folding>
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="true">
+        <entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="60">
+              <caret line="4" column="69" lean-forward="true" selection-start-line="4" selection-start-column="69" selection-end-line="4" selection-end-column="69" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/pkg/sky_engine/lib/async/stream_controller.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="193">
+              <caret line="142" column="31" lean-forward="true" selection-start-line="142" selection-start-column="31" selection-end-line="142" selection-end-column="31" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+      <file pinned="false" current-in-tab="false">
+        <entry file="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/pkg/sky_engine/lib/async/broadcast_stream_controller.dart">
+          <provider selected="true" editor-type-id="text-editor">
+            <state relative-caret-position="148">
+              <caret line="354" column="2" selection-start-line="354" selection-start-column="2" selection-end-line="354" selection-end-column="2" />
+            </state>
+          </provider>
+        </entry>
+      </file>
+    </leaf>
+  </component>
+  <component name="IdeDocumentHistory">
+    <option name="CHANGED_PATHS">
+      <list>
+        <option value="$PROJECT_DIR$/lib/dubbing_lib.dart" />
+      </list>
+    </option>
+  </component>
+  <component name="ProjectFrameBounds" extendedState="6">
+    <option name="width" value="1440" />
+    <option name="height" value="900" />
+  </component>
+  <component name="ProjectView">
+    <navigator currentView="ProjectPane" proportions="" version="1">
+      <foldersAlwaysOnTop value="true" />
+    </navigator>
+    <panes>
+      <pane id="AndroidView" />
+      <pane id="Scope" />
+      <pane id="ProjectPane">
+        <subPane>
+          <expand>
+            <path>
+              <item name="dubbing_lib" type="b2602c69:ProjectViewProjectNode" />
+              <item name="dubbing_lib" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="dubbing_lib" type="b2602c69:ProjectViewProjectNode" />
+              <item name="dubbing_lib" type="462c0819:PsiDirectoryNode" />
+              <item name="android" type="462c0819:PsiDirectoryNode" />
+            </path>
+            <path>
+              <item name="dubbing_lib" type="b2602c69:ProjectViewProjectNode" />
+              <item name="dubbing_lib" type="462c0819:PsiDirectoryNode" />
+              <item name="android" type="462c0819:PsiDirectoryNode" />
+              <item name="main" type="462c0819:PsiDirectoryNode" />
+            </path>
+          </expand>
+          <select />
+        </subPane>
+      </pane>
+      <pane id="PackagesPane" />
+    </panes>
+  </component>
+  <component name="PropertiesComponent">
+    <property name="dart.analysis.tool.window.force.activate" value="false" />
+    <property name="last_opened_file_path" value="$PROJECT_DIR$/android" />
+    <property name="show.migrate.to.gradle.popup" value="false" />
+  </component>
+  <component name="RunDashboard">
+    <option name="ruleStates">
+      <list>
+        <RuleState>
+          <option name="name" value="ConfigurationTypeDashboardGroupingRule" />
+        </RuleState>
+        <RuleState>
+          <option name="name" value="StatusDashboardGroupingRule" />
+        </RuleState>
+      </list>
+    </option>
+  </component>
+  <component name="SvnConfiguration">
+    <configuration />
+  </component>
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="96a4f947-f66a-4efc-b495-ae979b3315bb" name="Default Changelist" comment="" />
+      <created>1571458484872</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1571458484872</updated>
+    </task>
+    <servers />
+  </component>
+  <component name="ToolWindowManager">
+    <frame x="-7" y="-7" width="1550" height="838" extended-state="6" />
+    <editor active="true" />
+    <layout>
+      <window_info id="Capture Tool" />
+      <window_info id="Structure" side_tool="true" />
+      <window_info id="Favorites" side_tool="true" />
+      <window_info id="Build Variants" side_tool="true" />
+      <window_info id="Image Layers" />
+      <window_info id="Designer" />
+      <window_info id="Captures" side_tool="true" />
+      <window_info id="Resources Explorer" />
+      <window_info active="true" content_ui="combo" id="Project" order="0" visible="true" weight="0.25" />
+      <window_info anchor="bottom" id="Run" />
+      <window_info anchor="bottom" id="Dart Analysis" weight="0.32956153" />
+      <window_info anchor="bottom" id="Logcat" />
+      <window_info anchor="bottom" id="TODO" />
+      <window_info anchor="bottom" id="Debug" />
+      <window_info anchor="bottom" id="Terminal" visible="true" weight="0.32956153" />
+      <window_info anchor="bottom" id="Event Log" side_tool="true" />
+      <window_info anchor="bottom" id="Flutter Performance" side_tool="true" />
+      <window_info anchor="bottom" id="Version Control" />
+      <window_info anchor="bottom" id="Android Profiler" show_stripe_button="false" />
+      <window_info anchor="right" id="Device File Explorer" side_tool="true" />
+      <window_info anchor="right" id="Capture Analysis" />
+      <window_info anchor="right" id="Theme Preview" />
+      <window_info anchor="right" id="Flutter Inspector" />
+      <window_info anchor="right" id="Flutter Outline" />
+      <window_info anchor="right" id="Palette&#9;" />
+    </layout>
+  </component>
+  <component name="editorHistoryManager">
+    <entry file="file://$PROJECT_DIR$/example/lib/main.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="-20">
+          <caret line="10" column="43" selection-start-line="10" selection-start-column="43" selection-end-line="10" selection-end-column="44" />
+          <folding>
+            <element signature="e#0#39#0" expanded="true" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/pkg/sky_engine/lib/async/stream_controller.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="193">
+          <caret line="142" column="31" lean-forward="true" selection-start-line="142" selection-start-column="31" selection-end-line="142" selection-end-column="31" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/bin/cache/pkg/sky_engine/lib/async/broadcast_stream_controller.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="148">
+          <caret line="354" column="2" selection-start-line="354" selection-start-column="2" selection-end-line="354" selection-end-column="2" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://C:/flutter/flutter_windows_v1.9.1+hotfix.4-stable/flutter/packages/flutter/lib/src/services/platform_channel.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="148">
+          <caret line="361" column="7" selection-start-line="361" selection-start-column="7" selection-end-line="361" selection-end-column="7" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lib/dubbing_lib.dart">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="195">
+          <caret line="15" column="41" lean-forward="true" selection-start-line="15" selection-start-column="41" selection-end-line="15" selection-end-column="41" />
+          <folding>
+            <element signature="e#1407#1412#0" />
+            <element signature="e#1482#1941#0" />
+            <element signature="e#1403#1413#0" />
+          </folding>
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/android/src/main/AndroidManifest.xml">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="60">
+          <caret line="4" column="69" lean-forward="true" selection-start-line="4" selection-start-column="69" selection-end-line="4" selection-end-column="69" />
+        </state>
+      </provider>
+    </entry>
+  </component>
+</project>

+ 10 - 0
.metadata

@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
+  channel: stable
+
+project_type: plugin

+ 3 - 0
CHANGELOG.md

@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.

+ 1 - 0
LICENSE

@@ -0,0 +1 @@
+TODO: Add your license here.

+ 14 - 0
README.md

@@ -0,0 +1,14 @@
+# dubbing_lib
+
+A new Flutter plugin for dubbing
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/developing-packages/),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter, view our 
+[online documentation](https://flutter.dev/docs), which offers tutorials, 
+samples, guidance on mobile development, and a full API reference.

+ 8 - 0
android/.gitignore

@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures

+ 1 - 0
android/.idea/.name

@@ -0,0 +1 @@
+dubbing_lib

BIN
android/.idea/caches/build_file_checksums.ser


+ 116 - 0
android/.idea/codeStyles/Project.xml

@@ -0,0 +1,116 @@
+<component name="ProjectCodeStyleConfiguration">
+  <code_scheme name="Project" version="173">
+    <codeStyleSettings language="XML">
+      <indentOptions>
+        <option name="CONTINUATION_INDENT_SIZE" value="4" />
+      </indentOptions>
+      <arrangement>
+        <rules>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:android</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>xmlns:.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:id</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*:name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>name</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>style</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>^$</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>ANDROID_ATTRIBUTE_ORDER</order>
+            </rule>
+          </section>
+          <section>
+            <rule>
+              <match>
+                <AND>
+                  <NAME>.*</NAME>
+                  <XML_ATTRIBUTE />
+                  <XML_NAMESPACE>.*</XML_NAMESPACE>
+                </AND>
+              </match>
+              <order>BY_NAME</order>
+            </rule>
+          </section>
+        </rules>
+      </arrangement>
+    </codeStyleSettings>
+  </code_scheme>
+</component>

+ 3 - 0
android/.idea/dictionaries/hwhong.xml

@@ -0,0 +1,3 @@
+<component name="ProjectDictionaryState">
+  <dictionary name="hwhong" />
+</component>

+ 18 - 0
android/.idea/gradle.xml

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="distributionType" value="DEFAULT_WRAPPED" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+          </set>
+        </option>
+        <option name="resolveModulePerSourceSet" value="false" />
+        <option name="testRunner" value="PLATFORM" />
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 6 - 0
android/.idea/kotlinc.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Kotlin2JvmCompilerArguments">
+    <option name="jvmTarget" value="1.8" />
+  </component>
+</project>

+ 9 - 0
android/.idea/misc.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 8 - 0
android/.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/dubbing_lib.iml" filepath="$PROJECT_DIR$/dubbing_lib.iml" />
+    </modules>
+  </component>
+</project>

+ 12 - 0
android/.idea/runConfigurations.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
+        <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 54 - 0
android/build.gradle

@@ -0,0 +1,54 @@
+group 'cn.i2edu.dubbing_lib'
+version '1.0-SNAPSHOT'
+
+buildscript {
+    ext.kotlin_version = '1.2.71'
+    repositories {
+        google()
+        jcenter()
+        maven { url 'https://jitpack.io' }
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.2.1'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+rootProject.allprojects {
+    repositories {
+        google()
+        jcenter()
+        maven { url 'https://jitpack.io' }
+    }
+}
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+    compileSdkVersion 28
+
+    sourceSets {
+        main {
+            java.srcDirs += 'src/main/kotlin'
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+    defaultConfig {
+        minSdkVersion 16
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+    lintOptions {
+        disable 'InvalidPackage'
+    }
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    compileOnly  files('libs/flutter.jar')
+    implementation 'com.squareup.retrofit2:retrofit:2.6.1'
+    implementation 'com.googlecode.mp4parser:isoparser:1.0-RC-37'
+    implementation 'com.alibaba:fastjson:1.1.71.android'
+    implementation files('libs/Msc.jar')
+}

+ 2 - 0
android/gradle.properties

@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx1536M
+

+ 5 - 0
android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

BIN
android/libs/Msc.jar


BIN
android/libs/arm64-v8a/libmsc.so


BIN
android/libs/armeabi-v7a/libmsc.so


BIN
android/libs/flutter.jar


+ 1 - 0
android/settings.gradle

@@ -0,0 +1 @@
+rootProject.name = 'dubbing_lib'

+ 8 - 0
android/src/main/AndroidManifest.xml

@@ -0,0 +1,8 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+  package="cn.i2edu.dubbing_lib">
+
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO"/>
+
+</manifest>

+ 624 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/DubbingLibPlugin.kt

@@ -0,0 +1,624 @@
+package cn.i2edu.dubbing_lib
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.media.MediaPlayer
+import android.media.MediaRecorder
+import android.os.Build
+import android.os.Environment
+import android.os.Handler
+import android.os.Message
+import android.text.TextUtils
+import android.view.View
+import android.widget.Toast
+import cn.i2edu.dubbing_lib.audioUtils.AudioDecoder
+import cn.i2edu.dubbing_lib.audioUtils.AudioEncoder
+import cn.i2edu.dubbing_lib.audioUtils.VideoAudioMixer
+import cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.ReadSentenceResult
+import cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.XmlResultParser
+import cn.i2edu.dubbing_lib.audioUtils.compose.AudioComposer
+import cn.i2edu.dubbing_lib.bean.SimpleWord
+import cn.i2edu.dubbing_lib.bean.VideoData
+import cn.i2edu.dubbing_lib.callback.MixinHandlerCallback
+import cn.i2edu.dubbing_lib.util.PausableThreadPool
+import com.alibaba.fastjson.JSON
+import com.i2edu.dubbing_lib.listener.DownloadListener
+import com.i2edu.dubbing_lib.util.AudioEvaluatorUtil
+import com.i2edu.dubbing_lib.util.DownloadUtil
+import com.i2edu.dubbing_lib.util.EvaluatorCallBack
+import com.iflytek.cloud.EvaluatorResult
+import com.iflytek.cloud.SpeechConstant
+import com.iflytek.cloud.SpeechError
+import com.iflytek.cloud.SpeechUtility
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import io.flutter.plugin.common.MethodChannel.Result
+import io.flutter.plugin.common.PluginRegistry.Registrar
+import java.io.File
+import java.io.IOException
+import java.lang.ref.WeakReference
+import java.util.*
+
+class DubbingLibPlugin: MethodCallHandler, MixinHandlerCallback {
+
+  private val activity: Activity
+  private var mediaRecorder: MediaRecorder? = null
+  private var mediaPlayer: MediaPlayer? = null
+  private var isRecording: Boolean = false
+  private var timer: Timer? = null
+  private val pausableThreadPool: PausableThreadPool = PausableThreadPool(1)
+  private val mixinHandler: MixinHandler = MixinHandler(this)
+  private var mixinResult: MethodChannel.Result? = null
+  private var videoData: VideoData? = null
+  private var videoAudioMixer: VideoAudioMixer? = null
+  private val Tag: String = "DubbingPlugin"
+
+  companion object {
+    
+    private lateinit var channel: MethodChannel
+    val CHANNEL_NAME = "cn.hwwwwh.flutter.plugins/DubbingPlugin"
+    val PATH_AUDIO = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_record/"
+    val PATH_AUDIO_DECODE = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_record_decode/"
+    val PATH_AUDIO_EVALUATOR_DECODE = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_record_evaluator_decode/"
+    val PATH_BGM_DECODE = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_bgm_decode/"
+    val PATH_BGM_RECORD_SYN = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_bgm_record_syn/"
+    val PATH_BGM_RECORD_SYN_DECODE = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_bgm_record_syn_decode/"
+    val PATH_VIDEO_MIXIN = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_video_mixin/"
+    private val BGM_DOWNLOAD_FINISHED = 0X3999
+    private val AUDIO_SYN_FINISHED = 0X4001
+    private val AUDIO_MIX_VIDIO_FINISHED = 0X4002
+    private val AUDIO_DECODE_FINISHED = 0x4003
+    private val AUDIO_ENCODE_FINISHED = 0x4004
+    
+    @JvmStatic
+    fun registerWith(registrar: Registrar) {
+      val channel = MethodChannel(registrar.messenger(), "dubbing_lib")
+      channel.setMethodCallHandler(DubbingLibPlugin(registrar.activity()))
+    }
+
+    private class MixinHandler(plugin: DubbingLibPlugin): Handler() {
+      private val plugin: WeakReference<DubbingLibPlugin> = WeakReference(plugin)
+
+      override fun handleMessage(msg: Message) {
+        if (plugin.get() != null) {
+          plugin.get()?.onHandleMessage(msg)
+        }
+      }
+    }
+  }
+
+  @SuppressLint("CheckResult")
+  constructor(activity: Activity) {
+    this.activity = activity
+  }
+
+  override fun onMethodCall(call: MethodCall, result: Result) {
+    when (call.method) {
+      "initSpeechSdk" -> {
+        SpeechUtility.createUtility(activity, SpeechConstant.APPID +"=589c15f3")
+        result.success(true)
+      }
+      "getPlatformVersion" -> result.success("Android ${android.os.Build.VERSION.RELEASE}")
+      "setExtraFullScreen" -> {
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+          activity.window.decorView.systemUiVisibility = activity.window.decorView.systemUiVisibility.or(View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
+        }
+      }
+      "downLoadVideo" -> {
+        downloadVideo(call.argument<String>("url")!!, result)
+      }
+      "startRecord" -> {
+        val duration = call.argument<Int>("duration")!!
+        val fileName = call.argument<String>("fileName")!!
+        val index = call.argument<Int>("index")!!
+        val en = call.argument<String>("content")!!
+        // 开启录音
+        initMediaRecorder(index, duration, fileName, en, result)
+      }
+      "playRecordAudio" -> {
+        val fileName = call.argument<String>("fileName")!!
+        playRecord(fileName, result)
+      }
+      "pauseRecordAudio" -> {
+        mediaPlayer?.pause()
+        result.success(true)
+      }
+      "getIsMediaPlayPause" -> {
+        result.success(mediaPlayer != null && !mediaPlayer!!.isPlaying)
+      }
+      "cleanAudioData" -> {
+        try {
+          // 取得 video id, 根据前缀进行删除操作
+          val videoId = call.argument<String>("videoId")!!
+          deleteFileWithPrefix(videoId, PATH_AUDIO)
+          deleteFileWithPrefix(videoId, PATH_AUDIO_DECODE)
+          result.success(true)
+        } catch (e: Exception){
+          e.printStackTrace()
+          result.error("1003", "clean cache file failed", null)
+        }
+      }
+      "findIsExistCacheVideo" -> {
+        try {
+          // 取得 video id, 根据前缀进行查找操作
+          val videoId = call.argument<String>("videoId")!!
+          val map = getExistCache(videoId)
+          result.success(map)
+        } catch (e: Exception){
+          e.printStackTrace()
+          result.error("1004", "find cache file failed", null)
+        }
+      }
+      "startMixinAudio" -> {
+        try {
+          videoData = VideoData(
+                  videoId = call.argument<String>("videoId")!!,
+                  bgmUrl = call.argument<String>("bgmUrl")!!,
+                  durationList = call.argument<List<Long>>("durationList")!!,
+                  endTimeList = call.argument<List<Long>>("endTimeList")!!,
+                  audioDecodePaths =  call.argument<List<String>>("audioDecodePaths")!!,
+                  videoPath = call.argument<String>("videoPath")!!
+          )
+          mixinResult = result
+          startMixinAudio()
+        } catch (e: Exception){
+          e.printStackTrace()
+          result.error("1005", "mixin audio failed", null)
+        }
+      }
+      else -> result.notImplemented()
+    }
+  }
+
+  private fun downloadVideo(url: String, result: MethodChannel.Result) {
+    val downloadUtil = DownloadUtil()
+    downloadUtil.downloadFile(url, object : DownloadListener {
+      override fun onStart() {
+
+      }
+
+      override fun onProgress(progress: Int) {
+        activity.runOnUiThread {
+          channel.invokeMethod("downloadUpdate", mapOf("progress" to progress))
+        }
+      }
+
+      override fun onFinish(localPath: String) {
+        activity.runOnUiThread {
+          result.success(localPath)
+        }
+      }
+
+      override fun onFailure() {
+        activity.runOnUiThread {
+          result.error("10002", "下载存储错误", null)
+        }
+      }
+
+    })
+  }
+
+  private fun initMediaRecorder(index: Int, duration: Int, fileName: String, en: String, result: MethodChannel.Result) {
+    // create file
+    val file = File(PATH_AUDIO)
+    if (!file.exists()) {
+      file.mkdirs()
+    }
+    stopRecord()
+    // config media recorder
+    if (mediaRecorder == null) {
+      mediaRecorder = MediaRecorder()
+      mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
+      mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)// 设置所录制的音视频文件的格式
+      mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)// 设置所录制的声音的编码格式
+      mediaRecorder?.setAudioEncodingBitRate(96000)// 比特率
+      mediaRecorder?.setAudioChannels(2)// 通道
+      mediaRecorder?.setAudioSamplingRate(44100)// 采样率
+    }
+    // start record
+    startRecord(index, duration, fileName, en, result)
+  }
+
+  private fun startRecord(index: Int, duration: Int, fileName: String, en: String, result: MethodChannel.Result) {
+    if (mediaRecorder == null) return
+    val filePath = PATH_AUDIO + fileName
+    try {
+      mediaRecorder?.setOutputFile(filePath)
+      mediaRecorder?.prepare()
+      isRecording = true
+      mediaRecorder?.start()
+    } catch (e: IOException) {
+      e.printStackTrace()
+      isRecording = false
+    }
+    if (isRecording) {
+      var piecePosition = 0
+      if (timer != null) {
+        timer?.cancel()
+      }
+      timer = Timer()
+      // 这里参照少儿英语秀之前的逻辑
+      timer?.schedule(object : TimerTask() {
+        override fun run() {
+          activity.runOnUiThread {
+            isRecording = true
+            piecePosition++
+            // 传递进度
+            channel.invokeMethod("recordProgress", mapOf("progress" to piecePosition * 5))
+            while (piecePosition * 5 >= duration) {
+              // 录制完毕
+              try {
+                result.success(filePath)
+              } catch (e: Exception) {
+                e.printStackTrace()
+              } finally {
+                timer?.cancel()
+                stopRecord()
+                // 解码音频
+                Thread {
+                  decodeRecord(index, fileName, filePath)
+                }.start()
+                // 评测音频
+                Thread {
+                  evaluatorRecord(index, filePath, en)
+                }.start()
+              }
+              break
+            }
+          }
+        }
+      }, 0, 5)
+    } else {
+      result.error("1002", "start record failed", null)
+    }
+  }
+
+  private fun evaluatorRecord(index: Int, recordPath: String, en: String) {
+    val dir = File(PATH_AUDIO_EVALUATOR_DECODE)
+    if (!dir.exists()) dir.mkdirs()
+    val decodePath = PATH_AUDIO_EVALUATOR_DECODE + "/" + UUID.randomUUID().toString() + ".pcm"
+    AudioEvaluatorUtil.getInstance(context = activity.applicationContext)
+            .setEvaluatorCallBack(object : EvaluatorCallBack {
+              override fun onResult(result: EvaluatorResult) {
+                activity.runOnUiThread {
+                  val builder = StringBuilder()
+                  builder.append(result.resultString)
+                  if (!TextUtils.isEmpty(builder)) {
+                    val resultParser = XmlResultParser()
+                    val parseResult = resultParser.parse(builder.toString()) as ReadSentenceResult
+                    Toast.makeText(activity.applicationContext,
+                            "识别成功,结果:${if (parseResult.is_rejected) "您在乱读哦" else "您没乱读结果为:${parseResult.total_score}"}", Toast.LENGTH_SHORT).show()
+                    val formatWords = arrayListOf<SimpleWord>()
+                    for (sentence in parseResult.sentences) {
+                      val words = sentence.words
+                      for (word in words) {
+                        // 过滤掉sil、 silv 表示静音, fil 表示噪音
+                        if (word.content == "sil" || word.content == "silv" || word.content == "fil")
+                          continue
+                        formatWords.add(SimpleWord(word.content, word.total_score))
+                      }
+                    }
+
+                    channel.invokeMethod("evaluatorResult",
+                            mapOf("index" to index,"score" to (if(parseResult.is_rejected) null else parseResult.total_score), "words" to JSON.toJSONString(formatWords)))
+                  } else {
+                    Toast.makeText(activity.applicationContext, "识别失败,结果为空", Toast.LENGTH_SHORT).show()
+                    channel.invokeMethod("evaluatorResult", mapOf("index" to index,"score" to null))
+                  }
+                }
+              }
+
+              override fun onError(error: SpeechError) {
+                activity.runOnUiThread {
+                  Toast.makeText(activity.applicationContext, "识别失败,错误描述:${error.errorDescription},错误码:${error.errorCode}",
+                          Toast.LENGTH_SHORT).show()
+                  channel.invokeMethod("evaluatorResult", mapOf("index" to index,"score" to null))
+                }
+              }
+            })
+            ?.startEvaluator(en, recordPath, decodePath)
+  }
+
+  private fun decodeRecord(index: Int, fileName: String, recordPath: String) {
+    val decodePath = doDecode(fileName, recordPath, PATH_AUDIO_DECODE)
+    if (decodePath != null && File(decodePath).exists()) {
+      // 解码成功
+      activity.runOnUiThread {
+        channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to decodePath))
+      }
+    } else {
+      // 解码失败
+      activity.runOnUiThread {
+        channel.invokeMethod("decodeResult", mapOf("index" to index, "path" to ""))
+      }
+    }
+  }
+
+  private fun stopRecord() {
+    try {
+      mediaRecorder?.stop()
+    } catch (e: Exception) {
+      // TODO from i2 dubit
+      // 这里有可能会抛出两种异常,一种是IllegalStateException,抛出这种异常是由于在没有start的情况下使用了stop
+      // 另外一种是RuntimeException, 这种异常的产生原因是在执行了start()过后马上执行stop(),具体可看mRecorder.stop()的源码注释
+      e.printStackTrace()
+    } finally {
+      isRecording = false
+      try {
+        mediaRecorder?.reset()
+        mediaRecorder?.release()
+        mediaRecorder = null
+      } catch (e: Exception) {
+        e.printStackTrace()
+      }
+    }
+  }
+
+  private fun playRecord(fileName: String, result: MethodChannel.Result) {
+    resetMediaPlayer()
+    mediaPlayer = MediaPlayer()
+    mediaPlayer?.setDataSource(fileName)
+    mediaPlayer?.prepare()
+    mediaPlayer?.start()
+
+    mediaPlayer?.setOnCompletionListener {
+      resetMediaPlayer()
+      // 播放完成
+      result.success(true)
+    }
+  }
+
+  private fun resetMediaPlayer() {
+    mediaPlayer?.stop()
+    mediaPlayer?.release()
+    mediaPlayer = null
+  }
+
+  @Throws(Exception::class)
+  private fun deleteFileWithPrefix(video: String, path: String): Boolean {
+    val file = File(path)
+    if (!file.exists()) return false
+    // 取得所有文件
+    val files = file.listFiles()
+    if (files == null || files.isEmpty()) return false
+    for (fileExist in files) {
+      if (fileExist.isFile) {
+        // 验证文件名
+        if (fileExist.name.startsWith("${video}_")) {
+          fileExist.delete()
+        }
+      }
+    }
+    return true
+  }
+
+  @Throws(Exception::class)
+  private fun getExistCache(video: String): Map<Int, String> {
+    val map = hashMapOf<Int, String>()
+
+    // 取得所有文件
+    val file = File(PATH_AUDIO)
+    if (!file.exists()) return map
+    val files = file.listFiles()
+    if (files == null || files.isEmpty()) return map
+
+    // 取得所有文件(这里取decode的文件)
+    val decodeFile = File(PATH_AUDIO_DECODE)
+    if (!decodeFile.exists()) return map
+    val decodeFiles = decodeFile.listFiles()
+    if (decodeFiles == null || decodeFiles.isEmpty()) return map
+
+    for (i in 0 until files.size) {
+      val fileExist = files[i]
+      if (fileExist.isFile) {
+        // 验证文件名
+        if (fileExist.name.startsWith("${video}_")) {
+          // 填充map
+          try {
+            val videoIndex = fileExist.name.substring(fileExist.name.indexOf("_") + 1, fileExist.name.length).toInt()
+            map[videoIndex]= fileExist.absolutePath.toString()
+            // 取decode的文件
+            var decodePath = ""
+            for (decodeFile in decodeFiles) {
+              val decodeVideoIndex = decodeFile.name.substring(decodeFile.name.indexOf("_") + 1, decodeFile.name.length).toInt()
+              if (decodeVideoIndex == videoIndex) {
+                decodePath = decodeFile.absolutePath
+              }
+            }
+            if (decodePath.isEmpty()) {
+              map.remove(videoIndex)
+            } else {
+              map[videoIndex] = "${map[videoIndex]}|$decodePath"
+            }
+          } catch (e: Exception) {
+            e.printStackTrace()
+          }
+        }
+      }
+    }
+    return map
+  }
+
+  // -------------- 合成录音相关
+
+  override fun onHandleMessage(msg: Message) {
+    when (msg.what) {
+      BGM_DOWNLOAD_FINISHED -> {
+        // step2 解码背景音乐
+        Toast.makeText(activity.applicationContext, "BGM_DOWNLOAD_FINISHED", Toast.LENGTH_SHORT).show()
+        val i = videoData?.bgmUrl?.lastIndexOf('/')
+        val name = videoData?.bgmUrl?.substring(i!!)
+        decodeBgmAudio(name!!, videoData?.bgmPath!!)
+      }
+      AUDIO_DECODE_FINISHED -> {
+        // step3  背景音乐与录音合成
+        Toast.makeText(activity.applicationContext, "AUDIO_DECODE_FINISHED", Toast.LENGTH_SHORT).show()
+        syncAudios()
+      }
+      AUDIO_SYN_FINISHED -> {
+        // step4 编码音频
+        Toast.makeText(activity.applicationContext, "AUDIO_SYN_FINISHED", Toast.LENGTH_SHORT).show()
+        encodeAsynAudio()
+      }
+      AUDIO_ENCODE_FINISHED -> {
+        Toast.makeText(activity.applicationContext, "AUDIO_ENCODE_FINISHED", Toast.LENGTH_SHORT).show()
+        mixinAudioAndVideo()
+      }
+      AUDIO_MIX_VIDIO_FINISHED -> {
+        Toast.makeText(activity.applicationContext, "AUDIO_MIX_VIDIO_FINISHED", Toast.LENGTH_SHORT).show()
+        audioMixVideoFinish()
+      }
+    }
+  }
+
+  private fun startMixinAudio() {
+    pausableThreadPool.execute {
+      // 下载背景音乐
+      val downloadUtil = DownloadUtil()
+      downloadUtil.downloadBGMFile(videoData!!.bgmUrl, object: DownloadListener {
+        override fun onStart() {
+        }
+
+        override fun onProgress(progress: Int) {
+        }
+
+        override fun onFinish(localPath: String) {
+          // 下载成功传回bgm地址
+          activity.runOnUiThread {
+            channel.invokeMethod("bgmAudioPath", mapOf("bgmPath" to localPath))
+            videoData!!.bgmPath = localPath
+            // step2 解码背景音乐
+            val message = Message.obtain()
+            message.what = BGM_DOWNLOAD_FINISHED
+            mixinHandler.sendMessage(message)
+          }
+        }
+
+        override fun onFailure() {
+          activity.runOnUiThread {
+            mixinResult?.error("1005", "down load bgm failed", "")
+          }
+        }
+      })
+    }
+  }
+
+  private fun decodeBgmAudio(fileName:String, localPath: String) {
+    if (TextUtils.isEmpty(localPath)) return
+    pausableThreadPool.execute {
+      val decodedPath = doDecode(fileName, localPath, PATH_BGM_DECODE)
+      videoData?.decodeBgmPath = decodedPath
+      // step3  背景音乐与录音合成
+      val message = Message.obtain()
+      message.what = AUDIO_DECODE_FINISHED
+      mixinHandler.sendMessage(message)
+    }
+  }
+
+  private fun syncAudios() {
+    // 合成背景音乐和录音 (从尾部开始)
+    val fileDIR = File(PATH_BGM_RECORD_SYN)
+    if (!fileDIR.exists()) {
+      fileDIR.mkdirs()
+    }
+    val mixFilePath = File(fileDIR.absolutePath + "/" + "mixin.mp3")
+//        mixFilePath.createNewFile()
+    // 合成操作
+    val tempPath = arrayOf<String>(videoData?.decodeBgmPath!!)
+    pausableThreadPool.execute {
+      AudioComposer.composeAudio(tempPath[0],
+              videoData?.audioDecodePaths,
+              mixFilePath.absolutePath,
+              false,
+              videoData?.endTimeList,
+              videoData?.durationList,
+              object : AudioComposer.ComposeAudioInterface {
+                override fun composeSuccess(result: String?) {
+                  videoData?.decodeAsyncBgmPath = result!!
+                  val message = Message.obtain()
+                  message.what = AUDIO_SYN_FINISHED
+                  mixinHandler.sendMessage(message)
+                }
+
+                override fun composeFail() {
+                  activity.runOnUiThread {
+                    mixinResult?.error("1005", "async bgm and record failed", "")
+                  }
+                }
+              })
+    }
+  }
+
+  private fun encodeAsynAudio() {
+    pausableThreadPool.execute {
+      val accEncoder = AudioEncoder
+              .createAccEncoder(videoData?.decodeAsyncBgmPath)
+//                String finalMixPath = FileBiz.get_temp_recorders_dir() + "/" + UUID.randomUUID() + ".mp4";
+      val file = File(PATH_BGM_RECORD_SYN_DECODE)
+      if (!file.exists()) file.mkdirs()
+      val finalMixPath = PATH_BGM_RECORD_SYN_DECODE + "mixinDecode.aac"
+      val isEncodeFinished = !TextUtils.isEmpty(accEncoder.encodeToFile(finalMixPath))
+      if (!isEncodeFinished) {
+        activity.runOnUiThread{
+          mixinResult?.error("1005", "encode async audio failed", "")
+        }
+        return@execute
+      }
+      videoData?.encodeAudioWithBgmPath = finalMixPath
+      val message = Message.obtain()
+      message.what = AUDIO_ENCODE_FINISHED
+      mixinHandler.sendMessage(message)
+    }
+  }
+
+  private fun mixinAudioAndVideo() {
+    pausableThreadPool.execute {
+      if (videoAudioMixer == null) {
+        videoAudioMixer = VideoAudioMixer(activity.applicationContext)
+      }
+      videoAudioMixer?.setListener(object: VideoAudioMixer.VideoAudioMixListener {
+        override fun mixSuccess() {
+          videoData?.mixVideoPath = PATH_VIDEO_MIXIN + "${videoData?.videoId}_mix.mp4"
+          val msg = Message()
+          msg.what = AUDIO_MIX_VIDIO_FINISHED
+          mixinHandler.sendMessage(msg)
+        }
+
+        override fun mixFail(reason: String?) {
+          activity.runOnUiThread{
+            mixinResult?.error("1005", "mix video and audio failed", "")
+          }
+        }
+      })
+      videoAudioMixer?.mux(videoData?.encodeAudioWithBgmPath, videoData?.videoPath,
+              "${videoData?.videoId}_mix.mp4", PATH_VIDEO_MIXIN)
+    }
+  }
+
+  private fun audioMixVideoFinish() {
+    mixinResult?.success(videoData?.mixVideoPath)
+  }
+
+  private fun doDecode(fileName: String, path: String, saveDirPath: String): String? {
+    try {
+      // 解码后的路径
+      val decodeFile = File(saveDirPath)
+      if (!decodeFile.exists()) {
+        decodeFile.mkdirs()
+      }
+      val finalFile = File(decodeFile.absolutePath + "/" + fileName)
+      if (!finalFile.exists()) {
+        finalFile.createNewFile()
+      }
+      val audioDec = AudioDecoder
+              .createDefualtDecoder(path)
+      audioDec.decodeToFile(finalFile.absolutePath)
+      return finalFile.absolutePath
+    } catch (e: Exception) {
+      e.printStackTrace()
+    }
+    return null
+  }
+
+}

+ 41 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/api/DownloadHelper.kt

@@ -0,0 +1,41 @@
+package cn.i2edu.dubbing_lib.api
+
+import okhttp3.OkHttpClient
+import retrofit2.Retrofit
+import java.util.concurrent.TimeUnit
+
+class DownloadHelper {
+    companion object {
+        val Tag: String = "DownloadHelper"
+        // kotlin DLC mode
+        val instance: DownloadHelper by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
+            DownloadHelper()
+        }
+    }
+
+    private lateinit var mRetrofit: Retrofit
+    private lateinit var mHttpClient: OkHttpClient
+
+    constructor(): this(30, 30, 30)
+
+    constructor(connectTimeOut: Long, readTimeOut: Long, writeTimeOut: Long) {
+        val builder = OkHttpClient.Builder()
+                .connectTimeout(connectTimeOut, TimeUnit.SECONDS)
+                .readTimeout(readTimeOut, TimeUnit.SECONDS)
+                .writeTimeout(writeTimeOut, TimeUnit.SECONDS);
+        mHttpClient = builder.build()
+    }
+
+    fun buildRetrofit(baseUrl: String): DownloadHelper {
+        mRetrofit = Retrofit.Builder()
+                .baseUrl(baseUrl)
+                .client(mHttpClient)
+                .build()
+        return this
+    }
+
+    public fun <T> createRetrofit(serviceClass: Class<T>): T {
+        return mRetrofit.create(serviceClass)
+    }
+
+}

+ 13 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/api/DownloadService.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.dubbing_lib.api
+
+import okhttp3.ResponseBody
+import retrofit2.Call
+import retrofit2.http.GET
+import retrofit2.http.Streaming
+import retrofit2.http.Url
+
+interface DownloadService {
+    @Streaming
+    @GET
+    fun downFile(@Url fileUrl:String): Call<ResponseBody>
+}

+ 181 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AACAudioEncoder.java

@@ -0,0 +1,181 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+class AACAudioEncoder extends AudioEncoder {
+
+
+    private final static String AUDIO_MIME = "audio/mp4a-latm";
+    private final static long audioBytesPerSample = 44100 * 16 / 8;
+
+    AACAudioEncoder(String rawAudioFile) {
+        super(rawAudioFile);
+    }
+
+    @Override
+    public String encodeToFile(String outEncodeFile) {
+        FileInputStream fisRawAudio = null;
+        FileOutputStream fosAccAudio = null;
+        try {
+            fisRawAudio = new FileInputStream(rawAudioFile);
+            fosAccAudio = new FileOutputStream(outEncodeFile);
+
+            final MediaCodec audioEncoder = createACCAudioDecoder();
+            audioEncoder.start();
+
+            ByteBuffer[] audioInputBuffers = audioEncoder.getInputBuffers();
+            ByteBuffer[] audioOutputBuffers = audioEncoder.getOutputBuffers();
+            boolean sawInputEOS = false;
+            boolean sawOutputEOS = false;
+            long audioTimeUs = 0;
+            BufferInfo outBufferInfo = new BufferInfo();
+
+            boolean readRawAudioEOS = false;
+            byte[] rawInputBytes = new byte[4096];
+            int readRawAudioCount = 0;
+            int rawAudioSize = 0;
+            long lastAudioPresentationTimeUs = 0;
+
+            int inputBufIndex, outputBufIndex;
+            while (!sawOutputEOS) {
+                if (!sawInputEOS) {
+                    inputBufIndex = audioEncoder.dequeueInputBuffer(10000);
+                    if (inputBufIndex >= 0) {
+                        ByteBuffer inputBuffer = audioInputBuffers[inputBufIndex];
+                        inputBuffer.clear();
+
+                        int bufferSize = inputBuffer.remaining();
+                        if (bufferSize != rawInputBytes.length) {
+                            rawInputBytes = new byte[bufferSize];
+                        }
+
+                        if (!readRawAudioEOS) {
+                            readRawAudioCount = fisRawAudio.read(rawInputBytes);
+                            if (readRawAudioCount == -1) {
+                                readRawAudioEOS = true;
+                            }
+                        }
+
+                        if (readRawAudioEOS) {
+                            audioEncoder.queueInputBuffer(inputBufIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                            sawInputEOS = true;
+                        } else {
+                            inputBuffer.put(rawInputBytes, 0, readRawAudioCount);
+                            rawAudioSize += readRawAudioCount;
+                            audioEncoder.queueInputBuffer(inputBufIndex, 0, readRawAudioCount, audioTimeUs, 0);
+                            audioTimeUs = (long) (1000000 * (rawAudioSize / 2.0) / audioBytesPerSample);
+                        }
+                    }
+                }
+
+                outputBufIndex = audioEncoder.dequeueOutputBuffer(outBufferInfo, 10000);
+                if (outputBufIndex >= 0) {
+
+                    // Simply ignore codec config buffers.
+                    if ((outBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                        audioEncoder.releaseOutputBuffer(outputBufIndex, false);
+                        continue;
+                    }
+
+                    if (outBufferInfo.size != 0) {
+                        ByteBuffer outBuffer = audioOutputBuffers[outputBufIndex];
+                        outBuffer.position(outBufferInfo.offset);
+                        outBuffer.limit(outBufferInfo.offset + outBufferInfo.size);
+                        if (lastAudioPresentationTimeUs < outBufferInfo.presentationTimeUs) {
+                            lastAudioPresentationTimeUs = outBufferInfo.presentationTimeUs;
+                            int outBufSize = outBufferInfo.size;
+                            int outPacketSize = outBufSize + 7;
+
+                            outBuffer.position(outBufferInfo.offset);
+                            outBuffer.limit(outBufferInfo.offset + outBufSize);
+
+                            byte[] outData = new byte[outBufSize + 7];
+                            addADTStoPacket(outData, outPacketSize);
+                            outBuffer.get(outData, 7, outBufSize);
+
+                            fosAccAudio.write(outData, 0, outData.length);
+                        }
+                    }
+
+                    audioEncoder.releaseOutputBuffer(outputBufIndex, false);
+
+                    if ((outBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        sawOutputEOS = true;
+                    }
+                } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    audioOutputBuffers = audioEncoder.getOutputBuffers();
+                } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    @SuppressWarnings("unused") MediaFormat audioFormat = audioEncoder.getOutputFormat();
+                }
+            }
+            return outEncodeFile;
+        } catch (FileNotFoundException e) {
+//            CustomBugReporter.reportError(AACAudioEncoder.class.getName(), "file not found exception", e);
+            e.printStackTrace();
+        } catch (IOException e) {
+//            CustomBugReporter.reportError(AACAudioEncoder.class.getName(), "ioexception" ,e);
+            e.printStackTrace();
+        } finally {
+
+            try {
+                if (fisRawAudio != null)
+                    fisRawAudio.close();
+                if (fosAccAudio != null)
+                    fosAccAudio.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return null;
+    }
+
+    private MediaCodec createACCAudioDecoder() throws IOException {
+        MediaCodec codec = MediaCodec.createEncoderByType(AUDIO_MIME);
+        MediaFormat format = new MediaFormat();
+        format.setString(MediaFormat.KEY_MIME, AUDIO_MIME);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
+        format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
+        format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
+        format.setInteger(MediaFormat.KEY_AAC_PROFILE,
+                MediaCodecInfo.CodecProfileLevel.AACObjectLC);
+//		format.setString(MediaFormat.KEY_MIME, AUDIO_MIME);
+//		format.setInteger(MediaFormat.KEY_BIT_RATE, 128000);
+//		format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+//		format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
+//		format.setInteger(MediaFormat.KEY_AAC_PROFILE,
+//				MediaCodecInfo.CodecProfileLevel.AACObjectLC);
+        codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+        return codec;
+    }
+
+    /**
+     * Add ADTS header at the beginning of each and every AAC packet. This is
+     * needed as MediaCodec encoder generates a packet of raw AAC data.
+     * <p>
+     * Note the packetLen must count in the ADTS header itself.
+     **/
+    private void addADTStoPacket(byte[] packet, int packetLen) {
+        int profile = 2; // AAC LC
+        int freqIdx = 4; // 44.1KHz
+        int chanCfg = 2; // Channel Configurations
+
+        // fill in ADTS data
+        packet[0] = (byte) 0xFF;
+        packet[1] = (byte) 0xF9;
+        packet[2] = (byte) (((profile - 1) << 6) + (freqIdx << 2) + (chanCfg >> 2));
+        packet[3] = (byte) (((chanCfg & 3) << 6) + (packetLen >> 11));
+        packet[4] = (byte) ((packetLen & 0x7FF) >> 3);
+        packet[5] = (byte) (((packetLen & 7) << 5) + 0x1F);
+        packet[6] = (byte) 0xFC;
+    }
+}

+ 143 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AndroidAudioDecoder.java

@@ -0,0 +1,143 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Android SDK ֧�ֵı�����
+ *
+ * @version android.os.Build.VERSION.SDK_INT >= 16
+ */
+public class AndroidAudioDecoder extends AudioDecoder {
+
+
+    AndroidAudioDecoder(String encodefile) {
+        super(encodefile);
+    }
+
+    @Override
+    public RawAudioInfo decodeToFile(String outFile) throws IOException {
+
+        long beginTime = System.currentTimeMillis();
+
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(mEncodeFile);
+
+        MediaFormat mediaFormat = null;
+        for (int i = 0; i < extractor.getTrackCount(); i++) {
+            MediaFormat format = extractor.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("audio/")) {
+                extractor.selectTrack(i);
+                mediaFormat = format;
+                break;
+            }
+        }
+
+        if (mediaFormat == null) {
+            Log.e("AndroidAudioDecoder", "not a valid file with audio track..");
+            extractor.release();
+            return null;
+        }
+
+        RawAudioInfo rawAudioInfo = new RawAudioInfo();
+        rawAudioInfo.channel = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+        rawAudioInfo.sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+//        rawAudioInfo.channel = 1;
+//        rawAudioInfo.sampleRate = 8000;
+        rawAudioInfo.tempRawFile = outFile;
+
+        String mediaMime = mediaFormat.getString(MediaFormat.KEY_MIME);
+        MediaCodec codec = MediaCodec.createDecoderByType(mediaMime);
+        codec.configure(mediaFormat, null, null, 0);
+        codec.start();
+
+        ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+        ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
+
+        final double audioDurationUs = mediaFormat.getLong(MediaFormat.KEY_DURATION);
+        final long kTimeOutUs = 5000;
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawInputEOS = false;
+        boolean sawOutputEOS = false;
+        int totalRawSize = 0;
+        try (FileOutputStream fosDecoder = new FileOutputStream(rawAudioInfo.tempRawFile)) {
+            while (!sawOutputEOS) {
+                if (!sawInputEOS) {
+                    int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
+                    if (inputBufIndex >= 0) {
+                        ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
+                        int sampleSize = extractor.readSampleData(dstBuf, 0);
+                        if (sampleSize < 0) {
+                            Log.e("AndroidAudioDecoder", "saw input EOS.");
+                            sawInputEOS = true;
+                            codec.queueInputBuffer(inputBufIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                        } else {
+                            long presentationTimeUs = extractor.getSampleTime();
+                            codec.queueInputBuffer(inputBufIndex, 0, sampleSize, presentationTimeUs, 0);
+                            extractor.advance();
+                        }
+                    }
+                }
+                int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+                if (res >= 0) {
+
+                    // Simply ignore codec config buffers.
+                    if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                        Log.e("AndroidAudioDecoder", "audio encoder: codec config buffer");
+                        codec.releaseOutputBuffer(res, false);
+                        continue;
+                    }
+
+                    if (info.size != 0) {
+
+                        ByteBuffer outBuf = codecOutputBuffers[res];
+
+                        outBuf.position(info.offset);
+                        outBuf.limit(info.offset + info.size);
+                        byte[] data = new byte[info.size];
+                        outBuf.get(data);
+                        totalRawSize += data.length;
+                        fosDecoder.write(data);
+                        if (mOnAudioDecoderListener != null)
+                            mOnAudioDecoderListener.onDecode(data, info.presentationTimeUs / audioDurationUs);
+                        Log.e("AndroidAudioDecoder", mEncodeFile + " presentationTimeUs : " + info.presentationTimeUs);
+                    }
+
+                    codec.releaseOutputBuffer(res, false);
+
+                    if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        Log.e("AndroidAudioDecoder", "saw output EOS.");
+                        sawOutputEOS = true;
+                    }
+
+                } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    codecOutputBuffers = codec.getOutputBuffers();
+                    Log.e("AndroidAudioDecoder", "output buffers have changed.");
+                } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    MediaFormat oformat = codec.getOutputFormat();
+                    Log.e("AndroidAudioDecoder", "output format has changed to " + oformat);
+                }
+            }
+            rawAudioInfo.size = totalRawSize;
+
+            if (mOnAudioDecoderListener != null)
+                mOnAudioDecoderListener.onDecode(null, 1);
+
+            Log.e("AndroidAudioDecoder", "decode " + outFile + " cost " + (System.currentTimeMillis() - beginTime) + " milliseconds !");
+
+            return rawAudioInfo;
+        } finally {
+            codec.stop();
+            codec.release();
+            extractor.release();
+        }
+
+    }
+}

+ 39 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AudioDecoder.java

@@ -0,0 +1,39 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+import java.io.IOException;
+
+/**
+ * -��Ƶ������
+ */
+public abstract class AudioDecoder {
+
+    final String mEncodeFile;
+
+    OnAudioDecoderListener mOnAudioDecoderListener;
+
+    AudioDecoder(String encodefile) {
+        this.mEncodeFile = encodefile;
+    }
+
+    public static AudioDecoder createDefualtDecoder(String encodefile) {
+        return new AndroidAudioDecoder(encodefile);
+    }
+
+    public void setOnAudioDecoderListener(OnAudioDecoderListener l) {
+        this.mOnAudioDecoderListener = l;
+    }
+
+    public abstract RawAudioInfo decodeToFile(String outFile)
+            throws IOException;
+
+    public static class RawAudioInfo {
+        public String tempRawFile;
+        public int size;
+        public long sampleRate;
+        public int channel;
+    }
+
+    public interface OnAudioDecoderListener {
+        void onDecode(byte[] decodedBytes, double progress) throws IOException;
+    }
+}

+ 17 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/AudioEncoder.java

@@ -0,0 +1,17 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+
+public abstract class AudioEncoder {
+
+    final String rawAudioFile;
+
+    AudioEncoder(String rawAudioFile) {
+        this.rawAudioFile = rawAudioFile;
+    }
+
+    public static AudioEncoder createAccEncoder(String rawAudioFile) {
+        return new AACAudioEncoder(rawAudioFile);
+    }
+
+    public abstract String encodeToFile(String outEncodeFile);
+}

+ 30 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/MD5Util.java

@@ -0,0 +1,30 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+import java.security.MessageDigest;
+
+public class MD5Util {
+
+    public static String getMD5Str(String src) {
+        try {
+            MessageDigest messageDigest = MessageDigest.getInstance("MD5");
+            messageDigest.update(src.getBytes());
+            return toHexString(messageDigest.digest());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    private static String toHexString(byte[] b) {
+        StringBuilder sb = new StringBuilder(b.length * 2);
+        for (byte aB : b) {
+            sb.append(Digit[(aB & 0xf0) >>> 4]);
+            sb.append(Digit[aB & 0x0f]);
+        }
+        return sb.toString();
+    }
+
+    private static final char[] Digit = {'0', '1', '2', '3', '4', '5', '6', '7',
+            '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+}

+ 222 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/VideoAudioMixer.java

@@ -0,0 +1,222 @@
+package cn.i2edu.dubbing_lib.audioUtils;
+
+import android.content.Context;
+
+
+import com.coremedia.iso.boxes.Container;
+import com.googlecode.mp4parser.FileDataSourceImpl;
+import com.googlecode.mp4parser.authoring.Movie;
+import com.googlecode.mp4parser.authoring.Track;
+import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
+import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
+import com.googlecode.mp4parser.authoring.tracks.AACTrackImpl;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.BufferOverflowException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by Administrator on 2016/11/24.
+ */
+
+public class VideoAudioMixer {
+    private VideoAudioMixListener mListener = null;
+    private Context context;
+
+    public VideoAudioMixer(Context context) {
+        this.context = context;
+    }
+
+    public interface VideoAudioMixListener {
+        void mixSuccess();
+
+        void mixFail(String reason);
+    }
+
+    public void setListener(VideoAudioMixListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * 合成音频和视频
+     *
+     * @param audioPath     原音频文件路径
+     * @param vedioPath     原视频路径
+     * @param mixedFileName 合成后的文件名
+     * @param mixedFilePath 合成后的文件路径
+     */
+//    public void mux(String audioPath, String vedioPath, String mixedFileName, String mixedFilePath) {
+//        File mixDir = new File(mixedFilePath);
+//        if (!mixDir.exists()) {
+//            mixDir.mkdir();
+//        }
+//        try {
+//            Movie countVideo = MovieCreator.build(vedioPath);
+//            // 创建新的视频
+//            Movie newMovie = new Movie();
+//            // 创建新的音轨
+//            AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(audioPath));
+//            List<Track> videoTrackList = new ArrayList<>();
+//            // 获取原视频视频轨
+//            for (Track t : countVideo.getTracks()) {
+//                if (t.getHandler().equals("vide")) {
+//                    videoTrackList.add(t);
+//                }
+//            }
+//            // 在新视频文件中添加原视频的视频
+//            for (int i = 0; i < videoTrackList.size(); i++) {
+//                newMovie.addTrack(videoTrackList.get(i));
+//            }
+//            // 在新视频中添加音轨
+//            newMovie.addTrack(aacTrack);
+//            // 开始合成并且输出
+//            {
+//                Container out = new DefaultMp4Builder().build(newMovie);
+////                FileOutputStream fos = new FileOutputStream(new File(mixDir.getAbsolutePath() + "/" + mixedFileName));
+//                File file = new File(mixDir.getAbsolutePath() + "/" + mixedFileName);
+//                RandomAccessFile fos = new RandomAccessFile(file,"rw");
+//                out.writeContainer(fos.getChannel());
+//                fos.close();
+//            }
+//            if (mListener != null) {
+//                mListener.mixSuccess();
+////                BufferOverflowException
+//            }
+//        } catch (FileNotFoundException e) {
+//            e.printStackTrace();
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "file not found exception", e);
+//            if (mListener != null) {
+//                mListener.mixFail(null);
+//            }
+//        } catch (IOException e) {
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "ioexception", e);
+//            e.printStackTrace();
+//            if (mListener != null) {
+//                mListener.mixFail(null);
+//            }
+//            } catch (BufferOverflowException e){
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "合成音频和视频时出错",e);
+//            e.printStackTrace();
+//            if (mListener != null){
+//                mListener.mixFail(context.getString(R.string.toast_run_out_of_memory));
+//            }
+//        }
+//
+//    }
+
+    public void mux(String audioPath, String vedioPath, String mixedFileName, String mixedFilePath) {
+        File mixDir = new File(mixedFilePath);
+        if (!mixDir.exists()) {
+            mixDir.mkdir();
+        }
+        try {
+            Movie countVideo = MovieCreator.build(vedioPath);
+            // 创建新的视频
+            Movie newMovie = new Movie();
+            // 创建新的音轨
+            AACTrackImpl aacTrack = new AACTrackImpl(new FileDataSourceImpl(audioPath));
+            List<Track> videoTrackList = new ArrayList<>();
+            // 获取原视频视频轨
+            for (Track t : countVideo.getTracks()) {
+                if (t.getHandler().equals("vide")) {
+                    videoTrackList.add(t);
+                }
+            }
+            // 在新视频文件中添加原视频的视频
+            for (int i = 0; i < videoTrackList.size(); i++) {
+                newMovie.addTrack(videoTrackList.get(i));
+            }
+            // 在新视频中添加音轨
+            newMovie.addTrack(aacTrack);
+            // 开始合成并且输出
+            {
+                Container out = new DefaultMp4Builder().build(newMovie);
+                FileOutputStream fos = new FileOutputStream(new File(mixDir.getAbsolutePath() + "/" + mixedFileName));
+                BufferedWritableFileByteChannel byteBufferByteChannel = new BufferedWritableFileByteChannel(fos);
+                out.writeContainer(byteBufferByteChannel);
+                byteBufferByteChannel.close();
+                fos.close();
+            }
+            if (mListener != null) {
+                mListener.mixSuccess();
+//                BufferOverflowException
+            }
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "file not found exception", e);
+            if (mListener != null) {
+                mListener.mixFail(null);
+            }
+        } catch (IOException e) {
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "ioexception", e);
+            e.printStackTrace();
+            if (mListener != null) {
+                mListener.mixFail(null);
+            }
+        } catch (BufferOverflowException e){
+//            CustomBugReporter.reportError(VedioAudioMixer.class.getName(), "合成音频和视频时出错",e);
+            e.printStackTrace();
+            if (mListener != null){
+                mListener.mixFail("内存不足,请清理内存后重试!");
+            }
+        }
+
+    }
+
+    private static class BufferedWritableFileByteChannel implements WritableByteChannel {
+        private static final int BUFFER_CAPACITY = 1000000;
+
+        private boolean isOpen = true;
+        private final OutputStream outputStream;
+        private final ByteBuffer byteBuffer;
+        private final byte[] rawBuffer = new byte[BUFFER_CAPACITY];
+
+        private BufferedWritableFileByteChannel(OutputStream outputStream) {
+            this.outputStream = outputStream;
+            this.byteBuffer = ByteBuffer.wrap(rawBuffer);
+        }
+
+        @Override
+        public int write(ByteBuffer inputBuffer) {
+            int inputBytes = inputBuffer.remaining();
+            if (inputBytes > byteBuffer.remaining()) {
+                dumpToFile();
+                byteBuffer.clear();
+
+                if (inputBytes > byteBuffer.remaining()) {
+                    throw new BufferOverflowException();
+                }
+            }
+
+            byteBuffer.put(inputBuffer);
+
+            return inputBytes;
+        }
+
+        @Override
+        public boolean isOpen() {
+            return isOpen;
+        }
+
+        @Override
+        public void close() {
+            dumpToFile();
+            isOpen = false;
+        }
+        private void dumpToFile() {
+            try {
+                outputStream.write(rawBuffer, 0, byteBuffer.position());
+            } catch (IOException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+}

+ 19 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/FinalResult.java

@@ -0,0 +1,19 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+/**
+ * <p>Title: FinalResult</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class FinalResult extends Result {
+	
+	public int ret;
+	
+	public float total_score;
+	
+	@Override
+	public String toString() {
+		return "返回值:" + ret + ",总分:" + total_score;
+	}
+}

+ 36 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadSentenceResult.java

@@ -0,0 +1,36 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+
+import cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.util.ResultFormatUtil;
+
+/**
+ * <p>Title: ReadSentenceResult</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class ReadSentenceResult extends Result {
+	
+	public ReadSentenceResult() {
+		category = "read_sentence";
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder buffer = new StringBuilder();
+		
+		if ("cn".equals(language)) {
+			buffer.append("[总体结果]\n").append("评测内容:").append(content).append("\n").append("朗读时长:").append(time_len).append("\n").append("总分:").append(total_score).append("\n\n")
+				.append("[朗读详情]").append(ResultFormatUtil.formatDetails_CN(sentences));
+		} else {
+			if (is_rejected) {
+				buffer.append("检测到乱读,").append("except_info:").append(except_info).append("\n\n");	// except_info代码说明详见《语音评测参数、结果说明文档》
+			}
+			
+			buffer.append("[总体结果]\n").append("评测内容:").append(content).append("\n").append("总分:").append(total_score).append("\n\n")
+				.append("[朗读详情]").append(ResultFormatUtil.formatDetails_EN(sentences));
+		}
+		
+		return buffer.toString();
+	}
+}

+ 27 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadSyllableResult.java

@@ -0,0 +1,27 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.util.ResultFormatUtil;
+
+/**
+ * <p>Title: ReadSyllableResult</p>
+ * <p>Description: 中文单字评测结果</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class ReadSyllableResult extends Result {
+	
+	public ReadSyllableResult() {
+		language = "cn";
+		category = "read_syllable";
+	}
+	
+	@Override
+	public String toString() {
+
+		return "[总体结果]\n" + "评测内容:" + content + "\n" +
+				"朗读时长:" + time_len + "\n" +
+				"总分:" + total_score + "\n\n" +
+				"[朗读详情]" + ResultFormatUtil.formatDetails_CN(sentences);
+	}
+}

+ 45 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/ReadWordResult.java

@@ -0,0 +1,45 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.util.ResultFormatUtil;
+
+/**
+ * <p>Title: ReadWordResult</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ *
+ * @author iflytek
+ */
+public class ReadWordResult extends Result {
+
+    public ReadWordResult() {
+        category = "read_word";
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder buffer = new StringBuilder();
+
+        if ("cn".equals(language)) {
+            buffer.append("[总体结果]\n")
+                    .append("评测内容:").append(content).append("\n")
+                    .append("朗读时长:").append(time_len).append("\n")
+                    .append("总分:").append(total_score).append("\n\n")
+                    .append("[朗读详情]")
+                    .append(ResultFormatUtil.formatDetails_CN(sentences));
+        } else {
+            if (is_rejected) {
+                buffer.append("检测到乱读,")
+                        .append("except_info:").append(except_info).append("\n\n");    // except_info代码说明详见《语音评测参数、结果说明文档》
+            }
+
+            buffer.append("[总体结果]\n")
+                    .append("评测内容:").append(content).append("\n")
+                    .append("总分:").append(total_score).append("\n\n")
+                    .append("[朗读详情]")
+                    .append(ResultFormatUtil.formatDetails_EN(sentences));
+        }
+
+        return buffer.toString();
+    }
+}

+ 54 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/Result.java

@@ -0,0 +1,54 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+import java.util.ArrayList;
+
+import cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Sentence;
+
+/**
+ * <p>Title: Result</p>
+ * <p>Description: 评测结果</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class Result {
+	/**
+	 * 评测语种:en(英文)、cn(中文)
+	 */
+	public String language;
+	/**
+	 * 评测种类:read_syllable(cn单字)、read_word(词语)、read_sentence(句子) 
+	 */
+	public String category;
+	/**
+	 * 开始帧位置,每帧相当于10ms
+	 */
+	public int beg_pos;
+	/**
+	 * 结束帧位置
+	 */
+	public int end_pos;
+	/**
+	 * 评测内容
+	 */
+	public String content;
+	/**
+	 * 总得分
+	 */
+	public float total_score;
+	/**
+	 * 时长(cn)
+	 */
+	public int time_len;
+	/**
+	 * 异常信息(en)
+	 */
+	public String except_info;
+	/**
+	 * 是否乱读(cn)
+	 */
+	public boolean is_rejected;
+	/**
+	 * xml结果中的sentence标签
+	 */
+	public ArrayList<Sentence> sentences;
+}

+ 272 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/XmlResultParser.java

@@ -0,0 +1,272 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse;
+
+import android.text.TextUtils;
+import android.util.Xml;
+
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Phone;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Sentence;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Syll;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Word;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+/**
+ * <p>Title: XmlResultParser</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class XmlResultParser {
+	
+	@SuppressWarnings("TryWithIdenticalCatches")
+	public Result parse(String xml) {
+		if (TextUtils.isEmpty(xml)) {
+			return null;
+		}
+		
+		XmlPullParser pullParser = Xml.newPullParser();
+		
+		try {
+			InputStream ins = new ByteArrayInputStream(xml.getBytes());
+			pullParser.setInput(ins, "utf-8");
+			FinalResult finalResult = null;
+			
+			int eventType = pullParser.getEventType();
+			while (XmlPullParser.END_DOCUMENT != eventType) {
+				switch (eventType) {
+				case XmlPullParser.START_TAG:
+					if ("FinalResult".equals(pullParser.getName())) {
+						// 只有一个总分的结果
+						finalResult = new FinalResult();
+					} else if ("ret".equals(pullParser.getName())) {
+						finalResult.ret = getInt(pullParser, "value");
+					} else if ("total_score".equals(pullParser.getName())) {
+						finalResult.total_score = getFloat(pullParser, "value");
+					} else if ("xml_result".equals(pullParser.getName())) {
+						// 详细结果
+						return parseResult(pullParser);
+					}
+					
+					break;
+				case XmlPullParser.END_TAG:
+					if ("FinalResult".equals(pullParser.getName())) {
+						return finalResult;
+					}
+					break;
+				
+				default:
+					break;
+				}
+				eventType = pullParser.next();
+			}
+		} catch (XmlPullParserException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+		return null;
+	}
+	
+	@SuppressWarnings("TryWithIdenticalCatches")
+	private Result parseResult(XmlPullParser pullParser) {
+		Result result = null;
+		// <rec_paper>标签是否已扫描到
+		boolean rec_paperPassed = false;
+		Sentence sentence = null;
+		Word word = null;
+		Syll syll = null;
+		Phone phone = null;
+		
+		int eventType;
+		try {
+			eventType = pullParser.getEventType();
+			while (XmlPullParser.END_DOCUMENT != eventType) {
+				switch (eventType) {
+				case XmlPullParser.START_TAG:
+					if ("rec_paper".equals(pullParser.getName())) {
+						rec_paperPassed = true;
+					} else if ("read_syllable".equals(pullParser.getName())) {
+						if (!rec_paperPassed) {
+							result = new ReadSyllableResult();
+						} else {
+							readTotalResult(result, pullParser);
+						}
+					} else if ("read_word".equals(pullParser.getName())) {
+						if (!rec_paperPassed) {
+							result = new ReadWordResult();
+							String lan = getLanguage(pullParser);
+							result.language = (null == lan)? "cn": lan;
+						} else {
+							readTotalResult(result, pullParser);
+						}
+					} else if ("read_sentence".equals(pullParser.getName()) || 
+							"read_chapter".equals(pullParser.getName())) {
+						if (!rec_paperPassed) {
+							result = new ReadSentenceResult();
+							String lan = getLanguage(pullParser);
+							result.language = (null == lan)? "cn": lan;
+						} else {
+							readTotalResult(result, pullParser);
+						}
+					} else if ("sentence".equals(pullParser.getName())) {
+						if (null == result.sentences) {
+							result.sentences = new ArrayList<>();
+						}
+						sentence = createSentence(pullParser);
+					} else if ("word".equals(pullParser.getName())) {
+						if (null != sentence && null == sentence.words) {
+							sentence.words = new ArrayList<>();
+						}
+						word = createWord(pullParser);
+					} else if ("syll".equals(pullParser.getName())) {
+						if (null != word && null == word.sylls) {
+							word.sylls = new ArrayList<>();
+						}
+						syll = createSyll(pullParser);
+					} else if ("phone".equals(pullParser.getName())) {
+						if (null != syll && null == syll.phones) {
+							syll.phones = new ArrayList<>();
+						}
+						phone = createPhone(pullParser);
+					}
+					
+					break;
+				case XmlPullParser.END_TAG:
+					if ("phone".equals(pullParser.getName())) {
+						syll.phones.add(phone);
+					} else if ("syll".equals(pullParser.getName())) {
+						word.sylls.add(syll);
+					} else if ("word".equals(pullParser.getName())) {
+						sentence.words.add(word);
+					} else if ("sentence".equals(pullParser.getName())) {
+						result.sentences.add(sentence);
+					} else if ("read_syllable".equals(pullParser.getName()) 
+							|| "read_word".equals(pullParser.getName()) 
+							|| "read_sentence".equals(pullParser.getName())) {
+						return result;
+					} 
+					break;
+
+				default:
+					break;
+				}
+				
+				eventType = pullParser.next();
+			}
+		} catch (XmlPullParserException e) {
+			e.printStackTrace();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		
+		return result;
+	}
+	
+	private void readTotalResult(Result result, XmlPullParser pullParser) {
+		result.beg_pos = getInt(pullParser, "beg_pos");
+		result.end_pos = getInt(pullParser, "end_pos");
+		result.content = getContent(pullParser);
+		result.total_score = getFloat(pullParser, "total_score");
+		result.time_len = getInt(pullParser, "time_len");
+		result.except_info = getExceptInfo(pullParser);
+		result.is_rejected = getIsRejected(pullParser);
+	}
+	
+	private Phone createPhone(XmlPullParser pullParser) {
+		Phone phone;
+		phone = new Phone();
+		phone.beg_pos = getInt(pullParser, "beg_pos");
+		phone.end_pos = getInt(pullParser, "end_pos");
+		phone.content = getContent(pullParser);
+		phone.dp_message = getInt(pullParser, "dp_message");
+		phone.time_len = getInt(pullParser, "time_len");
+		return phone;
+	}
+
+	private Syll createSyll(XmlPullParser pullParser) {
+		Syll syll;
+		syll = new Syll();
+		syll.beg_pos = getInt(pullParser, "beg_pos");
+		syll.end_pos = getInt(pullParser, "end_pos");
+		syll.content = getContent(pullParser);
+		syll.symbol = getSymbol(pullParser);
+		syll.dp_message = getInt(pullParser, "dp_message");
+		syll.time_len = getInt(pullParser, "time_len");
+		return syll;
+	}
+
+	private Word createWord(XmlPullParser pullParser) {
+		Word word;
+		word = new Word();
+		word.beg_pos = getInt(pullParser, "beg_pos");
+		word.end_pos = getInt(pullParser, "end_pos");
+		word.content = getContent(pullParser);
+		word.symbol = getSymbol(pullParser);
+		word.time_len = getInt(pullParser, "time_len");
+		word.dp_message = getInt(pullParser, "dp_message");
+		word.total_score = getFloat(pullParser, "total_score");
+		word.global_index = getInt(pullParser, "global_index");
+		word.index = getInt(pullParser, "index");
+		return word;
+	}
+
+	private Sentence createSentence(XmlPullParser pullParser) {
+		Sentence sentence;
+		sentence = new Sentence();
+		sentence.beg_pos = getInt(pullParser, "beg_pos");
+		sentence.end_pos = getInt(pullParser, "end_pos");
+		sentence.content = getContent(pullParser);
+		sentence.time_len = getInt(pullParser, "time_len");
+		sentence.index = getInt(pullParser, "index");
+		sentence.word_count = getInt(pullParser, "word_count");
+		return sentence;
+	}
+
+	private String getLanguage(XmlPullParser pullParser) {
+		return pullParser.getAttributeValue(null, "lan");
+	}
+	
+	private String getExceptInfo(XmlPullParser pullParser) {
+		return pullParser.getAttributeValue(null, "except_info");
+	}
+	
+	private boolean getIsRejected(XmlPullParser pullParser) {
+		String isRejected = pullParser.getAttributeValue(null, "is_rejected");
+		if (null == isRejected) {
+			return false;
+		}
+		
+		return Boolean.parseBoolean(isRejected);
+	}
+
+	private String getSymbol(XmlPullParser pullParser) {
+		return pullParser.getAttributeValue(null, "symbol");
+	}
+
+	private float getFloat(XmlPullParser pullParser, String attrName) {
+		String val = pullParser.getAttributeValue(null, attrName);
+		if (null == val) {
+			return 0f;
+		}
+		return Float.parseFloat(val);
+	}
+
+	private String getContent(XmlPullParser pullParser) {
+		return pullParser.getAttributeValue(null, "content");
+	}
+	
+	private int getInt(XmlPullParser pullParser, String attrName) {
+		String val = pullParser.getAttributeValue(null, attrName);
+		if (null == val) {
+			return 0;
+		}
+		return Integer.parseInt(val);
+	}
+}

+ 101 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Phone.java

@@ -0,0 +1,101 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity;
+
+import java.util.HashMap;
+
+/**
+ * <p>Title: Phone</p>
+ * <p>Description: 音素,对应于xml结果中的Phone标签</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class Phone {
+	/**
+	 * 讯飞音标-标准音标映射表(en)
+	 */
+	public static final HashMap<String, String> phone_map = new HashMap<>();
+	
+	static {
+		phone_map.put("aa", "ɑ:");
+		phone_map.put("oo", "ɔ");
+		phone_map.put("ae", "æ");
+		phone_map.put("ah", "ʌ");
+		phone_map.put("ao", "ɔ:");
+		phone_map.put("aw", "aʊ");
+		phone_map.put("ax", "ə");
+		phone_map.put("ay", "aɪ");
+		phone_map.put("eh", "e");
+		phone_map.put("er", "ə:");
+		phone_map.put("ey", "eɪ");
+		phone_map.put("ih", "ɪ");
+		phone_map.put("iy", "i:");
+		phone_map.put("ow", "əʊ");
+		phone_map.put("oy", "ɔɪ");
+		phone_map.put("uh", "ʊ");
+		phone_map.put("uw", "ʊ:");
+		phone_map.put("ch", "tʃ");
+		phone_map.put("dh", "ð");
+		phone_map.put("hh", "h");
+		phone_map.put("jh", "dʒ");
+		phone_map.put("ng", "ŋ");
+		phone_map.put("sh", "ʃ");
+		phone_map.put("th", "θ");
+		phone_map.put("zh", "ʒ");
+		phone_map.put("y", "j");
+		phone_map.put("d", "d");
+		phone_map.put("k", "k");
+		phone_map.put("l", "l");
+		phone_map.put("m", "m");
+		phone_map.put("n", "n");
+		phone_map.put("b", "b");
+		phone_map.put("f", "f");
+		phone_map.put("g", "g");
+		phone_map.put("p", "p");
+		phone_map.put("r", "r");
+		phone_map.put("s", "s");
+		phone_map.put("t", "t");
+		phone_map.put("v", "v");
+		phone_map.put("w", "w");
+		phone_map.put("z", "z");
+		phone_map.put("ar", "eə");
+		phone_map.put("ir", "iə");
+		phone_map.put("ur", "ʊə");
+		phone_map.put("tr", "tr");
+		phone_map.put("dr", "dr");
+		phone_map.put("ts", "ts");
+		phone_map.put("dz", "dz");
+	}
+	
+	/**
+	 * 开始帧位置,每帧相当于10ms
+	 */
+	public int beg_pos;
+	/**
+	 * 结束帧位置
+	 */
+	public int end_pos;
+	/**
+	 * 音素内容
+	 */
+	public String content;
+	/**
+	 * 增漏读信息:0(正确),16(漏读),32(增读),64(回读),128(替换)
+	 */
+	public int dp_message;
+	/**
+	 * 时长(单位:帧,每帧相当于10ms)(cn)
+	 */
+	public int time_len;
+	
+	/**
+	 * 得到content对应的标准音标(en)
+	 */
+	public String getStdSymbol() {
+		return getStdSymbol(content);
+	}
+	
+	public static String getStdSymbol(String content) {
+		String std = phone_map.get(content);
+		return (null == std)? content: std;
+	}
+	
+}

+ 44 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Sentence.java

@@ -0,0 +1,44 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity;
+
+import java.util.ArrayList;
+
+/**
+ * <p>Title: Sentence</p>
+ * <p>Description: 句子,对应于xml结果中的sentence标签</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class Sentence {
+	/**
+	 * 开始帧位置,每帧相当于10ms
+	 */
+	public int beg_pos;
+	/**
+	 * 结束帧位置
+	 */
+	public int end_pos;
+	/**
+	 * 句子内容
+	 */
+	public String content;
+	/**
+	 * 总得分
+	 */
+	public float total_score;
+	/**
+	 * 时长(单位:帧,每帧相当于10ms)(cn)
+	 */
+	public int time_len;
+	/**
+	 * 句子的索引(en)
+	 */
+	public int index;
+	/**
+	 * 单词数(en)
+	 */
+	public int word_count;
+	/**
+	 * sentence包括的word
+	 */
+	public ArrayList<Word> words;
+}

+ 56 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Syll.java

@@ -0,0 +1,56 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity;
+
+import java.util.ArrayList;
+
+/**
+ * <p>Title: Syll</p>
+ * <p>Description: 音节,对应于结果xml中的Syll标签</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class Syll {
+	/**
+	 * 开始帧位置,每帧相当于10ms
+	 */
+	public int beg_pos;
+	/**
+	 * 结束帧位置
+	 */
+	public int end_pos;
+	/**
+	 * 音节内容
+	 */
+	public String content;
+	/**
+	 * 拼音(cn),数字代表声调,5表示轻声,如fen1
+	 */
+	public String symbol;
+	/**
+	 * 增漏读信息:0(正确),16(漏读),32(增读),64(回读),128(替换)
+	 */
+	public int dp_message;
+	/**
+	 * 时长(单位:帧,每帧相当于10ms)(cn)
+	 */
+	public int time_len;
+	/**
+	 * Syll包含的音节
+	 */
+	public ArrayList<Phone> phones;
+	
+	/**
+	 * 获取音节的标准音标(en)
+	 * 
+	 * @return 标准音标
+	 */
+	public String getStdSymbol() {
+		StringBuilder stdSymbol = new StringBuilder();
+		String[] symbols = content.split(" ");
+
+		for (String symbol1 : symbols) {
+			stdSymbol.append(Phone.getStdSymbol(symbol1));
+		}
+		
+		return stdSymbol.toString();
+	}
+}

+ 57 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/entity/Word.java

@@ -0,0 +1,57 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity;
+
+import java.util.ArrayList;
+
+/**
+ * <p>Title: Word</p>
+ * <p>Description: 单词,对应于结果xml中的word标签</p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class Word {
+	/**
+	 * 开始帧位置,每帧相当于10ms
+	 */
+	@SuppressWarnings("unused")
+	public int beg_pos;
+	/**
+	 * 结束帧位置
+	 */
+	@SuppressWarnings("unused")
+	public int end_pos;
+	/**
+	 * 单词内容
+	 */
+	public String content;
+	/**
+	 * 增漏读信息:0(正确),16(漏读),32(增读),64(回读),128(替换)
+	 */
+	public int dp_message;
+	/**
+	 * 单词在全篇索引(en)
+	 */
+	@SuppressWarnings("unused")
+	public int global_index;
+	/**
+	 * 单词在句子中的索引(en)
+	 */
+	@SuppressWarnings("unused")
+	public int index;
+	/**
+	 * 拼音(cn),数字代表声调,5表示轻声,如fen1
+	 */
+	public String symbol;
+	/**
+	 * 时长(单位:帧,每帧相当于10ms)(cn)
+	 */
+	public int time_len;
+	/**
+	 * 单词得分(en)
+	 */
+	public float total_score;
+	/**
+	 * Word包含的Syll
+	 */
+	public ArrayList<Syll> sylls;
+	
+}

+ 118 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/util/ResultFormatUtil.java

@@ -0,0 +1,118 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.util;
+
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Phone;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Sentence;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Syll;
+import com.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.entity.Word;
+
+import java.util.ArrayList;
+
+/**
+ * <p>Title: ResultFormatUtl</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class ResultFormatUtil {
+	
+	/**
+	 * 将英语评测详情按格式输出
+	 * 
+	 * @param sentences 评测结果
+	 * @return 英语评测详情
+	 */
+	public static String formatDetails_EN(ArrayList<Sentence> sentences) {
+		StringBuilder buffer = new StringBuilder();
+		if (null == sentences) {
+			return buffer.toString();
+		}
+		
+		for (Sentence sentence: sentences ) {
+			if ("噪音".equals(ResultTranslateUtil.getContent(sentence.content)) 
+					|| "静音".equals(ResultTranslateUtil.getContent(sentence.content))) {
+				continue;
+			}
+			
+			if (null == sentence.words) {
+				continue;
+			}
+			for (Word word: sentence.words) {
+				if ("噪音".equals(ResultTranslateUtil.getContent(word.content)) 
+						|| "静音".equals(ResultTranslateUtil.getContent(word.content))) {
+					continue;
+				}
+				
+				buffer.append("\n单词[")
+						.append(ResultTranslateUtil.getContent(word.content))
+						.append("] ")
+						.append("朗读:")
+						.append(ResultTranslateUtil.getDpMessageInfo(word.dp_message)).append(" 得分:").append(word.total_score);
+				if (null == word.sylls) {
+					buffer.append("\n");
+					continue;
+				}
+				
+				for (Syll syll: word.sylls) {
+					buffer.append("\n└音节[").append(ResultTranslateUtil.getContent(syll.getStdSymbol())).append("] ");
+					if (null == syll.phones) {
+						continue;
+					}
+					
+					for (Phone phone: syll.phones) {
+						buffer.append("\n\t└音素[").append(ResultTranslateUtil.getContent(phone.getStdSymbol())).append("] ").append(" 朗读:").append(ResultTranslateUtil.getDpMessageInfo(phone.dp_message));
+					}
+					
+				}
+				buffer.append("\n");
+			}
+		}
+		
+		return buffer.toString();
+	}
+
+	/**
+	 * 将汉语评测详情按格式输出
+	 * 
+	 * @param sentences 评测结果
+	 * @return 汉语评测详情
+	 */
+	public static String formatDetails_CN(ArrayList<Sentence> sentences) {
+		StringBuilder buffer = new StringBuilder();
+		if (null == sentences) {
+			return buffer.toString();
+		}
+		
+		for (Sentence sentence: sentences ) {
+			if (null == sentence.words) {
+				continue;
+			}
+			
+			for (Word word: sentence.words) {
+				buffer.append("\n词语[").append(ResultTranslateUtil.getContent(word.content)).append("] ").append(word.symbol).append(" 时长:").append(word.time_len);
+				if (null == word.sylls) {
+					continue;
+				}
+				
+				for (Syll syll: word.sylls) {
+					if ("噪音".equals(ResultTranslateUtil.getContent(syll.content)) 
+							|| "静音".equals(ResultTranslateUtil.getContent(syll.content))) {
+						continue;
+					}
+					
+					buffer.append("\n└音节[").append(ResultTranslateUtil.getContent(syll.content)).append("] ").append(syll.symbol).append(" 时长:").append(syll.time_len);
+					if (null == syll.phones) {
+						continue;
+					}
+					
+					for (Phone phone: syll.phones) {
+						buffer.append("\n\t└音素[").append(ResultTranslateUtil.getContent(phone.content)).append("] ").append("时长:").append(phone.time_len).append(" 朗读:").append(ResultTranslateUtil.getDpMessageInfo(phone.dp_message));
+					}
+					
+				}
+				buffer.append("\n");
+			}
+		}
+		
+		return buffer.toString();
+	}
+}

+ 38 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audioEvaluator/resultParse/util/ResultTranslateUtil.java

@@ -0,0 +1,38 @@
+package cn.i2edu.dubbing_lib.audioUtils.audioEvaluator.resultParse.util;
+
+import android.util.SparseArray;
+
+import java.util.HashMap;
+
+/**
+ * <p>Title: ResultTranslateUtl</p>
+ * <p>Description: </p>
+ * <p>Company: www.iflytek.com</p>
+ * @author iflytek
+ */
+public class ResultTranslateUtil {
+	
+	private static final SparseArray<String> dp_message_map = new SparseArray<>();
+	private static final HashMap<String, String> special_content_map = new HashMap<>();
+	
+	static {
+		dp_message_map.put(0, "正常");
+		dp_message_map.put(16, "漏读");
+		dp_message_map.put(32, "增读");
+		dp_message_map.put(64, "回读");
+		dp_message_map.put(128, "替换");
+		
+		special_content_map.put("sil", "静音");
+		special_content_map.put("silv", "静音");
+		special_content_map.put("fil", "噪音");
+	}
+	
+	public static String getDpMessageInfo(int dp_message) {
+		return dp_message_map.get(dp_message);
+	}
+	
+	public static String getContent(String content) {
+		String val = special_content_map.get(content);
+		return (null == val)? content: val;
+	}
+}

+ 172 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/CommonFunction.java

@@ -0,0 +1,172 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+import android.os.Looper;
+
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+
+/**
+ * Created by zhengtongyu on 16/5/23.
+ */
+public class CommonFunction {
+    public static String FormatRecordTime(int recordTime) {
+        int minute = recordTime / 60;
+        int second = recordTime % 60;
+
+        String formatRecordTime = "";
+
+        if (minute != 0) {
+            formatRecordTime += String.valueOf(minute) + "′";
+        }
+
+        formatRecordTime += String.valueOf(second) + "″";
+
+        return formatRecordTime;
+    }
+
+    public static String GetDate() {
+        long time = System.currentTimeMillis();
+        SimpleDateFormat simpleDateFormat =
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
+        return simpleDateFormat.format(time);
+    }
+
+    public static String GetDate(long time) {
+        SimpleDateFormat simpleDateFormat =
+                new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
+        return simpleDateFormat.format(time);
+    }
+
+    public static boolean notEmpty(CharSequence text) {
+        return !isEmpty(text);
+    }
+
+    public static boolean isEmpty(CharSequence text) {
+        return text == null || text.length() == 0;
+
+    }
+
+//    public static String GetPackageName() {
+//        String processName = "";
+//        ActivityManager activityManager = (ActivityManager) MyApplication.getInstance()
+//                .getSystemService(Context.ACTIVITY_SERVICE);
+//        Iterator<ActivityManager.RunningAppProcessInfo> infoIterator =
+//                activityManager.getRunningAppProcesses().iterator();
+//        ActivityManager.RunningAppProcessInfo runningAppProcessInfo;
+//
+//        while (infoIterator.hasNext()) {
+//            runningAppProcessInfo = infoIterator.next();
+//
+//            try {
+//                if (runningAppProcessInfo.pid == android.os.Process.myPid()) {
+//                    processName = runningAppProcessInfo.processName;
+//                    return processName;
+//                }
+//            } catch (Exception e) {
+//                Log.e("查询进程出错", e.toString());
+//            }
+//        }
+//
+//        return processName;
+//    }
+
+
+    public static byte[] GetBytes(short shortValue, boolean bigEnding) {
+        byte[] byteArray = new byte[2];
+
+        if (bigEnding) {
+            byteArray[1] = (byte) (shortValue & 0x00ff);
+            shortValue >>= 8;
+            byteArray[0] = (byte) (shortValue & 0x00ff);
+        } else {
+            byteArray[0] = (byte) (shortValue & 0x00ff);
+            shortValue >>= 8;
+            byteArray[1] = (byte) (shortValue & 0x00ff);
+        }
+
+        return byteArray;
+    }
+
+    public static short GetShort(byte firstByte, byte secondByte, boolean bigEnding) {
+        short shortValue = 0;
+
+        if (bigEnding) {
+            shortValue |= (firstByte & 0x00ff);
+            shortValue <<= 8;
+            shortValue |= (secondByte & 0x00ff);
+        } else {
+            shortValue |= (secondByte & 0x00ff);
+            shortValue <<= 8;
+            shortValue |= (firstByte & 0x00ff);
+        }
+
+        return shortValue;
+    }
+
+    public static byte[] AverageShortByteArray(byte firstShortHighByte, byte firstShortLowByte,
+                                               byte secondShortHighByte, byte secondShortLowByte,
+                                               boolean bigEnding) {
+        short firstShort =
+                CommonFunction.GetShort(firstShortHighByte, firstShortLowByte, bigEnding);
+        short secondShort =
+                CommonFunction.GetShort(secondShortHighByte, secondShortLowByte, bigEnding);
+        return CommonFunction.GetBytes((short) (firstShort / 2 + secondShort / 2), bigEnding);
+    }
+
+    public static short AverageShort(byte firstShortHighByte, byte firstShortLowByte,
+                                     byte secondShortHighByte, byte secondShortLowByte,
+                                     boolean bigEnding) {
+        short firstShort =
+                CommonFunction.GetShort(firstShortHighByte, firstShortLowByte, bigEnding);
+        short secondShort =
+                CommonFunction.GetShort(secondShortHighByte, secondShortLowByte, bigEnding);
+        return (short) (firstShort / 2 + secondShort / 2);
+    }
+
+    public static short WeightShort(byte firstShortHighByte, byte firstShortLowByte,
+                                    byte secondShortHighByte, byte secondShortLowByte,
+                                    float firstWeight, float secondWeight, boolean bigEnding) {
+        short firstShort =
+                CommonFunction.GetShort(firstShortHighByte, firstShortLowByte, bigEnding);
+        short secondShort =
+                CommonFunction.GetShort(secondShortHighByte, secondShortLowByte, bigEnding);
+        return (short) (firstShort * firstWeight + secondShort * secondWeight);
+    }
+
+    public static boolean IsInMainThread() {
+        return Looper.myLooper() == Looper.getMainLooper();
+    }
+
+    public static byte[] GetByteBuffer(short[] shortArray, boolean bigEnding) {
+        return GetByteBuffer(shortArray, shortArray.length, bigEnding);
+    }
+
+    public static byte[] GetByteBuffer(short[] shortArray, int shortArrayLength,
+                                       boolean bigEnding) {
+        int actualShortArrayLength = shortArray.length;
+
+        if (shortArrayLength > actualShortArrayLength) {
+            shortArrayLength = actualShortArrayLength;
+        }
+
+        short shortValue;
+        byte[] byteArray = new byte[2 * shortArrayLength];
+
+        for (int i = 0; i < shortArrayLength; i++) {
+            shortValue = shortArray[i];
+
+            if (bigEnding) {
+                byteArray[i * 2 + 1] = (byte) (shortValue & 0x00ff);
+                shortValue >>= 8;
+                byteArray[i * 2] = (byte) (shortValue & 0x00ff);
+            } else {
+                byteArray[i * 2] = (byte) (shortValue & 0x00ff);
+                shortValue >>= 8;
+                byteArray[i * 2 + 1] = (byte) (shortValue & 0x00ff);
+            }
+        }
+
+        return byteArray;
+    }
+
+}

+ 48 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/Constant.java

@@ -0,0 +1,48 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+
+//import zty.composeaudio.BuildConfig;
+
+public class Constant {
+//    public static final boolean Debug = BuildConfig.DEBUG;
+
+    public static final int NoExistIndex = -1;
+
+    public static final int OneSecond = 1000;
+
+//    public static final int RecordSampleRate = 44100; // 采样率
+    public static final int RecordSampleRate = 8000; // 采样率
+    public static final int RecrrdSampleRate_16k = 16000;
+    public static final int RecordByteNumber = 2; // 采样率
+    public static final int RecordChannelNumber = 1;  // 与DEFAULT_CHANNEL_CONFIG相关,因为是mono单声,所以是1
+    public static final int RecordDataNumberInOneSecond =
+            RecordSampleRate * RecordByteNumber * RecordChannelNumber;
+    // 与DEFAULT_CHANNEL_CONFIG相关,因为是mono单声,所以是1
+    public static final int BehaviorSampleRate = 44100; // 采样率
+    public static final int LameMp3Quality = 7; // Lame Default Settings
+    public static final int LameBehaviorChannelNumber = RecordChannelNumber;
+    // 与DEFAULT_CHANNEL_CONFIG相关,因为是mono单声,所以是1
+    public static final int lameRecordBitRate = 64;
+    // Encoded bit rate. MP3 file will be encoded with bit rate 64kbps
+    public static final int LameBehaviorBitRate = 128;
+
+    public static final int MusicCutEndOffset = 2;
+
+    public static final int MaxDecodeProgress = 50;
+    public static final int NormalMaxProgress = 100;
+
+    public static final int RecordVolumeMaxRank = 9;
+
+    public static final int ThreadPoolCount = 5;
+
+    public static final float VoiceWeight = 1.8f;
+    public static final float VoiceBackgroundWeight = 0.2f;
+
+    public static final String IGeneImageSuffix = ".ipg";
+    public static final String JPGSuffix = ".jpg";
+    public static final String PngSuffix = ".png";
+    public static final String MusicSuffix = ".mp3";
+    public static final String LyricSuffix = ".lrc";
+    public static final String RecordSuffix = ".mp3";
+    public static final String PcmSuffix = ".pcm";
+}

+ 531 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/DecodeEngine.java

@@ -0,0 +1,531 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+import android.content.Context;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import com.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.sound.pcm.resampling.ssrc.SSRC;
+
+
+/**
+ * Created by 郑童宇 on 2016/03/04.
+ */
+public class DecodeEngine {
+    private static final String TAG = DecodeEngine.class.getName();
+    private static DecodeEngine instance;
+    private DecodeEngine() {
+    }
+
+    public static DecodeEngine getInstance(Context context) {
+        if (instance == null) {
+            synchronized (DecodeEngine.class) {
+                if (instance == null) {
+                    instance = new DecodeEngine();
+                }
+            }
+        }
+
+        return instance;
+    }
+
+    public void beginDecodeMusicFile(String musicFileUrl, String decodeFileUrl, int startSecond,
+                                     int desireSampleRate,
+                                     int desireChannel,
+                                     int desireBitwidth,
+                                     final DecodeOperateInterface decodeOperateInterface) {
+
+        final boolean decodeResult =
+                decodeMusicFile(musicFileUrl, decodeFileUrl, startSecond, desireSampleRate, desireChannel, desireBitwidth,
+                        decodeOperateInterface);
+
+//        Handler handler = new Handler(Looper.getMainLooper());
+//        handler.post(new Runnable() {
+//            @Override
+//            public void run() {
+                if (decodeResult) {
+                    decodeOperateInterface.decodeSuccess();
+                } else {
+                    decodeOperateInterface.decodeFail();
+                }
+                // 手动gc一下
+                System.gc();
+//            }
+//        });
+    }
+
+    private boolean decodeMusicFile(String musicFileUrl, String decodeFileUrl, int startSecond,
+                                    int desireSampleRate,
+                                    int desireChannel,
+                                    int desireBitwidth,
+//                                    Handler handler,
+                                    DecodeOperateInterface decodeOperateInterface) {
+        int sampleRate;
+        int channelCount;
+
+        long duration;
+
+        String mime;
+
+        MediaExtractor mediaExtractor = new MediaExtractor();
+        MediaFormat mediaFormat;
+        MediaCodec mediaCodec;
+
+        try {
+            mediaExtractor.setDataSource(musicFileUrl);
+        } catch (IOException e) {
+            LogFunction.error("设置解码音频文件路径错误", e);
+//            CustomBugReporter.reportError(DecodeEngine.class.getName(), "set decode audio file path error", e);
+            return false;
+        }
+
+        mediaFormat = mediaExtractor.getTrackFormat(0);
+        sampleRate = mediaFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) ?
+                mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) : 44100;
+        channelCount = mediaFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) ?
+                mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) : 1;
+        duration = mediaFormat.containsKey(MediaFormat.KEY_DURATION) ? mediaFormat.getLong
+                (MediaFormat.KEY_DURATION)
+                : 0;
+        mime = mediaFormat.containsKey(MediaFormat.KEY_MIME) ? mediaFormat.getString(MediaFormat
+                .KEY_MIME) : "";
+
+        LogFunction.log("歌曲信息",
+                "Track info: mime:" + mime + " 采样率sampleRate:" + sampleRate + " channels:" +
+                        channelCount + " duration:" + duration);
+        Log.d("audio_info", "Track info: mime:" + mime + " 采样率sampleRate:" + sampleRate + " channels:" +
+                channelCount + " duration:" + duration);
+        if (CommonFunction.isEmpty(mime) || !mime.startsWith("audio/")) {
+            LogFunction.error("解码文件不是音频文件", "mime:" + mime);
+            return false;
+        }
+
+        if (mime.equals("audio/ffmpeg")) {
+            mime = "audio/mpeg";
+            mediaFormat.setString(MediaFormat.KEY_MIME, mime);
+        }
+
+        try {
+            mediaCodec = MediaCodec.createDecoderByType(mime);
+
+            mediaCodec.configure(mediaFormat, null, null, 0);
+            mediaCodec.start();
+        } catch (Exception e) {
+            LogFunction.error("解码器configure出错", e);
+//            CustomBugReporter.reportError(DecodeEngine.class.getName(), "configure decoder error", e);
+            return false;
+        }
+
+        getDecodeData(mediaExtractor,
+                mediaCodec,
+                decodeFileUrl,
+                sampleRate,
+                channelCount,
+                startSecond,
+                desireSampleRate,
+                desireChannel,
+                desireBitwidth,
+//                handler,
+                decodeOperateInterface);
+        return true;
+    }
+
+    private void getDecodeData(MediaExtractor mediaExtractor,
+                               MediaCodec mediaCodec,
+                               String decodeFileUrl,
+                               int sampleRate,
+                               int channelCount,
+                               int startSecond,
+                               int desireSampleRate,
+                               int desireChannel,
+                               int desireBitwidth,
+//                               Handler handler,
+                               final DecodeOperateInterface decodeOperateInterface) {
+        boolean decodeInputEnd = false;
+        boolean decodeOutputEnd = false;
+
+        int sampleDataSize;
+        int inputBufferIndex;
+        int outputBufferIndex;
+        int byteNumber;
+
+        long decodeNoticeTime = System.currentTimeMillis();
+        long decodeTime;
+        long presentationTimeUs = 0;
+
+        final long timeOutUs = 100;
+//        final long startMicroseconds = startSecond * 1000 * 1000;
+//        final long endMicroseconds = endSecond * 1000 * 1000;
+        final long startMicroseconds = startSecond * 1000;
+//        final long endMicroseconds = endSecond * 1000;
+
+        ByteBuffer[] inputBuffers;
+        ByteBuffer[] outputBuffers;
+
+        ByteBuffer sourceBuffer;
+        ByteBuffer targetBuffer;
+
+//        MediaFormat outputFormat = mediaCodec.getOutputFormat();
+        MediaFormat outputFormat;
+
+        MediaCodec.BufferInfo bufferInfo;
+
+//        byteNumber =
+//                (outputFormat.containsKey("bit-width") ? outputFormat.getInteger("bit-width") :
+//                        0) / 8;
+        byteNumber = 2;
+
+
+
+        inputBuffers = mediaCodec.getInputBuffers();
+        outputBuffers = mediaCodec.getOutputBuffers();
+
+        mediaExtractor.selectTrack(0);
+
+        bufferInfo = new MediaCodec.BufferInfo();
+
+        BufferedOutputStream bufferedOutputStream = FileFunction
+                .GetBufferedOutputStreamFromFile(decodeFileUrl);
+
+        while (!decodeOutputEnd) {
+            if (decodeInputEnd) {
+                break;
+            }
+
+//            decodeTime = System.currentTimeMillis();
+
+//            if (decodeTime - decodeNoticeTime > Constant.OneSecond) {
+//                final int decodeProgress =
+//                        (int) ((presentationTimeUs - startMicroseconds) * Constant
+//                                .NormalMaxProgress /
+//                                endMicroseconds);
+//
+//                if (decodeProgress > 0) {
+//                    handler.post(new Runnable() {
+//                        @Override
+//                        public void run() {
+//                            decodeOperateInterface.updateDecodeProgress(decodeProgress);
+//                        }
+//                    });
+//                }
+//
+//                decodeNoticeTime = decodeTime;
+//            }
+
+            try {
+                inputBufferIndex = mediaCodec.dequeueInputBuffer(timeOutUs);
+
+                if (inputBufferIndex >= 0) {
+                    sourceBuffer = inputBuffers[inputBufferIndex];
+
+                    sampleDataSize = mediaExtractor.readSampleData(sourceBuffer, 0);
+
+                    if (sampleDataSize < 0) {
+                        decodeInputEnd = true;
+                        sampleDataSize = 0;
+                    } else {
+                        presentationTimeUs = mediaExtractor.getSampleTime();
+                    }
+
+                    mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleDataSize,
+                            presentationTimeUs,
+                            decodeInputEnd ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!decodeInputEnd) {
+                        mediaExtractor.advance();
+                    }
+                } else {
+                    LogFunction.error("inputBufferIndex", "" + inputBufferIndex);
+                }
+
+                // decode to PCM and push it to the AudioTrack player
+                outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, timeOutUs);
+
+                if (outputBufferIndex < 0) {
+                    switch (outputBufferIndex) {
+                        case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
+                            outputBuffers = mediaCodec.getOutputBuffers();
+                            LogFunction.error("MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED",
+                                    "[AudioDecoder]output buffers have changed.");
+                            break;
+                        case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
+                            outputFormat = mediaCodec.getOutputFormat();
+
+                            sampleRate = outputFormat.containsKey(MediaFormat.KEY_SAMPLE_RATE) ?
+                                    outputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE) :
+                                    sampleRate;
+                            channelCount = outputFormat.containsKey(MediaFormat.KEY_CHANNEL_COUNT) ?
+                                    outputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) :
+                                    channelCount;
+                            byteNumber = (outputFormat.containsKey("bit-width") ? outputFormat
+                                    .getInteger
+                                            ("bit-width") : 16) / 8;
+
+                            LogFunction.error("MediaCodec.INFO_OUTPUT_FORMAT_CHANGED",
+                                    "[AudioDecoder]output format has changed to " +
+                                            mediaCodec.getOutputFormat());
+                            break;
+                        default:
+                            LogFunction.error("error",
+                                    "[AudioDecoder] dequeueOutputBuffer returned " +
+                                            outputBufferIndex);
+                            break;
+                    }
+                    continue;
+                }
+
+                targetBuffer = outputBuffers[outputBufferIndex];
+
+                byte[] sourceByteArray = new byte[bufferInfo.size];
+
+                targetBuffer.get(sourceByteArray);
+                targetBuffer.clear();
+
+                mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
+
+                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    decodeOutputEnd = true;
+                }
+
+                if (sourceByteArray.length > 0 && bufferedOutputStream != null) {
+                    if (presentationTimeUs < startMicroseconds) {
+                        continue;
+                    }
+
+//                    byte[] convertByteNumberByteArray = ConvertByteNumber(byteNumber, Constant
+//                                    .RecordByteNumber,
+//                            sourceByteArray);
+                        byte[] convertByteNumberByteArray = ConvertByteNumber(byteNumber, desireBitwidth,
+                            sourceByteArray);
+
+//                    byte[] resultByteArray =
+//                            ConvertChannelNumber(channelCount, Constant.RecordChannelNumber,
+//                                    Constant.RecordByteNumber,
+//                                    convertByteNumberByteArray);
+                    byte[] resultByteArray =
+                            ConvertChannelNumber(channelCount, desireChannel,
+                                    desireBitwidth,
+                                    convertByteNumberByteArray);
+                    Log.d("tetetetet","finished");
+                    try {
+                        bufferedOutputStream.write(resultByteArray);
+                    } catch (Exception e) {
+                        LogFunction.error("输出解压音频数据异常", e);
+//                        CustomBugReporter.reportError(DecodeEngine.class.getName(), "output decoded audio data exception", e);
+                    }
+                }
+
+//                if (presentationTimeUs > endMicroseconds) {
+//                    break;
+//                }
+            } catch (Exception e) {
+                LogFunction.error("getDecodeData异常", e);
+//                CustomBugReporter.reportError(TAG, "getDecodedData exception", e);
+            }
+        }
+
+        if (bufferedOutputStream != null) {
+            try {
+                bufferedOutputStream.close();
+            } catch (IOException e) {
+                LogFunction.error("关闭bufferedOutputStream异常", e);
+//                CustomBugReporter.reportError(TAG, "close bufferedOutputStream exception 1",e);
+            }
+        }
+
+        if (sampleRate != desireSampleRate) {
+            Log.d("tetetetet1","finished");
+            Resample(sampleRate, desireSampleRate, byteNumber, desireBitwidth,decodeFileUrl);
+//            Resample(sampleRate, desireSampleRate, desireChannel, decodeFileUrl);
+            Log.d("tetetetet2","finished");
+        }
+
+        if (mediaCodec != null) {
+            mediaCodec.stop();
+            mediaCodec.release();
+        }
+
+        if (mediaExtractor != null) {
+            mediaExtractor.release();
+        }
+    }
+
+    private static void Resample(int srcSampleRate, int desSampleRate,int srcByteNumber, int desByteNumber,String decodeFileUrl) {
+        String newDecodeFileUrl = decodeFileUrl + "new";
+
+        try {
+            FileInputStream fileInputStream =
+                    new FileInputStream(new File(decodeFileUrl));
+            FileOutputStream fileOutputStream =
+                    new FileOutputStream(new File(newDecodeFileUrl));
+
+            new SSRC(fileInputStream, fileOutputStream, srcSampleRate, desSampleRate,
+                    srcByteNumber, desByteNumber, 1, Integer.MAX_VALUE,
+                    0, 0, true);
+            fileInputStream.close();
+            fileOutputStream.close();
+
+            FileFunction.RenameFile(newDecodeFileUrl, decodeFileUrl);
+        } catch (IOException e) {
+            LogFunction.error("关闭bufferedOutputStream异常", e);
+//            CustomBugReporter.reportError(TAG, "close bufferedOoutputStream exception 2", e);
+        }
+    }
+
+
+
+    private static byte[] ConvertByteNumber(int sourceByteNumber, int outputByteNumber, byte[]
+            sourceByteArray) {
+        Log.d("audio_info","sourceByteNumber:" + sourceByteNumber + "   outputByteNumber:" + outputByteNumber);
+        if (sourceByteNumber == outputByteNumber) {
+            return sourceByteArray;
+        }
+
+        int sourceByteArrayLength = sourceByteArray.length;
+
+        byte[] byteArray;
+
+        switch (sourceByteNumber) {
+            case 1:
+                switch (outputByteNumber) {
+                    case 2:
+                        byteArray = new byte[sourceByteArrayLength * 2];
+
+                        byte resultByte[];
+
+                        for (int index = 0; index < sourceByteArrayLength; index += 1) {
+                            resultByte = CommonFunction.GetBytes((short) (sourceByteArray[index]
+                                    * 256), Variable
+                                    .isBigEnding);
+
+                            byteArray[2 * index] = resultByte[0];
+                            byteArray[2 * index + 1] = resultByte[1];
+                        }
+
+                        return byteArray;
+                }
+                break;
+            case 2:
+                switch (outputByteNumber) {
+                    case 1:
+                        int outputByteArrayLength = sourceByteArrayLength / 2;
+
+                        byteArray = new byte[outputByteArrayLength];
+
+                        for (int index = 0; index < outputByteArrayLength; index += 1) {
+                            byteArray[index] = (byte) (CommonFunction.GetShort(sourceByteArray[2
+                                            * index],
+                                    sourceByteArray[2 * index + 1], Variable.isBigEnding) / 256);
+                        }
+
+                        return byteArray;
+                }
+                break;
+        }
+
+        return sourceByteArray;
+    }
+
+    public static byte[] ConvertChannelNumber(int sourceChannelCount, int outputChannelCount,
+                                               int byteNumber,
+                                               byte[] sourceByteArray) {
+        Log.d("audio_info","srcChannel:" + sourceChannelCount + "   desChannel:" + outputChannelCount);
+        if (sourceChannelCount == outputChannelCount) {
+            return sourceByteArray;
+        }
+
+        switch (byteNumber) {
+            case 1:
+            case 2:
+                break;
+            default:
+                return sourceByteArray;
+        }
+
+        int sourceByteArrayLength = sourceByteArray.length;
+
+        byte[] byteArray;
+
+        switch (sourceChannelCount) {
+            case 1:
+                switch (outputChannelCount) {
+                    case 2:
+                        byteArray = new byte[sourceByteArrayLength * 2];
+
+                        byte firstByte;
+                        byte secondByte;
+
+                        switch (byteNumber) {
+                            case 1:
+                                for (int index = 0; index < sourceByteArrayLength; index += 1) {
+                                    firstByte = sourceByteArray[index];
+
+                                    byteArray[2 * index] = firstByte;
+                                    byteArray[2 * index + 1] = firstByte;
+                                }
+                                break;
+                            case 2:
+                                for (int index = 0; index < sourceByteArrayLength; index += 2) {
+                                    firstByte = sourceByteArray[index];
+                                    secondByte = sourceByteArray[index + 1];
+
+                                    byteArray[2 * index] = firstByte;
+                                    byteArray[2 * index + 1] = secondByte;
+                                    byteArray[2 * index + 2] = firstByte;
+                                    byteArray[2 * index + 3] = secondByte;
+                                }
+                                break;
+                        }
+
+                        return byteArray;
+                }
+                break;
+            case 2:
+                switch (outputChannelCount) {
+                    case 1:
+                        int outputByteArrayLength = sourceByteArrayLength / 2;
+
+                        byteArray = new byte[outputByteArrayLength];
+
+                        switch (byteNumber) {
+                            case 1:
+                                for (int index = 0; index < outputByteArrayLength; index += 2) {
+                                    short averageNumber =
+                                            (short) ((short) sourceByteArray[2 * index] + (short)
+                                                    sourceByteArray[2 *
+                                                            index + 1]);
+                                    byteArray[index] = (byte) (averageNumber >> 1);
+                                }
+                                break;
+                            case 2:
+                                for (int index = 0; index < outputByteArrayLength; index += 2) {
+                                    byte resultByte[] = CommonFunction.AverageShortByteArray
+                                            (sourceByteArray[2 * index],
+                                                    sourceByteArray[2 * index + 1],
+                                                    sourceByteArray[2 *
+                                                            index + 2],
+                                                    sourceByteArray[2 * index + 3], Variable
+                                                            .isBigEnding);
+
+                                    byteArray[index] = resultByte[0];
+                                    byteArray[index + 1] = resultByte[1];
+                                }
+                                break;
+                        }
+
+                        return byteArray;
+                }
+                break;
+        }
+
+        return sourceByteArray;
+    }
+}

+ 12 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/DecodeOperateInterface.java

@@ -0,0 +1,12 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+/**
+ * Created by 郑童宇 on 2016/05/10.
+ */
+public interface DecodeOperateInterface {
+    void updateDecodeProgress(int decodeProgress);
+
+    void decodeSuccess();
+
+    void decodeFail();
+}

+ 202 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/FileFunction.java

@@ -0,0 +1,202 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+import android.app.Application;
+import android.os.Environment;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * Created by zhengtongyu on 16/5/23.
+ */
+@SuppressWarnings("ResultOfMethodCallIgnored")
+public class FileFunction {
+    public static boolean IsExitsSdcard() {
+        return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
+    }
+
+    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+    public static boolean IsFileExists(String path) {
+        if (CommonFunction.isEmpty(path)) {
+            return false;
+        }
+
+        return new File(path).exists();
+    }
+
+    private static void CreateDirectory(String path) {
+        File dir = new File(path);
+
+        if (!dir.exists()) {
+            dir.mkdirs();
+        }
+    }
+
+    public static void InitStorage(Application application) {
+        if (!FileFunction.IsExitsSdcard()) {
+            Variable.StorageDirectoryPath = application.getFilesDir().getAbsolutePath();
+        } else {
+            Variable.StorageDirectoryPath =
+                    Environment.getExternalStorageDirectory().getAbsolutePath() + "/ComposeAudio/";
+        }
+
+        Variable.ErrorFilePath = Variable.StorageDirectoryPath + "error.txt";
+
+        CreateDirectory(Variable.StorageDirectoryPath);
+    }
+
+    public static void SaveFile(String url, String content) {
+        SaveFile(url, content, true, false);
+    }
+
+    public static void SaveFile(String url, String content, boolean cover, boolean append) {
+        FileOutputStream out = null;
+        File file = new File(url);
+
+        try {
+            if (file.exists()) {
+                if (cover) {
+                    file.delete();
+                    file.createNewFile();
+                }
+            } else {
+                file.createNewFile();
+            }
+
+            out = new FileOutputStream(file, append);
+            out.write(content.getBytes());
+            out.close();
+            LogFunction.log("保存文件" + url, "保存文件成功");
+        } catch (Exception e) {
+            LogFunction.error("保存文件" + url, e);
+
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e1) {
+                    e1.printStackTrace();
+                }
+            }
+        }
+    }
+
+    public static void DeleteFile(String path) {
+        if (CommonFunction.notEmpty(path)) {
+            File file = new File(path);
+
+            if (file.exists()) {
+                try {
+                    file.delete();
+                } catch (Exception e) {
+                    LogFunction.error("删除本地文件失败", e);
+                }
+            }
+        }
+    }
+
+    public static void CopyFile(String oldPath, String newPath) {
+        try {
+            int byteRead;
+
+            File oldFile = new File(oldPath);
+            File newFile = new File(newPath);
+
+            if (oldFile.exists()) { //文件存在时
+                if (newFile.exists()) {
+                    newFile.delete();
+                }
+
+                newFile.createNewFile();
+
+                FileInputStream inputStream = new FileInputStream(oldPath); //读入原文件
+                FileOutputStream outputStream = new FileOutputStream(newPath);
+                byte[] buffer = new byte[1024];
+
+                while ((byteRead = inputStream.read(buffer)) != -1) {
+                    outputStream.write(buffer, 0, byteRead);
+                }
+
+                inputStream.close();
+            }
+        } catch (Exception e) {
+            LogFunction.error("复制单个文件操作出错", e);
+        }
+    }
+
+    public static FileInputStream GetFileInputStreamFromFile(String fileUrl) {
+        FileInputStream fileInputStream = null;
+
+        try {
+            File file = new File(fileUrl);
+
+            fileInputStream = new FileInputStream(file);
+        } catch (Exception e) {
+            LogFunction.error("GetBufferedInputStreamFromFile异常", e);
+        }
+
+        return fileInputStream;
+    }
+
+    public static FileOutputStream GetFileOutputStreamFromFile(String fileUrl) {
+        FileOutputStream bufferedOutputStream = null;
+
+        try {
+            File file = new File(fileUrl);
+
+            if (file.exists()) {
+                file.delete();
+            }
+
+            file.createNewFile();
+
+            bufferedOutputStream = new FileOutputStream(file);
+        } catch (Exception e) {
+            LogFunction.error("GetFileOutputStreamFromFile异常", e);
+        }
+
+        return bufferedOutputStream;
+    }
+
+    public static BufferedOutputStream GetBufferedOutputStreamFromFile(String fileUrl) {
+        BufferedOutputStream bufferedOutputStream = null;
+
+        try {
+            File file = new File(fileUrl);
+
+            if (file.exists()) {
+                file.delete();
+            }
+
+            file.createNewFile();
+
+            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file));
+        } catch (Exception e) {
+            LogFunction.error("GetBufferedOutputStreamFromFile异常", e);
+        }
+
+        return bufferedOutputStream;
+    }
+
+    public static void RenameFile(String oldPath, String newPath) {
+        if (CommonFunction.notEmpty(oldPath) && CommonFunction.notEmpty(newPath)) {
+            File newFile = new File(newPath);
+
+            if (newFile.exists()) {
+                newFile.delete();
+            }
+
+            File oldFile = new File(oldPath);
+
+            if (oldFile.exists()) {
+                try {
+                    oldFile.renameTo(new File(newPath));
+                } catch (Exception e) {
+                    LogFunction.error("删除本地文件失败", e);
+                }
+            }
+        }
+    }
+}

+ 127 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/LogFunction.java

@@ -0,0 +1,127 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * Created by zhengtongyu on 16/5/23.
+ */
+@SuppressWarnings("ResultOfMethodCallIgnored")
+public class LogFunction {
+    private static final String tag = "AppLog";
+
+    private static BufferedWriter errorOutputStream;
+
+    public static synchronized void UpdateErrorOutputStream() {
+        try {
+            Log.d("刷新error文件输出流", "刷新开始");
+
+            File file = new File(Variable.ErrorFilePath);
+
+            if (!file.exists()) {
+                file.createNewFile();
+            }
+
+            errorOutputStream = new BufferedWriter(
+                    new OutputStreamWriter(new FileOutputStream(Variable.ErrorFilePath, true)));
+        } catch (Exception e) {
+            Log.e("刷新error日志文件输出流出错", e.toString());
+        }
+    }
+
+    public static synchronized void FinishErrorOutputStream() {
+        try {
+            errorOutputStream.close();
+        } catch (Exception e) {
+            Log.e("关闭error文件出错", e.toString());
+        }
+    }
+
+    public static String getStackTrace(Exception e) {
+        StringWriter writer = new StringWriter();
+        e.printStackTrace(new PrintWriter(writer));
+        return writer.toString();
+    }
+
+    private static String getStackInformation(String content) {
+        StringBuilder buffer = new StringBuilder(content == null ? "" : content);
+        String className;
+        String methodName;
+        int lineNumber;
+
+        try {
+            final int beginStackIndex = 2;
+            final int outputStackLength = 2;
+            int endStackIndex = beginStackIndex + outputStackLength;
+            StackTraceElement[] element = new Throwable().getStackTrace();
+
+            int totalStackLength = element.length;
+
+            if (endStackIndex > totalStackLength) {
+                endStackIndex = totalStackLength;
+            }
+
+            for (int index = beginStackIndex; index < endStackIndex; index++) {
+                className = element[index].getFileName();
+                methodName = element[index].getMethodName();
+                lineNumber = element[index].getLineNumber();
+                buffer.append("\n").append(" ").append(methodName).append("(").append(className)
+                        .append(":").append(lineNumber).append(")");
+            }
+        } catch (Exception e) {
+            Log.e(tag + ":获取stack调用信息异常", e.toString());
+        }
+
+        return buffer.toString();
+    }
+
+    /*
+     * 打印日志数据
+	 */
+    public static void log(String title, String content) {
+    }
+
+    /*
+     * 打印错误日志数据,同时将数据写到外文件
+     */
+    public static void error(String title, String content) {
+
+        try {
+            if (errorOutputStream == null || !FileFunction.IsFileExists(Variable.ErrorFilePath)) {
+                UpdateErrorOutputStream();
+            }
+
+            errorOutputStream
+                    .write(CommonFunction.GetDate() + "   " + title + ":" + content + "\r\n");
+            errorOutputStream.flush();
+        } catch (Exception e) {
+            Log.e(tag + ":打印error数据异常", e.toString());
+        }
+    }
+
+    /*
+     * 为不使getStackInformation少输出一层,故而放弃使用error(title,e.toString()));
+     */
+    public static void error(String title, Exception exception) {
+
+        try {
+            if (errorOutputStream == null || !FileFunction.IsFileExists(Variable.ErrorFilePath)) {
+                UpdateErrorOutputStream();
+            }
+
+            errorOutputStream
+                    .write(CommonFunction.GetDate() + "   " + title + ":" + exception.toString() +
+                            "\r\n");
+            errorOutputStream.flush();
+        } catch (Exception e) {
+            Log.e(tag + ":打印error数据异常", e.toString());
+        }
+    }
+}

+ 8 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/Variable.java

@@ -0,0 +1,8 @@
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer;
+
+class Variable {
+    public static final boolean isBigEnding = false;
+
+    public static String StorageDirectoryPath;
+    public static String ErrorFilePath;
+}

+ 2562 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/sound/pcm/resampling/ssrc/SSRC.java

@@ -0,0 +1,2562 @@
+/*
+ * This program(except FFT and Bessel function part) is distributed under
+ * LGPL. See LGPL.txt for details. But, if you make a new program with derived
+ * code from this program,I strongly wish that my name and derived code are
+ * indicated explicitly.
+ */
+
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.sound.pcm.resampling.ssrc;
+
+
+import android.annotation.SuppressLint;
+
+import com.i2edu.dubbing_lib.audioUtils.audiotransfer.LogFunction;
+import com.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.util.I0Bessel;
+import com.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.util.SplitRadixFft;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.util.Random;
+
+
+/**
+ * Shibatch Sampling Rate Converter.
+ *
+ * @author <a href="shibatch@users.sourceforge.net">Naoki Shibata</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @author Maksim Khadkevich (a couple of minor changes)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+@SuppressWarnings({"SuspiciousNameCombination", "ConstantConditions", "UnnecessaryLocalVariable", "RedundantStringFormatCall", "UnusedAssignment", "StatementWithEmptyBody", "PointlessArithmeticExpression", "DanglingJavadoc", "JavaDoc", "ResultOfMethodCallIgnored", "PointlessBitwiseExpression"})
+public class SSRC {
+
+    /** */
+    private final ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;
+
+    /** */
+    private final SplitRadixFft fft = new SplitRadixFft();
+
+    /** */
+    private static final String VERSION = "1.30";
+
+    /** */
+    private double AA = 170;
+
+    /** */
+    private double DF = 100;
+
+    /** */
+    private int FFTFIRLEN = 65536;
+
+    /** */
+    //  private static final int M = 15;
+
+    /** */
+    private static final int RANDBUFLEN = 65536;
+
+    /** */
+    private int RINT(double x) {
+        return ((x) >= 0 ? ((int) ((x) + 0.5)) : ((int) ((x) - 0.5)));
+    }
+
+    /** */
+    private static final int scoeffreq[] = {0, 48000, 44100, 37800, 32000, 22050, 48000, 44100};
+
+    /** */
+    private static final int scoeflen[] = {1, 16, 20, 16, 16, 15, 16, 15};
+
+    /** */
+    private static final int samp[] = {8, 18, 27, 8, 8, 8, 10, 9};
+
+    /** */
+    private static final double[][] shapercoefs = {{-1}, // triangular dither
+
+            {-2.8720729351043701172, 5.0413231849670410156, -6.2442994117736816406,
+                    5.8483986854553222656, -3.7067542076110839844, 1.0495119094848632812,
+                    1.1830236911773681641, -2.1126792430877685547, 1.9094531536102294922,
+                    -0.99913084506988525391, 0.17090806365013122559, 0.32615602016448974609,
+                    -0.39127644896507263184, 0.26876461505889892578, -0.097676105797290802002,
+                    0.023473845794796943665,}, // 48k, N=16, amp=18
+
+            {-2.6773197650909423828, 4.8308925628662109375, -6.570110321044921875,
+                    7.4572014808654785156, -6.7263274192810058594, 4.8481650352478027344,
+                    -2.0412089824676513672, -0.7006359100341796875, 2.9537565708160400391,
+                    -4.0800385475158691406, 4.1845216751098632812, -3.3311812877655029297,
+                    2.1179926395416259766, -0.879302978515625, 0.031759146600961685181,
+                    0.42382788658142089844, -0.47882103919982910156, 0.35490813851356506348,
+                    -0.17496839165687561035, 0.060908168554306030273,}, // 44.1k, N=20, amp=27
+
+            {-1.6335992813110351562, 2.2615492343902587891, -2.4077029228210449219,
+                    2.6341717243194580078, -2.1440362930297851562, 1.8153258562088012695,
+                    -1.0816224813461303711, 0.70302653312683105469, -0.15991993248462677002,
+                    -0.041549518704414367676, 0.29416576027870178223, -0.2518316805362701416,
+                    0.27766478061676025391, -0.15785403549671173096, 0.10165894031524658203,
+                    -0.016833892092108726501,}, // 37.8k, N=16
+
+            {-0.82901298999786376953, 0.98922657966613769531, -0.59825712442398071289,
+                    1.0028809309005737305, -0.59938216209411621094, 0.79502451419830322266,
+                    -0.42723315954208374023, 0.54492527246475219727, -0.30792605876922607422,
+                    0.36871799826622009277, -0.18792048096656799316, 0.2261127084493637085,
+                    -0.10573341697454452515, 0.11435490846633911133, -0.038800679147243499756,
+                    0.040842197835445404053,}, // 32k, N=16
+
+            {-0.065229974687099456787, 0.54981261491775512695, 0.40278548002243041992,
+                    0.31783768534660339355, 0.28201797604560852051, 0.16985194385051727295,
+                    0.15433363616466522217, 0.12507140636444091797, 0.08903945237398147583,
+                    0.064410120248794555664, 0.047146003693342208862, 0.032805237919092178345,
+                    0.028495194390416145325, 0.011695005930960178375, 0.011831838637590408325,},
+            // 22.05k, N=15
+
+            {-2.3925774097442626953, 3.4350297451019287109, -3.1853709220886230469,
+                    1.8117271661758422852, 0.20124770700931549072, -1.4759907722473144531,
+                    1.7210904359817504883, -0.97746700048446655273, 0.13790138065814971924,
+                    0.38185903429985046387, -0.27421241998672485352, -0.066584214568138122559,
+                    0.35223302245140075684, -0.37672343850135803223, 0.23964276909828186035,
+                    -0.068674825131893157959,}, // 48k, N=16, amp=10
+
+            {-2.0833916664123535156, 3.0418450832366943359, -3.2047898769378662109,
+                    2.7571926116943359375, -1.4978630542755126953, 0.3427594602108001709,
+                    0.71733748912811279297, -1.0737057924270629883, 1.0225815773010253906,
+                    -0.56649994850158691406, 0.20968692004680633545, 0.065378531813621520996,
+                    -0.10322438180446624756, 0.067442022264003753662, 0.00495197344571352005,},
+            // 44.1k, N=15, amp=9
+    };
+
+    /** */
+    private double[][] shapebuf;
+
+    /** */
+    private int shaper_type, shaper_len, shaper_clipmin, shaper_clipmax;
+
+    /** */
+    private double[] randbuf;
+
+    /** */
+    private int randptr;
+
+    /** */
+    private boolean quiet = false;
+
+    /** */
+    private int lastshowed2;
+
+    /** */
+    private long starttime, lastshowed;
+
+    /** */
+    private static final int POOLSIZE = 97;
+
+    /** */
+    public int init_shaper(int freq, int nch, int min, int max, int dtype, int pdf,
+                           double noiseamp) {
+        int i;
+        int[] pool = new int[POOLSIZE];
+
+        for (i = 1; i < 6; i++) {
+            if (freq == scoeffreq[i]) {
+                break;
+            }
+        }
+        if ((dtype == 3 || dtype == 4) && i == 6) {
+            System.err
+                    .printf("Warning: ATH based noise shaping for destination frequency %dHz is not available, using triangular dither\n",
+                            freq);
+        }
+        if (dtype == 2 || i == 6) {
+            i = 0;
+        }
+        if (dtype == 4 && (i == 1 || i == 2)) {
+            i += 5;
+        }
+
+        shaper_type = i;
+
+        shapebuf = new double[nch][];
+        shaper_len = scoeflen[shaper_type];
+
+        for (i = 0; i < nch; i++) {
+            shapebuf[i] = new double[shaper_len];
+        }
+
+        shaper_clipmin = min;
+        shaper_clipmax = max;
+
+        randbuf = new double[RANDBUFLEN];
+
+        Random random = new Random(System.currentTimeMillis());
+        for (i = 0; i < POOLSIZE; i++) {
+            pool[i] = random.nextInt();
+        }
+
+        switch (pdf) {
+            case 0: // rectangular
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    int r, p;
+
+                    p = random.nextInt() % POOLSIZE;
+                    r = pool[p];
+                    pool[p] = random.nextInt();
+                    randbuf[i] = noiseamp * (((double) r) / Integer.MAX_VALUE - 0.5);
+                }
+                break;
+
+            case 1: // triangular
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    int r1, r2, p;
+
+                    p = random.nextInt() % POOLSIZE;
+                    r1 = pool[p];
+                    pool[p] = random.nextInt();
+                    p = random.nextInt() % POOLSIZE;
+                    r2 = pool[p];
+                    pool[p] = random.nextInt();
+                    randbuf[i] = noiseamp * ((((double) r1) / Integer.MAX_VALUE) -
+                            (((double) r2) / Integer.MAX_VALUE));
+                }
+                break;
+
+            case 2: // gaussian
+            {
+                int sw = 0;
+                double t = 0, u = 0;
+
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    double r;
+                    int p;
+
+                    if (sw == 0) {
+                        sw = 1;
+
+                        p = random.nextInt() % POOLSIZE;
+                        r = ((double) pool[p]) / Integer.MAX_VALUE;
+                        pool[p] = random.nextInt();
+                        if (r == 1.0) {
+                            r = 0.0;
+                        }
+
+                        t = Math.sqrt(-2 * Math.log(1 - r));
+
+                        p = random.nextInt() % POOLSIZE;
+                        r = ((double) pool[p]) / Integer.MAX_VALUE;
+                        pool[p] = random.nextInt();
+
+                        u = 2 * Math.PI * r;
+
+                        randbuf[i] = noiseamp * t * Math.cos(u);
+                    } else {
+                        sw = 0;
+
+                        randbuf[i] = noiseamp * t * Math.sin(u);
+                    }
+                }
+            }
+            break;
+        }
+
+        randptr = 0;
+
+        if (dtype == 0 || dtype == 1) {
+            return 1;
+        }
+        return samp[shaper_type];
+    }
+
+    /** */
+    public int do_shaping(double s, double[] peak, int dtype, int ch) {
+        double u, h;
+        int i;
+
+        if (dtype == 1) {
+            s += randbuf[randptr++ & (RANDBUFLEN - 1)];
+
+            if (s < shaper_clipmin) {
+                double d = s / shaper_clipmin;
+                peak[0] = peak[0] < d ? d : peak[0];
+                s = shaper_clipmin;
+            }
+            if (s > shaper_clipmax) {
+                double d = s / shaper_clipmax;
+                peak[0] = peak[0] < d ? d : peak[0];
+                s = shaper_clipmax;
+            }
+
+            return RINT(s);
+        }
+
+        h = 0;
+        for (i = 0; i < shaper_len; i++) {
+            h += shapercoefs[shaper_type][i] * shapebuf[ch][i];
+        }
+        s += h;
+        u = s;
+        s += randbuf[randptr++ & (RANDBUFLEN - 1)];
+
+        for (i = shaper_len - 2; i >= 0; i--) {
+            shapebuf[ch][i + 1] = shapebuf[ch][i];
+        }
+
+        if (s < shaper_clipmin) {
+            double d = s / shaper_clipmin;
+            peak[0] = peak[0] < d ? d : peak[0];
+            s = shaper_clipmin;
+            shapebuf[ch][0] = s - u;
+
+            if (shapebuf[ch][0] > 1) {
+                shapebuf[ch][0] = 1;
+            }
+            if (shapebuf[ch][0] < -1) {
+                shapebuf[ch][0] = -1;
+            }
+        } else if (s > shaper_clipmax) {
+            double d = s / shaper_clipmax;
+            peak[0] = peak[0] < d ? d : peak[0];
+            s = shaper_clipmax;
+            shapebuf[ch][0] = s - u;
+
+            if (shapebuf[ch][0] > 1) {
+                shapebuf[ch][0] = 1;
+            }
+            if (shapebuf[ch][0] < -1) {
+                shapebuf[ch][0] = -1;
+            }
+        } else {
+            s = RINT(s);
+            shapebuf[ch][0] = s - u;
+        }
+
+        return (int) s;
+    }
+
+    /** */
+    private void quit_shaper(int nch) {
+    }
+
+    /** */
+    private double alpha(double a) {
+        if (a <= 21) {
+            return 0;
+        }
+        if (a <= 50) {
+            return 0.5842 * Math.pow(a - 21, 0.4) + 0.07886 * (a - 21);
+        }
+        return 0.1102 * (a - 8.7);
+    }
+
+    /** */
+    private double win(double n, int len, double alp, double iza) {
+        return I0Bessel
+                .value(alp * Math.sqrt(1 - 4 * n * n / (((double) len - 1) * ((double) len - 1)))) /
+                iza;
+    }
+
+    /** */
+    private double sinc(double x) {
+        return x == 0 ? 1 : Math.sin(x) / x;
+    }
+
+    /** */
+    private double hn_lpf(int n, double lpf, double fs) {
+        double t = 1 / fs;
+        double omega = 2 * Math.PI * lpf;
+        return 2 * lpf * t * sinc(n * omega * t);
+    }
+
+    /** */
+    private void usage() {
+        System.err.printf("http://shibatch.sourceforge.net/\n\n");
+        System.err.printf("usage: ssrc [<options>] <source wav file> <destination wav file>\n");
+        System.err.printf("options : --rate <sampling rate>     output sample rate\n");
+        System.err.printf("          --att <attenuation(dB)>    attenuate signal\n");
+        System.err.printf("          --bits <number of bits>    output quantization bit length\n");
+        System.err.printf("          --tmpfile <file name>      specify temporal file\n");
+        System.err
+                .printf("          --twopass                  two pass processing to avoid clipping\n");
+        System.err.printf("          --normalize                normalize the wave file\n");
+        System.err.printf("          --quiet                    nothing displayed except error\n");
+        System.err.printf("          --dither [<type>]          dithering\n");
+        System.err.printf("                                       0 : no dither\n");
+        System.err.printf("                                       1 : no noise shaping\n");
+        System.err.printf("                                       2 : triangular spectral shape\n");
+        System.err.printf("                                       3 : ATH based noise shaping\n");
+        System.err
+                .printf("                                       4 : less dither amplitude than type 3\n");
+        System.err.printf("          --pdf <type> [<amp>]       select p.d.f. of noise\n");
+        System.err.printf("                                       0 : rectangular\n");
+        System.err.printf("                                       1 : triangular\n");
+        System.err.printf("                                       2 : Gaussian\n");
+        System.err.printf("          --profile <type>           specify profile\n");
+        System.err
+                .printf("                                       standard : the default quality\n");
+        System.err
+                .printf("                                       fast     : fast, not so bad quality\n");
+    }
+
+    /** */
+    private void fmterr(int x) {
+        throw new IllegalStateException("unknown error " + x);
+    }
+
+    /** */
+    private void setstarttime() {
+        starttime = System.currentTimeMillis();
+        lastshowed = 0;
+        lastshowed2 = -1;
+    }
+
+    /** */
+    private void showprogress(double p) {
+        int eta, pc;
+        long t;
+        if (quiet) {
+            return;
+        }
+
+        t = System.currentTimeMillis() - starttime;
+        if (p == 0) {
+            eta = 0;
+        } else {
+            eta = (int) (t * (1 - p) / p);
+        }
+
+        pc = (int) (p * 100);
+
+        if (pc != lastshowed2 || t != lastshowed) {
+            System.err.printf(" %3d%% processed", pc);
+            lastshowed2 = pc;
+        }
+        if (t != lastshowed) {
+            System.err.printf(", ETA =%4dmsec", eta);
+            lastshowed = t;
+        }
+        System.err.printf("\r");
+        System.err.flush();
+    }
+
+    /** */
+    private int gcd(int x, int y) {
+        int t;
+
+        while (y != 0) {
+            t = x % y;
+            x = y;
+            y = t;
+        }
+        return x;
+    }
+
+    /**
+     * @param fpi
+     * @param fpo
+     * @param nch
+     * @param bps
+     * @param dbps     sizeof(double)?
+     * @param sfrq
+     * @param dfrq
+     * @param gain
+     * @param chanklen
+     * @param twopass
+     * @param dither
+     * @return
+     * @throws IOException
+     */
+    @SuppressLint("Assert")
+    public double upsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, int sfrq,
+                           int dfrq, double gain, int chanklen, boolean twopass, int dither)
+            throws IOException {
+        int frqgcd, osf = 0, fs1, fs2;
+        double[][] stage1;
+        double[] stage2;
+        int n1, n1x, n1y, n2, n2b;
+        int filter2len;
+        int[] f1order, f1inc;
+        int[] fft_ip = null;
+        double[] fft_w = null;
+        ByteBuffer rawinbuf, rawoutbuf;
+        double[] inbuf, outbuf;
+        double[][] buf1, buf2;
+        double[] peak = new double[]{0};
+        int spcount = 0;
+        int i, j;
+
+        //        System.err.println("upsample");
+
+        filter2len = FFTFIRLEN; // stage 2 filter length
+
+        // Make stage 1 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            double guard = 2;
+
+            frqgcd = gcd(sfrq, dfrq);
+
+            fs1 = sfrq / frqgcd * dfrq;
+
+            if (fs1 / dfrq == 1) {
+                osf = 1;
+            } else if (fs1 / dfrq % 2 == 0) {
+                osf = 2;
+            } else if (fs1 / dfrq % 3 == 0) {
+                osf = 3;
+            } else {
+                throw new IllegalArgumentException(String.format(
+                        "Resampling from %dHz to %dHz is not supported.\n" +
+                                "%d/gcd(%d,%d)=%d must be divided by 2 or 3.\n", sfrq, dfrq, sfrq,
+                        sfrq, dfrq, fs1 / dfrq));
+            }
+
+            df = (dfrq * osf / 2 - sfrq / 2) * 2 / guard;
+            lpf = sfrq / 2 + (dfrq * osf / 2 - sfrq / 2) / guard;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n1 = (int) (fs1 / df * d + 1);
+            if (n1 % 2 == 0) {
+                n1++;
+            }
+
+            alp = alpha(aa);
+            iza = I0Bessel.value(alp);
+            // System.err.printf("iza = %g\n",iza);
+
+            n1y = fs1 / sfrq;
+            n1x = n1 / n1y + 1;
+
+            f1order = new int[n1y * osf];
+            for (i = 0; i < n1y * osf; i++) {
+                f1order[i] = fs1 / sfrq - (i * (fs1 / (dfrq * osf))) % (fs1 / sfrq);
+                if (f1order[i] == fs1 / sfrq) {
+                    f1order[i] = 0;
+                }
+            }
+
+            f1inc = new int[n1y * osf];
+            for (i = 0; i < n1y * osf; i++) {
+                f1inc[i] = f1order[i] < fs1 / (dfrq * osf) ? nch : 0;
+                if (f1order[i] == fs1 / sfrq) {
+                    f1order[i] = 0;
+                }
+            }
+
+            stage1 = new double[n1y][n1x];
+
+            for (i = -(n1 / 2); i <= n1 / 2; i++) {
+                stage1[(i + n1 / 2) % n1y][(i + n1 / 2) / n1y] =
+                        win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq;
+            }
+        }
+
+        // Make stage 2 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            int ipsize, wsize;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            fs2 = dfrq * osf;
+
+            for (i = 1; ; i = i * 2) {
+                n2 = filter2len * i;
+                if (n2 % 2 == 0) {
+                    n2--;
+                }
+                df = (fs2 * d) / (n2 - 1);
+                lpf = sfrq / 2;
+                if (df < DF) {
+                    break;
+                }
+            }
+
+            alp = alpha(aa);
+
+            iza = I0Bessel.value(alp);
+
+            for (n2b = 1; n2b < n2; n2b *= 2) {
+            }
+            n2b *= 2;
+
+            stage2 = new double[n2b];
+
+            for (i = -(n2 / 2); i <= n2 / 2; i++) {
+                stage2[i + n2 / 2] = win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) / n2b * 2;
+            }
+
+            ipsize = (int) (2 + Math.sqrt(n2b));
+            fft_ip = new int[ipsize];
+            fft_ip[0] = 0;
+            wsize = n2b / 2;
+            fft_w = new double[wsize];
+
+            fft.rdft(n2b, 1, stage2, fft_ip, fft_w);
+        }
+
+        // Apply filters
+
+        setstarttime();
+
+        {
+            int n2b2 = n2b / 2;
+            // inbuffs1Tv???
+            int rp;
+            // disposesfrqTv?
+            int ds;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt1;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt2 = 0;
+            // stage1 filter?oTv?n1y*osf]
+            int s1p;
+            boolean init;
+            boolean ending;
+            int sumread, sumwrite;
+            int osc;
+            int ip, ip_backup;
+            int s1p_backup, osc_backup;
+            int ch, p;
+            int inbuflen;
+            int delay = 0;
+
+            buf1 = new double[nch][n2b2 / osf + 1];
+
+            buf2 = new double[nch][n2b];
+
+            rawinbuf = ByteBuffer.allocate(nch * (n2b2 + n1x) * bps); // ,bps
+            rawoutbuf = ByteBuffer.allocate(nch * (n2b2 / osf + 1) * dbps); // ,dbps
+
+            inbuf = new double[nch * (n2b2 + n1x)];
+            outbuf = new double[nch * (n2b2 / osf + 1)];
+
+            s1p = 0;
+            rp = 0;
+            ds = 0;
+            osc = 0;
+
+            init = true;
+            ending = false;
+            inbuflen = n1 / 2 / (fs1 / sfrq) + 1;
+            delay = (int) ((double) n2 / 2 / (fs2 / dfrq));
+
+            sumread = sumwrite = 0;
+
+            while (true) {
+                int nsmplread, toberead, toberead2;
+
+                toberead2 = toberead =
+                        (int) (Math.floor((double) n2b2 * sfrq / (dfrq * osf)) + 1 + n1x -
+                                inbuflen);
+                if (toberead + sumread > chanklen) {
+                    toberead = chanklen - sumread;
+                }
+
+                rawinbuf.position(0);
+                rawinbuf.limit(Math.min(rawinbuf.limit(), bps * nch * toberead));
+                //                rawinbuf.limit(bps * nch * toberead);
+
+                byte[] tempData = new byte[rawinbuf.limit()];
+                nsmplread = fpi.read(tempData);
+                if (nsmplread < 0) {
+                    nsmplread = 0;
+                }
+
+                if (nsmplread < rawinbuf.limit()) {
+                    chanklen = sumread + nsmplread / bps * nch;
+                }
+
+                rawinbuf.limit(nsmplread);
+                rawinbuf = ByteBuffer.wrap(tempData);
+                rawinbuf.position(nsmplread);
+
+                rawinbuf.flip();
+                nsmplread /= bps * nch;
+
+                switch (bps) {
+                    case 1:
+                        for (i = 0; i < nsmplread * nch; i++)
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7f) * ((double) rawinbuf.get(i) - 128);
+                        break;
+
+                    case 2:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asShortBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v;
+                        }
+                        break;
+
+                    case 3:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7fffff) * ((rawinbuf.get(i * 3) << 0) |
+                                            (rawinbuf.get(i * 3 + 1) << 8) |
+                                            (rawinbuf.get(i * 3 + 2) << 16));
+                        }
+                        break;
+
+                    case 4:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asIntBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v;
+                        }
+                        break;
+                }
+
+                for (; i < nch * toberead2; i++) {
+                    inbuf[nch * inbuflen + i] = 0;
+                }
+
+                inbuflen += toberead2;
+
+                sumread += nsmplread;
+
+                ending = sumread >= chanklen;
+                //                ending = fpi.available() == 0 || sumread >= chanklen;
+
+                //              nsmplwrt1 = ((rp - 1) * sfrq / fs1 + inbuflen - n1x) * dfrq * osf / sfrq;
+                //              if (nsmplwrt1 > n2b2) { nsmplwrt1 = n2b2; }
+                nsmplwrt1 = n2b2;
+
+                // apply stage 1 filter
+
+                ip = ((sfrq * (rp - 1) + fs1) / fs1) * nch; // inbuf
+
+                s1p_backup = s1p;
+                ip_backup = ip;
+                osc_backup = osc;
+
+                for (ch = 0; ch < nch; ch++) {
+                    int op = ch; // outbuf
+                    //                  int fdo = fs1 / (dfrq * osf);
+                    int no = n1y * osf;
+
+                    s1p = s1p_backup;
+                    ip = ip_backup + ch;
+
+                    switch (n1x) {
+                        case 7:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                int s1o = f1order[s1p];
+
+                                buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] +
+                                        stage1[s1o][1] * inbuf[ip + 1 * nch] +
+                                        stage1[s1o][2] * inbuf[ip + 2 * nch] +
+                                        stage1[s1o][3] * inbuf[ip + 3 * nch] +
+                                        stage1[s1o][4] * inbuf[ip + 4 * nch] +
+                                        stage1[s1o][5] * inbuf[ip + 5 * nch] +
+                                        stage1[s1o][6] * inbuf[ip + 6 * nch];
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+
+                        case 9:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                int s1o = f1order[s1p];
+
+                                buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] +
+                                        stage1[s1o][1] * inbuf[ip + 1 * nch] +
+                                        stage1[s1o][2] * inbuf[ip + 2 * nch] +
+                                        stage1[s1o][3] * inbuf[ip + 3 * nch] +
+                                        stage1[s1o][4] * inbuf[ip + 4 * nch] +
+                                        stage1[s1o][5] * inbuf[ip + 5 * nch] +
+                                        stage1[s1o][6] * inbuf[ip + 6 * nch] +
+                                        stage1[s1o][7] * inbuf[ip + 7 * nch] +
+                                        stage1[s1o][8] * inbuf[ip + 8 * nch];
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+
+                        default:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                double tmp = 0;
+                                int ip2 = ip;
+
+                                int s1o = f1order[s1p];
+
+                                for (i = 0; i < n1x; i++) {
+                                    tmp += stage1[s1o][i] * inbuf[ip2];
+                                    ip2 += nch;
+                                }
+                                buf2[ch][p] = tmp;
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+                    }
+
+                    osc = osc_backup;
+
+                    // apply stage 2 filter
+
+                    for (p = nsmplwrt1; p < n2b; p++) {
+                        buf2[ch][p] = 0;
+                    }
+
+                    //for(i=0;i<n2b2;i++) { System.err.printf("%d:%g",i,buf2[ch][i]); }
+
+                    fft.rdft(n2b, 1, buf2[ch], fft_ip, fft_w);
+
+                    buf2[ch][0] = stage2[0] * buf2[ch][0];
+                    buf2[ch][1] = stage2[1] * buf2[ch][1];
+
+                    for (i = 1; i < n2b / 2; i++) {
+                        double re, im;
+
+                        re = stage2[i * 2] * buf2[ch][i * 2] -
+                                stage2[i * 2 + 1] * buf2[ch][i * 2 + 1];
+                        im = stage2[i * 2 + 1] * buf2[ch][i * 2] +
+                                stage2[i * 2] * buf2[ch][i * 2 + 1];
+
+                        //System.err.printf("%d : %g %g %g %g %g %g\n",i,stage2[i*2],stage2[i*2+1],buf2[ch][i*2],buf2[ch][i*2+1],re,im);
+
+                        buf2[ch][i * 2] = re;
+                        buf2[ch][i * 2 + 1] = im;
+                    }
+
+                    fft.rdft(n2b, -1, buf2[ch], fft_ip, fft_w);
+
+                    for (i = osc, j = 0; i < n2b2; i += osf, j++) {
+                        double f = (buf1[ch][j] + buf2[ch][i]);
+                        outbuf[op + j * nch] = f;
+                    }
+
+                    nsmplwrt2 = j;
+
+                    osc = i - n2b2;
+
+                    for (j = 0; i < n2b; i += osf, j++) {
+                        buf1[ch][j] = buf2[ch][i];
+                    }
+                }
+
+                rp += nsmplwrt1 * (sfrq / frqgcd) / osf;
+
+                rawoutbuf.clear();
+                if (twopass) {
+                    for (i = 0; i < nsmplwrt2 * nch; i++) {
+                        double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i];
+                        peak[0] = peak[0] < f ? f : peak[0];
+                        rawoutbuf.asDoubleBuffer().put(i, outbuf[i]);
+                    }
+                } else {
+                    switch (dbps) {
+                        case 1: {
+                            double gain2 = gain * 0x7f;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x80) {
+                                        double d = (double) s / -0x80;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x80;
+                                    }
+                                    if (0x7f < s) {
+                                        double d = (double) s / 0x7f;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7f;
+                                    }
+                                }
+
+                                rawoutbuf.put(i, (byte) (s + 0x80));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 2: {
+                            double gain2 = gain * 0x7fff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x8000) {
+                                        double d = (double) s / -0x8000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x8000;
+                                    }
+                                    if (0x7fff < s) {
+                                        double d = (double) s / 0x7fff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fff;
+                                    }
+                                }
+
+                                rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s);
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 3: {
+                            double gain2 = gain * 0x7fffff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x800000) {
+                                        double d = (double) s / -0x800000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x800000;
+                                    }
+                                    if (0x7fffff < s) {
+                                        double d = (double) s / 0x7fffff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fffff;
+                                    }
+                                }
+
+                                rawoutbuf.put(i * 3, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 1, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 2, (byte) (s & 255));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                    }
+                }
+
+                if (!init) {
+                    if (ending) {
+                        if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) {
+                            rawoutbuf.position(0);
+                            rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2;
+                        } else {
+                            rawoutbuf.position(0);
+                            int limitData = (int) (dbps * nch *
+                                    (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite));
+                            if (limitData > 0) {
+                                rawoutbuf.limit(limitData);
+                                writeBuffers(fpo, rawoutbuf);
+                            }
+                            break;
+                        }
+                    } else {
+                        rawoutbuf.position(0);
+                        rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                        writeBuffers(fpo, rawoutbuf);
+                        sumwrite += nsmplwrt2;
+                    }
+                } else {
+
+                    if (nsmplwrt2 < delay) {
+                        delay -= nsmplwrt2;
+                    } else {
+                        if (ending) {
+                            if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit(dbps * nch * (nsmplwrt2 - delay));
+                                writeBuffers(fpo, rawoutbuf);
+                                sumwrite += nsmplwrt2 - delay;
+                            } else {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit((int) (dbps * nch *
+                                        (Math.floor((double) sumread * dfrq / sfrq) + 2 + sumwrite +
+                                                nsmplwrt2 - delay)));
+                                writeBuffers(fpo, rawoutbuf);
+                                break;
+                            }
+                        } else {
+                            rawoutbuf.position(dbps * nch * delay);
+                            rawoutbuf.limit(dbps * nch * (nsmplwrt2));
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2 - delay;
+                            init = false;
+                        }
+                    }
+                }
+
+                {
+                    ds = (rp - 1) / (fs1 / sfrq);
+
+                    assert (inbuflen >= ds);
+
+                    System.arraycopy(inbuf, nch * ds, inbuf, 0,
+                            nch * (inbuflen - ds)); // memmove TODO overlap
+                    inbuflen -= ds;
+                    rp -= ds * (fs1 / sfrq);
+                }
+
+                if ((spcount++ & 7) == 7) {
+                    showprogress((double) sumread / chanklen);
+                }
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    @SuppressLint("Assert")
+    public double downsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps,
+                             int sfrq, int dfrq, double gain, int chanklen, boolean twopass,
+                             int dither) throws IOException {
+        int frqgcd, osf = 0, fs1, fs2;
+        double[] stage1;
+        double[][] stage2;
+        int n2, n2x, n2y, n1, n1b;
+        int filter1len;
+        int[] f2order, f2inc;
+        int[] fft_ip = null;
+        double[] fft_w = null;
+        ByteBuffer rawinbuf, rawoutbuf;
+        double[] inbuf, outbuf;
+        double[][] buf1, buf2;
+        int i, j;
+        int spcount = 0;
+        double[] peak = new double[]{0};
+
+        //        System.err.println("downsample");
+
+        filter1len = FFTFIRLEN; // stage 1 filter length
+
+        // Make stage 1 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            int ipsize, wsize;
+
+            frqgcd = gcd(sfrq, dfrq);
+
+            if (dfrq / frqgcd == 1) {
+                osf = 1;
+            } else if (dfrq / frqgcd % 2 == 0) {
+                osf = 2;
+            } else if (dfrq / frqgcd % 3 == 0) {
+                osf = 3;
+            } else {
+                throw new IllegalArgumentException(String.format(
+                        "Resampling from %dHz to %dHz is not supported.\n" +
+                                "%d/gcd(%d,%d)=%d must be divided by 2 or 3.", sfrq, dfrq, dfrq,
+                        sfrq, dfrq, dfrq / frqgcd));
+            }
+
+            fs1 = sfrq * osf;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n1 = filter1len;
+            for (i = 1; ; i = i * 2) {
+                n1 = filter1len * i;
+                if (n1 % 2 == 0) {
+                    n1--;
+                }
+                df = (fs1 * d) / (n1 - 1);
+                lpf = (dfrq - df) / 2;
+                if (df < DF) {
+                    break;
+                }
+            }
+
+            alp = alpha(aa);
+
+            iza = I0Bessel.value(alp);
+            LogFunction.log("须打log", "不打的话在某些机型上会卡住");
+            //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK
+
+            for (n1b = 1; n1b < n1; n1b *= 2) {
+            }
+            n1b *= 2;
+
+            stage1 = new double[n1b];
+
+            for (i = -(n1 / 2); i <= n1 / 2; i++) {
+                stage1[i + n1 / 2] =
+                        win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq / n1b * 2;
+                //System.err.printf("1: %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]); // OK
+            }
+
+            ipsize = (int) (2 + Math.sqrt(n1b));
+            fft_ip = new int[ipsize];
+            fft_ip[0] = 0;
+            wsize = n1b / 2;
+            fft_w = new double[wsize];
+
+            fft.rdft(n1b, 1, stage1, fft_ip, fft_w);
+            //for (i = -(n1 / 2); i <= n1 / 2; i++) {
+            // System.err.printf("1': %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]);
+            //}
+            //for (i = 0; i < ipsize; i++) {
+            // System.err.printf("ip: %06d: %d\n", i, fft_ip[i]); // OK
+            //}
+            //for (i = 0; i < wsize; i++) {
+            // System.err.printf("w: %06d: %e\n", i, fft_w[i]); // OK
+            //}
+        }
+
+        // Make stage 2 filter
+
+        if (osf == 1) {
+            fs2 = sfrq / frqgcd * dfrq;
+            n2 = 1;
+            n2y = n2x = 1;
+            f2order = new int[n2y];
+            f2order[0] = 0;
+            f2inc = new int[n2y];
+            f2inc[0] = sfrq / dfrq;
+            stage2 = new double[n2y][n2x];
+            stage2[0][0] = 1;
+        } else {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            double guard = 2;
+
+            fs2 = sfrq / frqgcd * dfrq;
+
+            df = (fs1 / 2 - sfrq / 2) * 2 / guard;
+            lpf = sfrq / 2 + (fs1 / 2 - sfrq / 2) / guard;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n2 = (int) (fs2 / df * d + 1);
+            if (n2 % 2 == 0) {
+                n2++;
+            }
+
+            alp = alpha(aa);
+            iza = I0Bessel.value(alp);
+            //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK
+
+            n2y = fs2 / fs1; // 0Tvfs2Tv?H
+            n2x = n2 / n2y + 1;
+
+            f2order = new int[n2y];
+            for (i = 0; i < n2y; i++) {
+                f2order[i] = fs2 / fs1 - (i * (fs2 / dfrq)) % (fs2 / fs1);
+                if (f2order[i] == fs2 / fs1) {
+                    f2order[i] = 0;
+                }
+            }
+
+            f2inc = new int[n2y];
+            for (i = 0; i < n2y; i++) {
+                f2inc[i] = (fs2 / dfrq - f2order[i]) / (fs2 / fs1) + 1;
+                if (f2order[i + 1 == n2y ? 0 : i + 1] == 0) {
+                    f2inc[i]--;
+                }
+            }
+
+            stage2 = new double[n2y][n2x];
+
+            //System.err.printf("n2y: %d, n2: %d\n", n2y, n2);
+            for (i = -(n2 / 2); i <= n2 / 2; i++) {
+                stage2[(i + n2 / 2) % n2y][(i + n2 / 2) / n2y] =
+                        win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1;
+                //System.err.printf(" stage2[%02d][%02d]: %f\n", (i + n2 / 2) % n2y, (i + n2 / 2) / n2y, win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1); // OK
+            }
+        }
+
+        // Apply filters
+
+        setstarttime();
+
+        {
+            int n1b2 = n1b / 2;
+            int rp; // inbuffs1Tv???
+            int rps; // rp(fs1/sfrq=osf)]
+            int rp2; // buf2fs2Tv???
+            int ds; // disposesfrqTv?
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            //          int nsmplwrt1;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt2 = 0;
+            int s2p; // stage1 filter?oTv?n1y*osf]
+            boolean init, ending;
+            //          int osc;
+            int bp; // rp2vZ?Dbuf2Tvu
+            int rps_backup, s2p_backup;
+            int k, ch, p;
+            int inbuflen = 0;
+            int sumread, sumwrite;
+            int delay = 0;
+            int op;
+
+            // |....B....|....C....| buf1 n1b2+n1b2
+            // |.A.|....D....| buf2 n2x+n1b2
+            //
+            // inbufBosf{TvORs?[
+            // CNA
+            // BCstage 1 filter
+            // DB
+            // ADstage 2 filter
+            // DA
+            // CDRs?[
+
+            buf1 =
+                    new double[nch][n1b];                                      //rawoutbuf = calloc(nch*(n2b2/osf+1),dbps);
+
+            buf2 = new double[nch][n2x + 1 + n1b2];
+
+            rawinbuf = ByteBuffer.allocate((nch * (n1b2 / osf + osf + 1)) * bps);
+            //System.err.println((double) n1b2 * sfrq / dfrq + 1);
+            rawoutbuf =
+                    ByteBuffer.allocate((int) (((double) n1b2 * dfrq / sfrq + 1) * (dbps * nch)));
+            inbuf = new double[nch * (n1b2 / osf + osf + 1)];
+            outbuf = new double[(int) (nch * ((double) n1b2 * dfrq / sfrq + 1))];
+
+            op = 0; // outbuf
+
+            s2p = 0;
+            rp = 0;
+            rps = 0;
+            ds = 0;
+            //          osc = 0;
+            rp2 = 0;
+
+            init = true;
+            ending = false;
+            delay = (int) ((double) n1 / 2 / ((double) fs1 / dfrq) +
+                    (double) n2 / 2 / ((double) fs2 / dfrq));
+            sumread = sumwrite = 0;
+
+            while (true) {
+                int nsmplread;
+                int toberead;
+                rps = 0;   //TODO settings this parameter to zero fixed a lot of problems
+                toberead = (n1b2 - rps - 1) / osf + 1;
+                if (toberead + sumread > chanklen) {
+                    toberead = chanklen - sumread;
+                }
+
+                rawinbuf.position(0);
+                rawinbuf.limit(bps * nch * toberead);
+
+                byte[] tempData = new byte[rawinbuf.limit()];
+                nsmplread = fpi.read(tempData);
+                if (nsmplread < 0) {
+                    nsmplread = 0;
+                }
+
+                //TODO sometimes happens, investigate around it
+                if (nsmplread < rawinbuf.limit()) {
+                    chanklen = sumread + nsmplread / bps * nch;
+                }
+
+                rawinbuf.limit(nsmplread);
+                rawinbuf = ByteBuffer.wrap(tempData);
+                rawinbuf.position(nsmplread);
+
+                rawinbuf.flip();
+                nsmplread /= bps * nch;
+
+                switch (bps) {
+                    case 1:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7f) * ((rawinbuf.get(i) & 0xff) - 128);
+                        }
+                        break;
+
+                    case 2:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asShortBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v;
+                            //                            System.err.printf("I: %f\n", inbuf[nch * inbuflen + i]);
+                        }
+                        break;
+
+                    case 3:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7fffff) * (((rawinbuf.get(i * 3) & 0xff) << 0) |
+                                            ((rawinbuf.get(i * 3 + 1) & 0xff) << 8) |
+                                            ((rawinbuf.get(i * 3 + 2) & 0xff) << 16));
+                        }
+                        break;
+
+                    case 4:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).getInt(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v;
+                        }
+                        break;
+                }
+
+                for (; i < nch * toberead; i++) {
+                    inbuf[i] = 0;
+                }
+
+                sumread += nsmplread;
+
+                //                ending = sumread >= chanklen;
+                ending = fpi.available() < 0 || sumread >= chanklen;
+
+                rps_backup = rps;
+                s2p_backup = s2p;
+
+                for (ch = 0; ch < nch; ch++) {
+                    rps = rps_backup;
+
+                    for (k = 0; k < rps; k++) {
+                        buf1[ch][k] = 0;
+                    }
+
+                    for (i = rps, j = 0; i < n1b2; i += osf, j++) {
+                        assert (j < ((n1b2 - rps - 1) / osf + 1));
+
+                        buf1[ch][i] = inbuf[j * nch + ch];
+
+                        for (k = i + 1; k < i + osf; k++) {
+                            buf1[ch][k] = 0;
+                        }
+                    }
+
+                    assert (j == ((n1b2 - rps - 1) / osf + 1));
+
+                    for (k = n1b2; k < n1b; k++) {
+                        buf1[ch][k] = 0;
+                    }
+
+                    rps = i - n1b2;
+                    rp += j;
+
+                    fft.rdft(n1b, 1, buf1[ch], fft_ip, fft_w);
+
+                    buf1[ch][0] = stage1[0] * buf1[ch][0];
+                    buf1[ch][1] = stage1[1] * buf1[ch][1];
+
+                    for (i = 1; i < n1b2; i++) {
+                        double re, im;
+
+                        re = stage1[i * 2] * buf1[ch][i * 2] -
+                                stage1[i * 2 + 1] * buf1[ch][i * 2 + 1];
+                        im = stage1[i * 2 + 1] * buf1[ch][i * 2] +
+                                stage1[i * 2] * buf1[ch][i * 2 + 1];
+
+                        buf1[ch][i * 2] = re;
+                        buf1[ch][i * 2 + 1] = im;
+                    }
+
+                    fft.rdft(n1b, -1, buf1[ch], fft_ip, fft_w);
+
+                    for (i = 0; i < n1b2; i++) {
+                        buf2[ch][n2x + 1 + i] += buf1[ch][i];
+                    }
+
+                    {
+                        int t1 = rp2 / (fs2 / fs1);
+                        if (rp2 % (fs2 / fs1) != 0) {
+                            t1++;
+                        }
+
+                        bp = buf2[0].length * ch + t1; // &(buf2[ch][t1]);
+                    }
+
+                    s2p = s2p_backup;
+
+                    for (p = 0; bp - (buf2[0].length * ch) < n1b2 + 1; p++) { // buf2[ch]
+                        double tmp = 0;
+                        int bp2;
+                        int s2o;
+
+                        bp2 = bp;
+                        s2o = f2order[s2p];
+                        bp += f2inc[s2p];
+                        s2p++;
+
+                        if (s2p == n2y) {
+                            s2p = 0;
+                        }
+
+                        assert ((bp2 - (buf2[0].length * ch)) * (fs2 / fs1) -
+                                (rp2 + p * (fs2 / dfrq)) == s2o); // &(buf2[ch][0])
+                        for (i = 0; i < n2x; i++) {
+                            //System.err.printf("%d (%d, %d)\n", i, bp2 / buf2[0].length, bp2 % buf2[0].length);
+                            tmp += stage2[s2o][i] *
+                                    buf2[bp2 / buf2[0].length][bp2 % buf2[0].length]; // *bp2++
+                            bp2++;
+                        }
+
+                        outbuf[op + p * nch + ch] = tmp;
+                        //System.err.printf("O: %06d: %f\n", op + p * nch + ch, tmp);
+                    }
+
+                    nsmplwrt2 = p;
+                }
+
+                rp2 += nsmplwrt2 * (fs2 / dfrq);
+
+                rawoutbuf.clear();
+                if (twopass) {
+                    for (i = 0; i < nsmplwrt2 * nch; i++) {
+                        double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i];
+                        peak[0] = peak[0] < f ? f : peak[0];
+                        //System.err.println("p: " + rawoutbuf.position() + ", l: " + rawoutbuf.limit());
+                        rawoutbuf.asDoubleBuffer().put(i, outbuf[i]);
+                        //if (i < 100) {
+                        // System.err.printf("1: %06d: %f\n", i, outbuf[i]);
+                        //}
+                        //System.err.print(StringUtil.getDump(rawoutbuf, i, 8));
+                    }
+                } else {
+                    switch (dbps) {
+                        case 1: {
+                            double gain2 = gain * 0x7f;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x80) {
+                                        double d = (double) s / -0x80;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x80;
+                                    }
+                                    if (0x7f < s) {
+                                        double d = (double) s / 0x7f;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7f;
+                                    }
+                                }
+
+                                rawoutbuf.put(i, (byte) (s + 0x80));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 2: {
+                            double gain2 = gain * 0x7fff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x8000) {
+                                        double d = (double) s / -0x8000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x8000;
+                                    }
+                                    if (0x7fff < s) {
+                                        double d = (double) s / 0x7fff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fff;
+                                    }
+                                }
+
+                                rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s);
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 3: {
+                            double gain2 = gain * 0x7fffff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x800000) {
+                                        double d = (double) s / -0x800000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x800000;
+                                    }
+                                    if (0x7fffff < s) {
+                                        double d = (double) s / 0x7fffff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fffff;
+                                    }
+                                }
+
+                                rawoutbuf.put(i * 3, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 1, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 2, (byte) (s & 255));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                    }
+                }
+
+                if (!init) {
+                    if (ending) {
+                        if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) {
+                            rawoutbuf.position(0);
+                            rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2;
+                        } else {
+                            rawoutbuf.position(0);
+                            int limitData = (int) (dbps * nch *
+                                    (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite));
+                            if (limitData > 0) {
+                                rawoutbuf.limit(limitData);
+                                writeBuffers(fpo, rawoutbuf);
+                            }
+                            break;
+                        }
+                    } else {
+                        rawoutbuf.position(0);
+                        rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                        writeBuffers(fpo, rawoutbuf);
+                        sumwrite += nsmplwrt2;
+                    }
+                } else {
+                    if (nsmplwrt2 < delay) {
+                        delay -= nsmplwrt2;
+                    } else {
+                        if (ending) {
+                            if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit(dbps * nch * (nsmplwrt2 - delay));
+                                writeBuffers(fpo, rawoutbuf);
+                                sumwrite += nsmplwrt2 - delay;
+                            } else {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit((int) (dbps * nch *
+                                        (Math.floor((double) sumread * dfrq / sfrq) + 2 + sumwrite +
+                                                nsmplwrt2 -
+                                                delay)));  //TODO fails with short signals (think that fixed this)
+                                writeBuffers(fpo, rawoutbuf);
+                                break;
+                            }
+                        } else {
+                            rawoutbuf.position(dbps * nch * delay);
+                            rawoutbuf.limit(dbps * nch * (nsmplwrt2));
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2 - delay;
+                            init = false;
+                        }
+                    }
+                }
+
+                {
+                    ds = (rp2 - 1) / (fs2 / fs1);
+
+                    if (ds > n1b2) {
+                        ds = n1b2;
+                    }
+
+                    for (ch = 0; ch < nch; ch++) {
+                        System.arraycopy(buf2[ch], ds, buf2[ch], 0,
+                                n2x + 1 + n1b2 - ds); // memmove TODO overlap
+                    }
+
+                    rp2 -= ds * (fs2 / fs1);
+                }
+
+                for (ch = 0; ch < nch; ch++) {
+                    System.arraycopy(buf1[ch], n1b2, buf2[ch], n2x + 1, n1b2);
+                }
+
+                if ((spcount++ & 7) == 7) {
+                    showprogress((double) sumread / chanklen);
+                }
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    public double no_src(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, double gain,
+                         int chanklen, boolean twopass, int dither) throws IOException {
+        double[] peak = new double[]{0};
+        int ch = 0, sumread = 0;
+
+        setstarttime();
+
+        ByteBuffer leos = null;
+        if (twopass) {
+            leos = ByteBuffer.allocate(8);
+        }
+
+        ByteBuffer buf = ByteBuffer.allocate(4);
+        while (sumread < chanklen * nch) {
+            double f = 0;
+            int s;
+
+            switch (bps) {
+                case 1:
+                    buf.position(0);
+                    buf.limit(1);
+
+                    byte[] tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+
+                    buf.flip();
+                    f = (1 / (double) 0x7f) * (buf.get(0) - 128);
+                    break;
+                case 2:
+                    buf.position(0);
+                    buf.limit(2);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    s = buf.order(byteOrder).asShortBuffer().get(0);
+                    f = (1 / (double) 0x7fff) * s;
+                    break;
+                case 3:
+                    buf.position(0);
+                    buf.limit(3);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    f = (1 / (double) 0x7fffff) * (((buf.get(0) & 0xff) << 0) |
+                            ((buf.get(1) & 0xff) << 8) |
+                            ((buf.get(2) & 0xff) << 16));
+                    break;
+                case 4:
+                    buf.position(0);
+                    buf.limit(4);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    s = buf.order(byteOrder).asIntBuffer().get(0);
+                    f = (1 / (double) 0x7fffffff) * s;
+                    break;
+            }
+
+            if (fpi.available() == 0) {
+                //            if (fpi.position() == fpi.size()) {
+                break;
+            }
+            f *= gain;
+
+            if (!twopass) {
+                switch (dbps) {
+                    case 1:
+                        f *= 0x7f;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(1);
+                        buf.put(0, (byte) (s + 128));
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                    case 2:
+                        f *= 0x7fff;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(2);
+                        buf.asShortBuffer().put(0, (short) s);
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                    case 3:
+                        f *= 0x7fffff;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(3);
+                        buf.put(0, (byte) (s & 255));
+                        s >>= 8;
+                        buf.put(1, (byte) (s & 255));
+                        s >>= 8;
+                        buf.put(2, (byte) (s & 255));
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                }
+            } else {
+                double p = f > 0 ? f : -f;
+                peak[0] = peak[0] < p ? p : peak[0];
+                leos.position(0);
+                leos.putDouble(f);
+                leos.flip();
+                writeBuffers(fpo, leos);
+            }
+
+            ch++;
+            if (ch == nch) {
+                ch = 0;
+            }
+            sumread++;
+
+            if ((sumread & 0x3ffff) == 0) {
+                showprogress((double) sumread / (chanklen * nch));
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    public static void main(String[] args) throws Exception {
+        new SSRC(args);
+    }
+
+    /** */
+    private static final double presets[] = {0.7, 0.9, 0.18};
+
+    public SSRC() {
+    }
+
+    /** */
+    SSRC(String[] argv) throws IOException {
+        String sfn, dfn, tmpfn = null;
+        FileInputStream fpi = null;
+        File fo = null;
+        FileOutputStream fpo = null;
+        File ft = null;
+        FileOutputStream fpto = null;
+        boolean twopass, normalize;
+        int dither, pdf, samp = 0;
+        int nch, bps;
+        int length;
+        int sfrq, dfrq, dbps;
+        double att, noiseamp;
+        double[] peak = new double[]{0};
+        int i;
+
+        // parse command line options
+
+        dfrq = -1;
+        att = 0;
+        dbps = -1;
+        twopass = false;
+        normalize = false;
+        dither = 0;
+        pdf = 0;
+        noiseamp = 0.18;
+
+        for (i = 0; i < argv.length; i++) {
+            if (argv[i].charAt(0) != '-') {
+                break;
+            }
+
+            if (argv[i].equals("--rate")) {
+                dfrq = Integer.parseInt(argv[++i]);
+                //System.err.printf("dfrq: %d\n", dfrq);
+                continue;
+            }
+
+            if (argv[i].equals("--att")) {
+                att = Float.parseFloat(argv[++i]);
+                continue;
+            }
+
+            if (argv[i].equals("--bits")) {
+                dbps = Integer.parseInt(argv[++i]);
+                if (dbps != 8 && dbps != 16 && dbps != 24) {
+                    throw new IllegalArgumentException(
+                            "Error: Only 8bit, 16bit and 24bit PCM are supported.");
+                }
+                dbps /= 8;
+                continue;
+            }
+
+            if (argv[i].equals("--twopass")) {
+                twopass = true;
+                continue;
+            }
+
+            if (argv[i].equals("--normalize")) {
+                twopass = true;
+                normalize = true;
+                continue;
+            }
+
+            if (argv[i].equals("--dither")) {
+                try {
+                    dither = Integer.parseInt(argv[i + 1]);
+                    if (dither < 0 || dither > 4) {
+                        throw new IllegalArgumentException(
+                                "unrecognized dither type : " + argv[i + 1]);
+                    }
+                    i++;
+                } catch (NumberFormatException e) {
+                    dither = -1;
+                }
+                continue;
+            }
+
+            if (argv[i].equals("--pdf")) {
+                try {
+                    pdf = Integer.parseInt(argv[i + 1]);
+                    if (pdf < 0 || pdf > 2) {
+                        throw new IllegalArgumentException(
+                                "unrecognized p.d.f. type : " + argv[i + 1]);
+                    }
+                    i++;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("unrecognized p.d.f. type : " + argv[i + 1]);
+                }
+
+                try {
+                    noiseamp = Double.parseDouble(argv[i + 1]);
+                    i++;
+                } catch (NumberFormatException e) {
+                    noiseamp = presets[pdf];
+                }
+
+                continue;
+            }
+
+            if (argv[i].equals("--quiet")) {
+                quiet = true;
+                continue;
+            }
+
+            if (argv[i].equals("--tmpfile")) {
+                tmpfn = argv[++i];
+                continue;
+            }
+
+            if (argv[i].equals("--profile")) {
+                //noinspection IfCanBeSwitch
+                if (argv[i + 1].equals("fast")) {
+                    AA = 96;
+                    DF = 8000;
+                    FFTFIRLEN = 1024;
+                } else if (argv[i + 1].equals("standard")) {
+                    /* nothing to do */
+                } else {
+                    throw new IllegalArgumentException("unrecognized profile : " + argv[i + 1]);
+                }
+                i++;
+                continue;
+            }
+
+            throw new IllegalArgumentException("unrecognized option : " + argv[i]);
+        }
+
+        if (!quiet) {
+            System.err.printf("Shibatch sampling rate converter version " + VERSION +
+                    "(high precision/nio)\n\n");
+        }
+
+        if (argv.length - i != 2) {
+            usage();
+            throw new IllegalStateException("too few arguments");
+        }
+
+        sfn = argv[i];
+        dfn = argv[i + 1];
+
+        try {
+            fpi = new FileInputStream(sfn);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("cannot open input file.");
+        }
+
+        // read wav header
+
+        {
+            @SuppressWarnings("unused") short word;
+            @SuppressWarnings("unused") int dword;
+
+            ByteBuffer bb = ByteBuffer.allocate(256).order(ByteOrder.LITTLE_ENDIAN);
+            bb.limit(36);
+            fpi.getChannel().read(bb);
+            bb.flip();
+            System.err.println("p: " + bb.position() + ", l: " + bb.limit());
+            if (bb.get() != 'R') {
+                fmterr(1);
+            }
+            if (bb.get() != 'I') {
+                fmterr(1);
+            }
+            if (bb.get() != 'F') {
+                fmterr(1);
+            }
+            if (bb.get() != 'F') {
+                fmterr(1);
+            }
+
+            dword = bb.getInt();
+
+            if (bb.get() != 'W') {
+                fmterr(2);
+            }
+            if (bb.get() != 'A') {
+                fmterr(2);
+            }
+            if (bb.get() != 'V') {
+                fmterr(2);
+            }
+            if (bb.get() != 'E') {
+                fmterr(2);
+            }
+            if (bb.get() != 'f') {
+                fmterr(2);
+            }
+            if (bb.get() != 'm') {
+                fmterr(2);
+            }
+            if (bb.get() != 't') {
+                fmterr(2);
+            }
+            if (bb.get() != ' ') {
+                fmterr(2);
+            }
+
+            int sizeOfFmt = bb.getInt();
+
+            if (bb.getShort() != 1) {
+                throw new IllegalStateException("Error: Only PCM is supported.");
+            }
+            nch = bb.getShort();
+            sfrq = bb.getInt();
+            bps = bb.getInt();
+            if (bps % sfrq * nch != 0) {
+                fmterr(4);
+            }
+
+            word = bb.getShort();
+            word = bb.getShort();
+
+            bps /= sfrq * nch;
+
+            if (sizeOfFmt > 16) {
+                bb.position(0);
+                bb.limit(2);
+                fpi.read(getDataFromByteBuffer(bb));
+                bb.flip();
+                int sizeofExtended = bb.getShort();
+                fpi.getChannel().position(fpi.getChannel().position() + sizeofExtended);
+            }
+
+            while (true) {
+                bb.position(0);
+                bb.limit(8);
+                fpi.getChannel().read(bb);
+                bb.flip();
+                int c0 = bb.get();
+                int c1 = bb.get();
+                int c2 = bb.get();
+                int c3 = bb.get();
+                length = bb.getInt();
+                System.err.printf("chunk: %c%c%c%c\n", c0, c1, c2, c3);
+                if (c0 == 'd' && c1 == 'a' && c2 == 't' && c3 == 'a') {
+                    break;
+                }
+                if (fpi.getChannel().position() == fpi.getChannel().size()) {
+                    break;
+                }
+                fpi.getChannel().position(fpi.getChannel().position() + length);
+            }
+            if (fpi.getChannel().position() == fpi.getChannel().size()) {
+                throw new IllegalStateException("Couldn't find data chank");
+            }
+        }
+
+        if (bps != 1 && bps != 2 && bps != 3 && bps != 4) {
+            throw new IllegalStateException(
+                    "Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported.");
+        }
+
+        if (dbps == -1) {
+            if (bps != 1) {
+                dbps = bps;
+            } else {
+                dbps = 2;
+            }
+            if (dbps == 4) {
+                dbps = 3;
+            }
+        }
+
+        if (dfrq == -1) {
+            dfrq = sfrq;
+        }
+
+        if (dither == -1) {
+            if (dbps < bps) {
+                if (dbps == 1) {
+                    dither = 4;
+                } else {
+                    dither = 3;
+                }
+            } else {
+                dither = 1;
+            }
+        }
+
+        if (!quiet) {
+            final String[] dtype = {"none", "no noise shaping", "triangular spectral shape",
+                    "ATH based noise shaping", "ATH based noise shaping(less amplitude)"};
+            final String[] ptype = {"rectangular", "triangular", "gaussian"};
+            System.err.printf("frequency : %d -> %d\n", sfrq, dfrq);
+            System.err.printf("attenuation : %gdB\n", att);
+            System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8);
+            System.err.printf("nchannels : %d\n", nch);
+            System.err.printf("length : %d bytes, %g secs\n", length,
+                    (double) length / bps / nch / sfrq);
+            if (dither == 0) {
+                System.err.printf("dither type : none\n");
+            } else {
+                System.err
+                        .printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf],
+                                noiseamp);
+            }
+            System.err.printf("\n");
+        }
+
+        if (twopass) {
+        }
+
+        try {
+            fo = new File(dfn);
+            fpo = new FileOutputStream(fo);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("cannot open output file.");
+        }
+
+        // generate wav header
+
+        {
+            short word;
+            int dword;
+
+            ByteBuffer leos = ByteBuffer.allocate(44).order(ByteOrder.LITTLE_ENDIAN);
+
+            leos.put("RIFF".getBytes());
+            dword = 0;
+            leos.putInt(dword);
+
+            leos.put("WAVEfmt ".getBytes());
+            dword = 16;
+            leos.putInt(dword);
+            word = 1;
+            leos.putShort(word); // inAudioFormat category, PCM
+            word = (short) nch;
+            leos.putShort(word); // channels
+            dword = dfrq;
+            leos.putInt(dword); // sampling rate
+            dword = dfrq * nch * dbps;
+            leos.putInt(dword); // bytes per sec
+            word = (short) (dbps * nch);
+            leos.putShort(word); // block alignment
+            word = (short) (dbps * 8);
+            leos.putShort(word); // bits per sample
+
+            leos.put("data".getBytes());
+            dword = 0;
+            leos.putInt(dword);
+
+            leos.flip();
+            writeBuffers(fpo, leos);
+        }
+
+        if (dither != 0) {
+            int min = 0, max = 0;
+            if (dbps == 1) {
+                min = -0x80;
+                max = 0x7f;
+            }
+            if (dbps == 2) {
+                min = -0x8000;
+                max = 0x7fff;
+            }
+            if (dbps == 3) {
+                min = -0x800000;
+                max = 0x7fffff;
+            }
+            if (dbps == 4) {
+                min = -0x80000000;
+                max = 0x7fffffff;
+            }
+
+            samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp);
+        }
+
+        if (twopass) {
+            double gain = 0;
+            int ch = 0;
+            int fptlen, sumread;
+
+            if (!quiet) {
+                System.err.printf("Pass 1\n");
+            }
+
+            try {
+                if (tmpfn != null) {
+                    ft = new File(tmpfn);
+                } else {
+                    ft = File.createTempFile("ssrc_", ".tmp");
+                }
+                fpto = new FileOutputStream(ft);
+            } catch (IOException e) {
+                throw new IllegalStateException("cannot open temporary file.");
+            }
+
+            //System.err.printf("nch: %d, bps: %d, size: %d, sfrq: %d, dfrq: %d, ???: %d, ???: %d, twopass: %b, dither: %d\n", nch, bps, 8, sfrq, dfrq, 1, length / bps / nch, twopass, dither);
+            if (normalize) {
+                if (sfrq < dfrq) {
+                    peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch,
+                            twopass, dither);
+                } else if (sfrq > dfrq) {
+                    peak[0] = downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch,
+                            twopass, dither);
+                } else {
+                    peak[0] =
+                            no_src(fpi, fpto, nch, bps, 8, 1, length / bps / nch, twopass, dither);
+                }
+            } else {
+                if (sfrq < dfrq) {
+                    peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20),
+                            length / bps / nch, twopass, dither);
+                } else if (sfrq > dfrq) {
+                    peak[0] =
+                            downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20),
+                                    length / bps / nch, twopass, dither);
+                } else {
+                    peak[0] = no_src(fpi, fpto, nch, bps, 8, Math.pow(10, -att / 20),
+                            length / bps / nch, twopass, dither);
+                }
+            }
+
+            fpto.close();
+
+            if (!quiet) {
+                System.err.printf("\npeak : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+
+            if (!normalize) {
+                if (peak[0] < Math.pow(10, -att / 20)) {
+                    peak[0] = 1;
+                } else {
+                    peak[0] *= Math.pow(10, att / 20);
+                }
+            } else {
+                peak[0] *= Math.pow(10, att / 20);
+            }
+
+            if (!quiet) {
+                System.err.printf("\nPass 2\n");
+            }
+
+            if (dither != 0) {
+                switch (dbps) {
+                    case 1:
+                        gain = (normalize || peak[0] >= (0x7f - samp) / (double) 0x7f) ?
+                                1 / peak[0] * (0x7f - samp) : 1 / peak[0] * 0x7f;
+                        break;
+                    case 2:
+                        gain = (normalize || peak[0] >= (0x7fff - samp) / (double) 0x7fff) ?
+                                1 / peak[0] * (0x7fff - samp) : 1 / peak[0] * 0x7fff;
+                        break;
+                    case 3:
+                        gain = (normalize || peak[0] >= (0x7fffff - samp) / (double) 0x7fffff) ?
+                                1 / peak[0] * (0x7fffff - samp) : 1 / peak[0] * 0x7fffff;
+                        break;
+                }
+            } else {
+                switch (dbps) {
+                    case 1:
+                        gain = 1 / peak[0] * 0x7f;
+                        break;
+                    case 2:
+                        gain = 1 / peak[0] * 0x7fff;
+                        break;
+                    case 3:
+                        gain = 1 / peak[0] * 0x7fffff;
+                        break;
+                }
+            }
+            randptr = 0;
+
+            setstarttime();
+
+            fptlen = (int) (ft.length() / 8);
+            //System.err.println("tmp: " + fpt.getFilePointer());
+
+            FileChannel fpti = new FileInputStream(ft).getChannel();
+            ByteBuffer leis = ByteBuffer.allocate(8);
+            for (sumread = 0; sumread < fptlen; ) {
+                double f;
+                int s;
+
+                leis.clear();
+                fpti.read(leis);
+                leis.flip();
+                f = leis.getDouble();
+                //if (sumread < 100) {
+                // System.err.printf("2: %06d: %f\n", sumread, f);
+                //}
+                f *= gain;
+                sumread++;
+
+                switch (dbps) {
+                    case 1: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(1);
+                        buf.put((byte) (s + 128));
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                    case 2: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
+                        buf.putShort((short) s);
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                    case 3: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(3);
+                        buf.put((byte) (s & 255));
+                        s >>= 8;
+                        buf.put((byte) (s & 255));
+                        s >>= 8;
+                        buf.put((byte) (s & 255));
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                }
+
+                ch++;
+                if (ch == nch) {
+                    ch = 0;
+                }
+
+                if ((sumread & 0x3ffff) == 0) {
+                    showprogress((double) sumread / fptlen);
+                }
+            }
+            showprogress(1);
+            if (!quiet) {
+                System.err.printf("\n");
+            }
+            fpti.close();
+            if (ft != null) {
+                //System.err.println("ft: " + ft);
+                if (!ft.delete()) {
+                    System.err.printf("Failed to remove %s\n", ft);
+                }
+            }
+        } else {
+            if (sfrq < dfrq) {
+                peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            } else if (sfrq > dfrq) {
+                peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            } else {
+                peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            }
+            if (!quiet) {
+                System.err.printf("\n");
+            }
+        }
+
+        if (dither != 0) {
+            quit_shaper(nch);
+        }
+
+        if (!twopass && peak[0] > 1) {
+            if (!quiet) {
+                System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+        }
+
+        {
+            int dword;
+            int len;
+
+            fpo.close();
+
+            fo = new File(dfn);
+
+            len = (int) fo.length();
+            FileChannel fpo1 = new RandomAccessFile(fo, "rw").getChannel();
+            ByteBuffer leos = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
+
+            dword = len - 8;
+            leos.position(0);
+            leos.limit(4);
+            leos.putInt(dword);
+            leos.flip();
+            fpo1.write(leos, 4);
+
+            dword = len - 44;
+            leos.position(0);
+            leos.limit(4);
+            leos.putInt(dword);
+            leos.flip();
+            fpo1.write(leos, 40);
+
+            fpo1.close();
+        }
+    }
+
+
+    /** */
+    public SSRC(InputStream fpi, OutputStream fpo, int sfrq, int dfrq, int bps, int dbps, int nch,
+                int length, double att, int dither, boolean quiet_) throws IOException {
+        String tmpfn = null;
+        boolean twopass, normalize;
+        int pdf, samp = 0;
+        double noiseamp;
+        double[] peak = new double[]{0};
+        int i;
+
+        // parse command line options
+
+        twopass = false;
+        normalize = false;
+
+        pdf = 0;
+        noiseamp = 0.18;
+
+        if (dither < 0 || dither > 4) {
+            throw new IllegalArgumentException("unrecognized dither type : " + dither);
+        }
+        this.quiet = quiet_;
+
+        if (bps != 1 && bps != 2 && bps != 3 && bps != 4) {
+            throw new IllegalStateException(
+                    "Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported.");
+        }
+
+        if (dbps == -1) {
+            if (bps != 1) {
+                dbps = bps;
+            } else {
+                dbps = 2;
+            }
+            if (dbps == 4) {
+                dbps = 3;
+            }
+        }
+
+        if (dfrq == -1) {
+            dfrq = sfrq;
+        }
+
+        if (dither == -1) {
+            if (dbps < bps) {
+                if (dbps == 1) {
+                    dither = 4;
+                } else {
+                    dither = 3;
+                }
+            } else {
+                dither = 1;
+            }
+        }
+
+        if (!quiet) {
+            final String[] dtype = {"none", "no noise shaping", "triangular spectral shape",
+                    "ATH based noise shaping", "ATH based noise shaping(less amplitude)"};
+            final String[] ptype = {"rectangular", "triangular", "gaussian"};
+            System.err.printf("frequency : %d -> %d\n", sfrq, dfrq);
+            System.err.printf("attenuation : %gdB\n", att);
+            System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8);
+            System.err.printf("nchannels : %d\n", nch);
+            System.err.printf("length : %d bytes, %g secs\n", length,
+                    (double) length / bps / nch / sfrq);
+            if (dither == 0) {
+                System.err.printf("dither type : none\n");
+            } else {
+                System.err
+                        .printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf],
+                                noiseamp);
+            }
+            System.err.printf("\n");
+        }
+
+
+        if (dither != 0) {
+            int min = 0, max = 0;
+            if (dbps == 1) {
+                min = -0x80;
+                max = 0x7f;
+            }
+            if (dbps == 2) {
+                min = -0x8000;
+                max = 0x7fff;
+            }
+            if (dbps == 3) {
+                min = -0x800000;
+                max = 0x7fffff;
+            }
+            if (dbps == 4) {
+                min = -0x80000000;
+                max = 0x7fffffff;
+            }
+
+            samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp);
+        }
+
+        if (sfrq < dfrq) {
+            peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                    length / bps / nch, twopass, dither);
+        } else if (sfrq > dfrq) {
+            peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                    length / bps / nch, twopass, dither);
+        } else {
+            peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20), length / bps / nch,
+                    twopass, dither);
+        }
+
+        if (!quiet) {
+            System.err.printf("\n");
+        }
+
+        if (dither != 0) {
+            quit_shaper(nch);
+        }
+
+        if (!twopass && peak[0] > 1) {
+            if (!quiet) {
+                System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+        }
+    }
+
+    protected byte[] getDataFromByteBuffer(ByteBuffer rawoutbuf) {
+        byte[] tempDataWrt = new byte[rawoutbuf.limit() - rawoutbuf.position()];
+        rawoutbuf.get(tempDataWrt, 0, tempDataWrt.length);
+
+        return tempDataWrt;
+    }
+
+    protected void writeBuffers(OutputStream fpo, ByteBuffer rawoutbuf) {
+        try {
+            fpo.write(getDataFromByteBuffer(rawoutbuf));
+        } catch (IOException e) {
+            // Some problems (Read end dead)
+        }
+    }
+}
+
+/* */

+ 155 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/util/I0Bessel.java

@@ -0,0 +1,155 @@
+/*
+ * Copyright(C) 1996 Takuya OOURA (email: ooura@mmm.t.u-tokyo.ac.jp).
+ * You may use, copy, modify this code for any purpose and
+ * without fee. You may distribute this ORIGINAL package.
+ */
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.util;
+
+
+/**
+ * Bessel I_0().
+ *
+ * @author <a href="mailto:ooura@mmm.t.u-tokyo.ac.jp">Takuya OOURA</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+public class I0Bessel {
+    /** */
+    private static final double[] a = {
+        8.5246820682016865877e-11, 2.5966600546497407288e-9,
+        7.9689994568640180274e-8, 1.9906710409667748239e-6,
+        4.0312469446528002532e-5, 6.4499871606224265421e-4,
+        0.0079012345761930579108, 0.071111111109207045212,
+        0.444444444444724909, 1.7777777777777532045,
+        4.0000000000000011182, 3.99999999999999998,
+        1.0000000000000000001,
+        1.1520919130377195927e-10, 2.2287613013610985225e-9,
+        8.1903951930694585113e-8, 1.9821560631611544984e-6,
+        4.0335461940910133184e-5, 6.4495330974432203401e-4,
+        0.0079013012611467520626, 0.071111038160875566622,
+        0.44444450319062699316, 1.7777777439146450067,
+        4.0000000132337935071, 3.9999999968569015366,
+        1.0000000003426703174,
+        1.5476870780515238488e-10, 1.2685004214732975355e-9,
+        9.2776861851114223267e-8, 1.9063070109379044378e-6,
+        4.0698004389917945832e-5, 6.4370447244298070713e-4,
+        0.0079044749458444976958, 0.071105052411749363882,
+        0.44445280640924755082, 1.7777694934432109713,
+        4.0000055808824003386, 3.9999977081165740932,
+        1.0000004333949319118,
+        2.0675200625006793075e-10, -6.1689554705125681442e-10,
+        1.2436765915401571654e-7, 1.5830429403520613423e-6,
+        4.2947227560776583326e-5, 6.3249861665073441312e-4,
+        0.0079454472840953930811, 0.070994327785661860575,
+        0.44467219586283000332, 1.7774588182255374745,
+        4.0003038986252717972, 3.9998233869142057195,
+        1.0000472932961288324,
+        2.7475684794982708655e-10, -3.8991472076521332023e-9,
+        1.9730170483976049388e-7, 5.9651531561967674521e-7,
+        5.1992971474748995357e-5, 5.7327338675433770752e-4,
+        0.0082293143836530412024, 0.069990934858728039037,
+        0.44726764292723985087, 1.7726685170014087784,
+        4.0062907863712704432, 3.9952750700487845355,
+        1.0016354346654179322
+    };
+    /** */
+    private static final double[] b = {
+        6.7852367144945531383e-8, 4.6266061382821826854e-7,
+        6.9703135812354071774e-6, 7.6637663462953234134e-5,
+        7.9113515222612691636e-4, 0.0073401204731103808981,
+        0.060677114958668837046, 0.43994941411651569622,
+        2.7420017097661750609, 14.289661921740860534,
+        59.820609640320710779, 188.78998681199150629,
+        399.8731367825601118, 427.56411572180478514,
+        1.8042097874891098754e-7, 1.2277164312044637357e-6,
+        1.8484393221474274861e-5, 2.0293995900091309208e-4,
+        0.0020918539850246207459, 0.019375315654033949297,
+        0.15985869016767185908, 1.1565260527420641724,
+        7.1896341224206072113, 37.354773811947484532,
+        155.80993164266268457, 489.5211371158540918,
+        1030.9147225169564806, 1093.5883545113746958,
+        4.8017305613187493564e-7, 3.261317843912380074e-6,
+        4.9073137508166159639e-5, 5.3806506676487583755e-4,
+        0.0055387918291051866561, 0.051223717488786549025,
+        0.42190298621367914765, 3.0463625987357355872,
+        18.895299447327733204, 97.915189029455461554,
+        407.13940115493494659, 1274.3088990480582632,
+        2670.9883037012547506, 2815.7166284662544712,
+        1.2789926338424623394e-6, 8.6718263067604918916e-6,
+        1.3041508821299929489e-4, 0.001428224737372747892,
+        0.014684070635768789378, 0.13561403190404185755,
+        1.1152592585977393953, 8.0387088559465389038,
+        49.761318895895479206, 257.2684232313529138,
+        1066.8543146269566231, 3328.3874581009636362,
+        6948.8586598121634874, 7288.4893398212481055,
+        3.409350368197032893e-6, 2.3079025203103376076e-5,
+        3.4691373283901830239e-4, 0.003794994977222908545,
+        0.038974209677945602145, 0.3594948380414878371,
+        2.9522878893539528226, 21.246564609514287056,
+        131.28727387146173141, 677.38107093296675421,
+        2802.3724744545046518, 8718.5731420798254081,
+        18141.348781638832286, 18948.925349296308859
+    };
+    /** */
+    private static final double[] c = {
+        2.5568678676452702768e-15, 3.0393953792305924324e-14,
+        6.3343751991094840009e-13, 1.5041298011833009649e-11,
+        4.4569436918556541414e-10, 1.746393051427167951e-8,
+        1.0059224011079852317e-6, 1.0729838945088577089e-4,
+        0.05150322693642527738,
+        5.2527963991711562216e-15, 7.202118481421005641e-15,
+        7.2561421229904797156e-13, 1.482312146673104251e-11,
+        4.4602670450376245434e-10, 1.7463600061788679671e-8,
+        1.005922609132234756e-6, 1.0729838937545111487e-4,
+        0.051503226936437300716,
+        1.3365917359358069908e-14, -1.2932643065888544835e-13,
+        1.7450199447905602915e-12, 1.0419051209056979788e-11,
+        4.58047881980598326e-10, 1.7442405450073548966e-8,
+        1.0059461453281292278e-6, 1.0729837434500161228e-4,
+        0.051503226940658446941,
+        5.3771611477352308649e-14, -1.1396193006413731702e-12,
+        1.2858641335221653409e-11, -5.9802086004570057703e-11,
+        7.3666894305929510222e-10, 1.6731837150730356448e-8,
+        1.0070831435812128922e-6, 1.0729733111203704813e-4,
+        0.051503227360726294675,
+        3.7819492084858931093e-14, -4.8600496888588034879e-13,
+        1.6898350504817224909e-12, 4.5884624327524255865e-11,
+        1.2521615963377513729e-10, 1.8959658437754727957e-8,
+        1.0020716710561353622e-6, 1.073037119856927559e-4,
+        0.05150322383300230775
+    };
+
+    public static double value(double x) {
+        int k;
+        double w, t, y;
+        w = Math.abs(x);
+        if (w < 8.5) {
+            t = w * w * 0.0625;
+            k = 13 * ((int) t);
+            y = (((((((((((a[k] * t + a[k + 1]) * t +
+                a[k + 2]) * t + a[k + 3]) * t + a[k + 4]) * t +
+                a[k + 5]) * t + a[k + 6]) * t + a[k + 7]) * t +
+                a[k + 8]) * t + a[k + 9]) * t + a[k + 10]) * t +
+                a[k + 11]) * t + a[k + 12];
+        } else if (w < 12.5) {
+            k = (int) w;
+            t = w - k;
+            k = 14 * (k - 8);
+            y = ((((((((((((b[k] * t + b[k + 1]) * t +
+                b[k + 2]) * t + b[k + 3]) * t + b[k + 4]) * t +
+                b[k + 5]) * t + b[k + 6]) * t + b[k + 7]) * t +
+                b[k + 8]) * t + b[k + 9]) * t + b[k + 10]) * t +
+                b[k + 11]) * t + b[k + 12]) * t + b[k + 13];
+        } else {
+            t = 60 / w;
+            k = 9 * ((int) t);
+            y = ((((((((c[k] * t + c[k + 1]) * t +
+                c[k + 2]) * t + c[k + 3]) * t + c[k + 4]) * t +
+                c[k + 5]) * t + c[k + 6]) * t + c[k + 7]) * t +
+                c[k + 8]) * Math.sqrt(t) * Math.exp(w);
+        }
+        return y;
+    }
+}
+
+/* */

+ 2604 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/audiotransfer/vavi/util/SplitRadixFft.java

@@ -0,0 +1,2604 @@
+/*
+ * Copyright Takuya OOURA, 1996-2001
+ *
+ * You may use, copy, modify and distribute this code
+ * for any purpose (include commercial use) and without fee.
+ * Please refer to this package when you modify this code.
+ */
+package cn.i2edu.dubbing_lib.audioUtils.audiotransfer.vavi.util;
+
+
+
+
+/**
+ * Fast Fourier/Cosine/Sine Transform.
+ * <pre>
+ *  dimension   :one
+ *  data length :power of 2
+ *  decimation  :frequency
+ *  radix       :<b>split-radix</b>
+ *  data        :inplace
+ *  table       :use
+ * </pre>
+ * <h4>Appendix:</h4>
+ * <p>
+ *  The cos/sin table is recalculated when the larger table required.
+ *  w[] and ip[] are compatible with all routines.
+ * </p>
+ * @author <a href="mailto:ooura@mmm.t.u-tokyo.ac.jp">Takuya OOURA</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+@SuppressWarnings({"PointlessArithmeticExpression", "JavaDoc", "SameParameterValue", "FinalPrivateMethod"})
+public class SplitRadixFft {
+
+    /** */
+    private static final int CDFT_RECURSIVE_N = 512;
+
+    /**
+     * Complex Discrete Fourier Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt;
+     *          X[k] = sum_j=0&amp;circ;n-1 x[j]*exp(2*pi*i*j*k/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt;
+     *          X[k] = sum_j=0&amp;circ;n-1 x[j]*exp(-2*pi*i*j*k/n), 0&lt;=k&lt;n
+     *      (notes: sum_j=0&amp;circ;n-1 is a summation from j=0 to n-1)
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          cdft(2*n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          cdft(2*n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          cdft(2*n, -1, a, ip, w);
+     *      is
+     *          cdft(2*n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= 2 * n - 1; j++) {
+     *              a[j] *= 1.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n 2*n data length (int)
+     *          n &gt;= 1, n = power of 2
+     * @param isgn
+     * @param a a[0...2*n-1] input/output data (REAL *)
+     *                      input data
+     *                          a[2*j] = Re(x[j]),
+     *                          a[2*j+1] = Im(x[j]), 0&lt;=j&lt;n
+     *                      output data
+     *                          a[2*k] = Re(X[k]),
+     *                          a[2*k+1] = Im(X[k]), 0&lt;=k&lt;n
+     * @param ip ip[0...*] work area for bit reversal (int *)
+     *           length of ip &gt;= 2+sqrt(n)
+     *           strictly,
+     *           length of ip &gt;=
+     *              2+(1&lt;&lt;(int)(log(n+0.5)/log(2))/2).
+     *           ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w w[0...n/2-1] cos/sin table (REAL *)
+     *          w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void cdft(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int nw;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        if (isgn >= 0) {
+            cftfsub(n, a, ip, 2, nw, w);
+        } else {
+            cftbsub(n, a, ip, 2, nw, w);
+        }
+    }
+
+    /**
+     * Real Discrete Fourier Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; RDFT
+     *          R[k] = sum_j = 0 &amp; &circ; (n - 1) a[j] * cos(2 * pi * j * k / n), 0 &lt;= k &lt;= n / 2
+     *          I[k] = sum_j = 0 &amp; &circ; (n - 1) a[j] * sin(2 * pi * j * k / n), 0 &lt; k &lt; n / 2
+     *      &lt;case2&gt; IRDFT (excluding scale)
+     *          a[k] = (R[0] + R[n / 2] * cos(pi * k)) / 2 +
+     *              sum_j = 1 &amp; &circ; (n / 2 - 1) R[j] * cos(2 * pi * j * k / n) +
+     *              sum_j = 1 &amp; &circ; (n / 2 - 1) I[j] * sin(2 * pi * j * k / n), 0 &lt;= k &lt; n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          rdft(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          rdft(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          rdft(n, 1, a, ip, w);
+     *      is
+     *          rdft(n, -1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n data length <br>
+     *  n &gt;= 2, n = power of 2
+     * @param isgn
+     * @param a [0...n-1] input/output data
+     * <pre>
+     *  &lt;case1&gt;
+     *      output data
+     *          a[2 * k] = R[k], 0 &lt;= k &lt; n / 2
+     *          a[2 * k + 1] = I[k], 0 &lt; k &lt; n / 2
+     *          a[1] = R[n/2]
+     *  &lt;case2&gt;
+     *      input data
+     *          a[2 * j] = R[j], 0 &lt;= j &lt; n / 2
+     *          a[2 * j + 1] = I[j], 0 &lt; j &lt; n / 2
+     *          a[1] = R[n / 2]
+     * </pre>
+     * @param ip [0...*] work area for bit reversal
+     * <pre>
+     *  length of ip &gt;= 2 + sqrt(n / 2)
+     *  strictly,
+     *  length of ip &gt;=
+     *      2 + (1 &lt;&lt; (int) (log(n / 2 + 0.5) / log(2)) / 2).
+     * </pre>
+     *  ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w [0...n/2-1] cos/sin table <br>
+     *  w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void rdft(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int nw, nc;
+        double xi;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 2)) {
+            nc = n >> 2;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xi = a[0] - a[1];
+            a[0] += a[1];
+            a[1] = xi;
+        } else {
+            a[1] = 0.5 * (a[0] - a[1]);
+            a[0] -= a[1];
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+    }
+
+    /**
+     * Discrete Cosine Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; IDCT (excluding scale)
+     *          C[k] = sum_j=0&amp;circ;n-1 a[j]*cos(pi*j*(k+1/2)/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt; DCT
+     *          C[k] = sum_j=0&amp;circ;n-1 a[j]*cos(pi*(j+1/2)*k/n), 0&lt;=k&lt;n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          ddct(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          ddct(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          ddct(n, -1, a, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          ddct(n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n data length (int)
+     * <pre>
+     *  n &gt;= 2, n = power of 2
+     * </pre>
+     * @param isgn
+     * @param a [0...n-1] input/output data (REAL *)
+     * <pre>
+     *  output data
+     *      a[k] = C[k], 0&lt;=k&lt;n
+     * </pre>
+     * @param ip [0...*] work area for bit reversal (int *)
+     * <pre>
+     *  length of ip &gt;= 2+sqrt(n/2)
+     *  strictly,
+     *  length of ip &gt;=
+     *      2+(1&lt;&lt;(int)(log(n/2+0.5)/log(2))/2).
+     *  ip[0],ip[1] are pointers of the cos/sin table.
+     * </pre>
+     * @param w [0...n*5/4-1] cos/sin table (REAL *)
+     * <pre>
+     *  w[],ip[] are initialized if ip[0] == 0.
+     * </pre>
+     */
+    public void ddct(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int j, nw, nc;
+        double xr;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > nc) {
+            nc = n;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn < 0) {
+            xr = a[n - 1];
+            for (j = n - 2; j >= 2; j -= 2) {
+                a[j + 1] = a[j] - a[j - 1];
+                a[j] += a[j - 1];
+            }
+            a[1] = a[0] - xr;
+            a[0] += xr;
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+        dctsub(n, a, nc, w, nw);
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xr = a[0] - a[1];
+            a[0] += a[1];
+            for (j = 2; j < n; j += 2) {
+                a[j - 1] = a[j] - a[j + 1];
+                a[j] += a[j + 1];
+            }
+            a[n - 1] = xr;
+        }
+    }
+
+    /**
+     * Discrete Sine Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; IDST (excluding scale)
+     *          S[k] = sum_j=1&circ;n A[j]*sin(pi*j*(k+1/2)/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt; DST
+     *          S[k] = sum_j=0&circ;n-1 a[j]*sin(pi*(j+1/2)*k/n), 0&lt;k&lt;=n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          ddst(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          ddst(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          ddst(n, -1, a, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          ddst(n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n data length (int)
+     *                      n &gt;= 2, n = power of 2
+     * @param isgn
+     * @param a [0...n-1] input/output data (REAL *)
+     *                      &lt;case1&gt;
+     *                          input data
+     *                              a[j] = A[j], 0&lt;j&lt;n
+     *                              a[0] = A[n]
+     *                          output data
+     *                              a[k] = S[k], 0&lt;=k&lt;n
+     *                      &lt;case2&gt;
+     *                          output data
+     *                              a[k] = S[k], 0&lt;k&lt;n
+     *                              a[0] = S[n]
+     * @param ip [0...*] work area for bit reversal (int *)
+     *                      length of ip &gt;= 2+sqrt(n/2)
+     *                      strictly,
+     *                      length of ip &gt;=
+     *                          2+(1&lt;&lt;(int)(log(n/2+0.5)/log(2))/2).
+     *                      ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w [0...n*5/4-1] cos/sin table (REAL *)
+     *                      w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void ddst(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int j, nw, nc;
+        double xr;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > nc) {
+            nc = n;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn < 0) {
+            xr = a[n - 1];
+            for (j = n - 2; j >= 2; j -= 2) {
+                a[j + 1] = -a[j] - a[j - 1];
+                a[j] -= a[j - 1];
+            }
+            a[1] = a[0] + xr;
+            a[0] -= xr;
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+        dstsub(n, a, nc, w, nw);
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xr = a[0] - a[1];
+            a[0] += a[1];
+            for (j = 2; j < n; j += 2) {
+                a[j - 1] = -a[j] - a[j + 1];
+                a[j] -= a[j + 1];
+            }
+            a[n - 1] = -xr;
+        }
+    }
+
+    /**
+     * Cosine Transform of RDFT (Real Symmetric DFT).
+     * <pre>
+     *  [definition]
+     *      C[k] = sum_j=0&circ;n a[j]*cos(pi*j*k/n), 0&lt;=k&lt;=n
+     *  [usage]
+     *      ip[0] = 0; // first time only
+     *      dfct(n, a, t, ip, w);
+     *  [parameters]
+     *  [remark]
+     *      Inverse of
+     *          a[0] *= 0.5;
+     *          a[n] *= 0.5;
+     *          dfct(n, a, t, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          a[n] *= 0.5;
+     *          dfct(n, a, t, ip, w);
+     *          for (j = 0; j &lt;= n; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n data length - 1 (int)
+     * <pre>
+     *  n &gt;= 2, n = power of 2
+     * </pre>
+     * @param a [0...n] input/output data (REAL *)
+     * <pre>
+     *  output data
+     *      a[k] = C[k], 0&lt;=k&lt;=n
+     * </pre>
+     * @param t [0...n/2] work area (REAL *)
+     * @param ip [0...*] work area for bit reversal (int *)
+     * <pre>
+     *  length of ip &gt;= 2+sqrt(n/4)
+     *  strictly,
+     *  length of ip &gt;=
+     *      2+(1&lt;&lt;(int)(log(n/4+0.5)/log(2))/2).
+     *  ip[0],ip[1] are pointers of the cos/sin table.
+     * </pre>
+     * @param w [0...n*5/8-1] cos/sin table (REAL *)
+     * <pre>
+     *  w[],ip[] are initialized if ip[0] == 0.
+     * </pre>
+     */
+    public void dfct(int n, double[] a, double[] t, int[] ip, double[] w) {
+        int j, k, l, m, mh, nw, nc;
+        double xr, xi, yr, yi;
+
+        nw = ip[0];
+        if (n > (nw << 3)) {
+            nw = n >> 3;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 1)) {
+            nc = n >> 1;
+            makect(nc, ip, w, nw);
+        }
+        m = n >> 1;
+        yi = a[m];
+        xi = a[0] + a[n];
+        a[0] -= a[n];
+        t[0] = xi - yi;
+        t[m] = xi + yi;
+        if (n > 2) {
+            mh = m >> 1;
+            for (j = 1; j < mh; j++) {
+                k = m - j;
+                xr = a[j] - a[n - j];
+                xi = a[j] + a[n - j];
+                yr = a[k] - a[n - k];
+                yi = a[k] + a[n - k];
+                a[j] = xr;
+                a[k] = yr;
+                t[j] = xi - yi;
+                t[k] = xi + yi;
+            }
+            t[mh] = a[mh] + a[n - mh];
+            a[mh] -= a[n - mh];
+            dctsub(m, a, nc, w, nw);
+            if (m > 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+                rftfsub(m, a, nc, w, nw);
+            } else if (m == 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+            }
+            a[n - 1] = a[0] - a[1];
+            a[1] = a[0] + a[1];
+            for (j = m - 2; j >= 2; j -= 2) {
+                a[2 * j + 1] = a[j] + a[j + 1];
+                a[2 * j - 1] = a[j] - a[j + 1];
+            }
+            l = 2;
+            m = mh;
+            while (m >= 2) {
+                dctsub(m, t, nc, w, nw);
+                if (m > 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                    rftfsub(m, t, nc, w, nw);
+                } else if (m == 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                }
+                a[n - l] = t[0] - t[1];
+                a[l] = t[0] + t[1];
+                k = 0;
+                for (j = 2; j < m; j += 2) {
+                    k += l << 2;
+                    a[k - l] = t[j] - t[j + 1];
+                    a[k + l] = t[j] + t[j + 1];
+                }
+                l <<= 1;
+                mh = m >> 1;
+                for (j = 0; j < mh; j++) {
+                    k = m - j;
+                    t[j] = t[m + k] - t[m + j];
+                    t[k] = t[m + k] + t[m + j];
+                }
+                t[mh] = t[m + mh];
+                m = mh;
+            }
+            a[l] = t[0];
+            a[n] = t[2] - t[1];
+            a[0] = t[2] + t[1];
+        } else {
+            a[1] = a[0];
+            a[2] = t[0];
+            a[0] = t[1];
+        }
+    }
+
+    /**
+     * Sine Transform of RDFT (Real Anti-symmetric DFT).
+     * <pre>
+     *  [definition]
+     *      S[k] = sum_j=1&amp;circ;n-1 a[j]*sin(pi*j*k/n), 0&lt;k&lt;n
+     *  [usage]
+     *      ip[0] = 0; // first time only
+     *      dfst(n, a, t, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          dfst(n, a, t, ip, w);
+     *      is
+     *          dfst(n, a, t, ip, w);
+     *          for (j = 1; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     * @param n data length + 1 (int)
+     * <pre>
+     *  n &gt;= 2, n = power of 2
+     * </pre>
+     * @param a [0...n-1] input/output data (REAL *)
+     * <pre>
+     *  output data
+     *      a[k] = S[k], 0&lt;k&lt;n
+     *      (a[0] is used for work area)
+     * </pre>
+     * @param t [0...n/2-1] work area (REAL *)
+     * @param ip [0...*] work area for bit reversal (int *)
+     * <pre>
+     *  length of ip &gt;= 2+sqrt(n/4)
+     *  strictly,
+     *  length of ip &gt;=
+     *      2+(1&lt;&lt;(int)(log(n/4+0.5)/log(2))/2).
+     *  ip[0],ip[1] are pointers of the cos/sin table.
+     * </pre>
+     * @param w [0...n*5/8-1] cos/sin table (REAL *)
+     * <pre>
+     *  w[],ip[] are initialized if ip[0] == 0.
+     * </pre>
+     */
+    public void dfst(int n, double[] a, double[] t, int[] ip, double[] w) {
+        int j, k, l, m, mh, nw, nc;
+        double xr, xi, yr, yi;
+
+        nw = ip[0];
+        if (n > (nw << 3)) {
+            nw = n >> 3;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 1)) {
+            nc = n >> 1;
+            makect(nc, ip, w, nw);
+        }
+        if (n > 2) {
+            m = n >> 1;
+            mh = m >> 1;
+            for (j = 1; j < mh; j++) {
+                k = m - j;
+                xr = a[j] + a[n - j];
+                xi = a[j] - a[n - j];
+                yr = a[k] + a[n - k];
+                yi = a[k] - a[n - k];
+                a[j] = xr;
+                a[k] = yr;
+                t[j] = xi + yi;
+                t[k] = xi - yi;
+            }
+            t[0] = a[mh] - a[n - mh];
+            a[mh] += a[n - mh];
+            a[0] = a[m];
+            dstsub(m, a, nc, w, nw);
+            if (m > 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+                rftfsub(m, a, nc, w, nw);
+            } else if (m == 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+            }
+            a[n - 1] = a[1] - a[0];
+            a[1] = a[0] + a[1];
+            for (j = m - 2; j >= 2; j -= 2) {
+                a[2 * j + 1] = a[j] - a[j + 1];
+                a[2 * j - 1] = -a[j] - a[j + 1];
+            }
+            l = 2;
+            m = mh;
+            while (m >= 2) {
+                dstsub(m, t, nc, w, nw);
+                if (m > 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                    rftfsub(m, t, nc, w, nw);
+                } else if (m == 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                }
+                a[n - l] = t[1] - t[0];
+                a[l] = t[0] + t[1];
+                k = 0;
+                for (j = 2; j < m; j += 2) {
+                    k += l << 2;
+                    a[k - l] = -t[j] - t[j + 1];
+                    a[k + l] = t[j] - t[j + 1];
+                }
+                l <<= 1;
+                mh = m >> 1;
+                for (j = 1; j < mh; j++) {
+                    k = m - j;
+                    t[j] = t[m + k] + t[m + j];
+                    t[k] = t[m + k] - t[m + j];
+                }
+                t[0] = t[m + mh];
+                m = mh;
+            }
+            a[l] = t[0];
+        }
+        a[0] = 0;
+    }
+
+    // -------- initializing routines --------
+
+    /** */
+    private void makewt(int nw, int[] ip, double[] w) {
+        int j, nwh, nw0, nw1;
+        double delta, wn4r, wk1r, wk1i, wk3r, wk3i;
+
+        ip[0] = nw;
+        ip[1] = 1;
+        if (nw > 2) {
+            nwh = nw >> 1;
+//          delta = Math.atan(1.0) / nwh;
+            delta = Math.PI / 4 / nwh;
+            wn4r = Math.cos(delta * nwh);
+            w[0] = 1;
+            w[1] = wn4r;
+            if (nwh >= 4) {
+                w[2] = 0.5 / Math.cos(delta * 2);
+                w[3] = 0.5 / Math.cos(delta * 6);
+            }
+            for (j = 4; j < nwh; j += 4) {
+                w[j] = Math.cos(delta * j);
+                w[j + 1] = Math.sin(delta * j);
+                w[j + 2] = Math.cos(3 * delta * j);
+                w[j + 3] = Math.sin(3 * delta * j);
+            }
+            nw0 = 0;
+            while (nwh > 2) {
+                nw1 = nw0 + nwh;
+                nwh >>= 1;
+                w[nw1] = 1;
+                w[nw1 + 1] = wn4r;
+                if (nwh >= 4) {
+                    wk1r = w[nw0 + 4];
+                    wk3r = w[nw0 + 6];
+                    w[nw1 + 2] = 0.5 / wk1r;
+                    w[nw1 + 3] = 0.5 / wk3r;
+                }
+                for (j = 4; j < nwh; j += 4) {
+                    wk1r = w[nw0 + 2 * j];
+                    wk1i = w[nw0 + 2 * j + 1];
+                    wk3r = w[nw0 + 2 * j + 2];
+                    wk3i = w[nw0 + 2 * j + 3];
+                    w[nw1 + j] = wk1r;
+                    w[nw1 + j + 1] = wk1i;
+                    w[nw1 + j + 2] = wk3r;
+                    w[nw1 + j + 3] = wk3i;
+                }
+                nw0 = nw1;
+            }
+        }
+    }
+
+    /** */
+    private void makect(int nc, int[] ip, double[] c, int cP) {
+        int j, nch;
+        double delta;
+
+        ip[1] = nc;
+        if (nc > 1) {
+            nch = nc >> 1;
+//          delta = Math.atan(1.0) / nch;
+            delta = Math.PI / 4 / nch;
+            c[cP + 0] = Math.cos(delta * nch);
+            c[cP + nch] = 0.5 * c[cP + 0];
+            for (j = 1; j < nch; j++) {
+                c[cP + j] = 0.5 * Math.cos(delta * j);
+                c[cP + nc - j] = 0.5 * Math.sin(delta * j);
+            }
+        }
+    }
+
+    // -------- child routines --------
+
+    /**
+     * 2nd
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #cdft(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void cftfsub(int n, double[] a, int[] ip, int ipP, int nw, double[] w) {
+        int m;
+
+        if (n > 32) {
+            m = n >> 2;
+            cftf1st(n, a, w, nw - m);
+            if (n > CDFT_RECURSIVE_N) {
+                cftrec1(m, a, 0, nw, w);
+                cftrec2(m, a, m, nw, w);
+                cftrec1(m, a, 2 * m, nw, w);
+                cftrec1(m, a, 3 * m, nw, w);
+            } else if (m > 32) {
+                cftexp1(n, a, 0, nw, w);
+            } else {
+                cftfx41(n, a, 0, nw, w);
+            }
+            bitrv2(n, ip, ipP, a);
+        } else if (n > 8) {
+            if (n == 32) {
+                cftf161(a, 0, w, nw - 8);
+                bitrv216(a);
+            } else {
+                cftf081(a, 0, w, 0);
+                bitrv208(a);
+            }
+        } else if (n == 8) {
+            cftf040(a);
+        } else if (n == 4) {
+            cftx020(a);
+        }
+    }
+
+    /**
+     * 2nd
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #cdft(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     */
+    private void cftbsub(int n, double[] a, int[] ip, int ipP, int nw, double[] w) {
+        int m;
+
+        if (n > 32) {
+            m = n >> 2;
+            cftb1st(n, a, w, nw - m);
+            if (n > CDFT_RECURSIVE_N) {
+                cftrec1(m, a, 0, nw, w);
+                cftrec2(m, a, m, nw, w);
+                cftrec1(m, a, 2 * m, nw, w);
+                cftrec1(m, a, 3 * m, nw, w);
+            } else if (m > 32) {
+                cftexp1(n, a, 0, nw, w);
+            } else {
+                cftfx41(n, a, 0, nw, w);
+            }
+            bitrv2conj(n, ip, ipP, a);
+        } else if (n > 8) {
+            if (n == 32) {
+                cftf161(a, 0, w, nw - 8);
+                bitrv216neg(a);
+            } else {
+                cftf081(a, 0, w, 0);
+                bitrv208neg(a);
+            }
+        } else if (n == 8) {
+            cftb040(a);
+        } else if (n == 4) {
+            cftx020(a);
+        }
+    }
+
+    /**
+     * 3rd
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private final void bitrv2(int n, int[] ip, int ipP, double[] a) {
+        int j, j1, k, k1, l, m, m2;
+        double xr, xi, yr, yi;
+
+        ip[ipP + 0] = 0;
+        l = n;
+        m = 1;
+        while ((m << 3) < l) {
+            l >>= 1;
+            for (j = 0; j < m; j++) {
+                ip[ipP + m + j] = ip[ipP + j] + l;
+            }
+            m <<= 1;
+        }
+        m2 = 2 * m;
+        if ((m << 3) == l) {
+            for (k = 0; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 -= m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                j1 = 2 * k + m2 + ip[ipP + k];
+                k1 = j1 + m2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+        } else {
+            for (k = 1; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+            }
+        }
+    }
+
+    /**
+     * 3rd
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private final void bitrv2conj(int n, int[] ip, int ipP, double[] a) {
+        int j, j1, k, k1, l, m, m2;
+        double xr, xi, yr, yi;
+
+        ip[ipP + 0] = 0;
+        l = n;
+        m = 1;
+        while ((m << 3) < l) {
+            l >>= 1;
+            for (j = 0; j < m; j++) {
+                ip[ipP + m + j] = ip[ipP + j] + l;
+            }
+            m <<= 1;
+        }
+        m2 = 2 * m;
+        if ((m << 3) == l) {
+            for (k = 0; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 -= m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                k1 = 2 * k + ip[ipP + k];
+                a[k1 + 1] = -a[k1 + 1];
+                j1 = k1 + m2;
+                k1 = j1 + m2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                k1 += m2;
+                a[k1 + 1] = -a[k1 + 1];
+            }
+        } else {
+            a[1] = -a[1];
+            a[m2 + 1] = -a[m2 + 1];
+            for (k = 1; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                k1 = 2 * k + ip[ipP + k];
+                a[k1 + 1] = -a[k1 + 1];
+                a[k1 + m2 + 1] = -a[k1 + m2 + 1];
+            }
+        }
+    }
+
+    /**
+     * 3rd
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv216(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x7r = a[14];
+        x7i = a[15];
+        x8r = a[16];
+        x8i = a[17];
+        x10r = a[20];
+        x10i = a[21];
+        x11r = a[22];
+        x11i = a[23];
+        x12r = a[24];
+        x12i = a[25];
+        x13r = a[26];
+        x13i = a[27];
+        x14r = a[28];
+        x14i = a[29];
+        a[2] = x8r;
+        a[3] = x8i;
+        a[4] = x4r;
+        a[5] = x4i;
+        a[6] = x12r;
+        a[7] = x12i;
+        a[8] = x2r;
+        a[9] = x2i;
+        a[10] = x10r;
+        a[11] = x10i;
+        a[14] = x14r;
+        a[15] = x14i;
+        a[16] = x1r;
+        a[17] = x1i;
+        a[20] = x5r;
+        a[21] = x5i;
+        a[22] = x13r;
+        a[23] = x13i;
+        a[24] = x3r;
+        a[25] = x3i;
+        a[26] = x11r;
+        a[27] = x11i;
+        a[28] = x7r;
+        a[29] = x7i;
+    }
+
+    /**
+     * 3rd
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv216neg(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i, x15r, x15i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x6r = a[12];
+        x6i = a[13];
+        x7r = a[14];
+        x7i = a[15];
+        x8r = a[16];
+        x8i = a[17];
+        x9r = a[18];
+        x9i = a[19];
+        x10r = a[20];
+        x10i = a[21];
+        x11r = a[22];
+        x11i = a[23];
+        x12r = a[24];
+        x12i = a[25];
+        x13r = a[26];
+        x13i = a[27];
+        x14r = a[28];
+        x14i = a[29];
+        x15r = a[30];
+        x15i = a[31];
+        a[2] = x15r;
+        a[3] = x15i;
+        a[4] = x7r;
+        a[5] = x7i;
+        a[6] = x11r;
+        a[7] = x11i;
+        a[8] = x3r;
+        a[9] = x3i;
+        a[10] = x13r;
+        a[11] = x13i;
+        a[12] = x5r;
+        a[13] = x5i;
+        a[14] = x9r;
+        a[15] = x9i;
+        a[16] = x1r;
+        a[17] = x1i;
+        a[18] = x14r;
+        a[19] = x14i;
+        a[20] = x6r;
+        a[21] = x6i;
+        a[22] = x10r;
+        a[23] = x10i;
+        a[24] = x2r;
+        a[25] = x2i;
+        a[26] = x12r;
+        a[27] = x12i;
+        a[28] = x4r;
+        a[29] = x4i;
+        a[30] = x8r;
+        a[31] = x8i;
+    }
+
+    /**
+     * 3rd
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv208(double[] a) {
+        double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x6r = a[12];
+        x6i = a[13];
+        a[2] = x4r;
+        a[3] = x4i;
+        a[6] = x6r;
+        a[7] = x6i;
+        a[8] = x1r;
+        a[9] = x1i;
+        a[12] = x3r;
+        a[13] = x3i;
+    }
+
+    /**
+     * 3rd
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv208neg(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x6r = a[12];
+        x6i = a[13];
+        x7r = a[14];
+        x7i = a[15];
+        a[2] = x7r;
+        a[3] = x7i;
+        a[4] = x3r;
+        a[5] = x3i;
+        a[6] = x5r;
+        a[7] = x5i;
+        a[8] = x1r;
+        a[9] = x1i;
+        a[10] = x6r;
+        a[11] = x6i;
+        a[12] = x2r;
+        a[13] = x2i;
+        a[14] = x4r;
+        a[15] = x4i;
+    }
+
+    /**
+     * 3rd
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftf1st(int n, double[] a, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[0] + a[j2];
+        x0i = a[1] + a[j2 + 1];
+        x1r = a[0] - a[j2];
+        x1i = a[1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        a[j2] = x1r - x3i;
+        a[j2 + 1] = x1i + x3r;
+        a[j3] = x1r + x3i;
+        a[j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        csc1 = w[wP + 2];
+        csc3 = w[wP + 3];
+        wd1r = 1;
+        wd1i = 0;
+        wd3r = 1;
+        wd3i = 0;
+        k = 0;
+        for (j = 2; j < mh - 2; j += 4) {
+            k += 4;
+            wk1r = csc1 * (wd1r + w[wP + k]);
+            wk1i = csc1 * (wd1i + w[wP + k + 1]);
+            wk3r = csc3 * (wd3r + w[wP + k + 2]);
+            wk3i = csc3 * (wd3i - w[wP + k + 3]);
+            wd1r = w[wP + k];
+            wd1i = w[wP + k + 1];
+            wd3r = w[wP + k + 2];
+            wd3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j] + a[j2];
+            x0i = a[j + 1] + a[j2 + 1];
+            x1r = a[j] - a[j2];
+            x1i = a[j + 1] - a[j2 + 1];
+            y0r = a[j + 2] + a[j2 + 2];
+            y0i = a[j + 3] + a[j2 + 3];
+            y1r = a[j + 2] - a[j2 + 2];
+            y1i = a[j + 3] - a[j2 + 3];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 + 2] + a[j3 + 2];
+            y2i = a[j1 + 3] + a[j3 + 3];
+            y3r = a[j1 + 2] - a[j3 + 2];
+            y3i = a[j1 + 3] - a[j3 + 3];
+            a[j] = x0r + x2r;
+            a[j + 1] = x0i + x2i;
+            a[j + 2] = y0r + y2r;
+            a[j + 3] = y0i + y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i - x2i;
+            a[j1 + 2] = y0r - y2r;
+            a[j1 + 3] = y0i - y2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1r * x0r - wk1i * x0i;
+            a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i + y3r;
+            a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+            a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3r * x0r + wk3i * x0i;
+            a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i - y3r;
+            a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+            a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j0] + a[j2];
+            x0i = a[j0 + 1] + a[j2 + 1];
+            x1r = a[j0] - a[j2];
+            x1i = a[j0 + 1] - a[j2 + 1];
+            y0r = a[j0 - 2] + a[j2 - 2];
+            y0i = a[j0 - 1] + a[j2 - 1];
+            y1r = a[j0 - 2] - a[j2 - 2];
+            y1i = a[j0 - 1] - a[j2 - 1];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 - 2] + a[j3 - 2];
+            y2i = a[j1 - 1] + a[j3 - 1];
+            y3r = a[j1 - 2] - a[j3 - 2];
+            y3i = a[j1 - 1] - a[j3 - 1];
+            a[j0] = x0r + x2r;
+            a[j0 + 1] = x0i + x2i;
+            a[j0 - 2] = y0r + y2r;
+            a[j0 - 1] = y0i + y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i - x2i;
+            a[j1 - 2] = y0r - y2r;
+            a[j1 - 1] = y0i - y2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1i * x0r - wk1r * x0i;
+            a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i + y3r;
+            a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+            a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3i * x0r + wk3r * x0i;
+            a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i - y3r;
+            a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+            a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+        }
+        wk1r = csc1 * (wd1r + wn4r);
+        wk1i = csc1 * (wd1i + wn4r);
+        wk3r = csc3 * (wd3r - wn4r);
+        wk3i = csc3 * (wd3i - wn4r);
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0 - 2] + a[j2 - 2];
+        x0i = a[j0 - 1] + a[j2 - 1];
+        x1r = a[j0 - 2] - a[j2 - 2];
+        x1i = a[j0 - 1] - a[j2 - 1];
+        x2r = a[j1 - 2] + a[j3 - 2];
+        x2i = a[j1 - 1] + a[j3 - 1];
+        x3r = a[j1 - 2] - a[j3 - 2];
+        x3i = a[j1 - 1] - a[j3 - 1];
+        a[j0 - 2] = x0r + x2r;
+        a[j0 - 1] = x0i + x2i;
+        a[j1 - 2] = x0r - x2r;
+        a[j1 - 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+        a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+        a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+        x0r = a[j0] + a[j2];
+        x0i = a[j0 + 1] + a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = a[j0 + 1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wn4r * (x0r - x0i);
+        a[j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = -wn4r * (x0r + x0i);
+        a[j3 + 1] = -wn4r * (x0i - x0r);
+        x0r = a[j0 + 2] + a[j2 + 2];
+        x0i = a[j0 + 3] + a[j2 + 3];
+        x1r = a[j0 + 2] - a[j2 + 2];
+        x1i = a[j0 + 3] - a[j2 + 3];
+        x2r = a[j1 + 2] + a[j3 + 2];
+        x2i = a[j1 + 3] + a[j3 + 3];
+        x3r = a[j1 + 2] - a[j3 + 2];
+        x3i = a[j1 + 3] - a[j3 + 3];
+        a[j0 + 2] = x0r + x2r;
+        a[j0 + 3] = x0i + x2i;
+        a[j1 + 2] = x0r - x2r;
+        a[j1 + 3] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+    }
+
+    /**
+     * 3rd
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private final void cftb1st(int n, double[] a, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[0] + a[j2];
+        x0i = -a[1] - a[j2 + 1];
+        x1r = a[0] - a[j2];
+        x1i = -a[1] + a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[0] = x0r + x2r;
+        a[1] = x0i - x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        a[j2] = x1r + x3i;
+        a[j2 + 1] = x1i + x3r;
+        a[j3] = x1r - x3i;
+        a[j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        csc1 = w[wP + 2];
+        csc3 = w[wP + 3];
+        wd1r = 1;
+        wd1i = 0;
+        wd3r = 1;
+        wd3i = 0;
+        k = 0;
+        for (j = 2; j < mh - 2; j += 4) {
+            k += 4;
+            wk1r = csc1 * (wd1r + w[wP + k]);
+            wk1i = csc1 * (wd1i + w[wP + k + 1]);
+            wk3r = csc3 * (wd3r + w[wP + k + 2]);
+            wk3i = csc3 * (wd3i - w[wP + k + 3]);
+            wd1r = w[wP + k];
+            wd1i = w[wP + k + 1];
+            wd3r = w[wP + k + 2];
+            wd3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j] + a[j2];
+            x0i = -a[j + 1] - a[j2 + 1];
+            x1r = a[j] - a[j2];
+            x1i = -a[j + 1] + a[j2 + 1];
+            y0r = a[j + 2] + a[j2 + 2];
+            y0i = -a[j + 3] - a[j2 + 3];
+            y1r = a[j + 2] - a[j2 + 2];
+            y1i = -a[j + 3] + a[j2 + 3];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 + 2] + a[j3 + 2];
+            y2i = a[j1 + 3] + a[j3 + 3];
+            y3r = a[j1 + 2] - a[j3 + 2];
+            y3i = a[j1 + 3] - a[j3 + 3];
+            a[j] = x0r + x2r;
+            a[j + 1] = x0i - x2i;
+            a[j + 2] = y0r + y2r;
+            a[j + 3] = y0i - y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i + x2i;
+            a[j1 + 2] = y0r - y2r;
+            a[j1 + 3] = y0i + y2i;
+            x0r = x1r + x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1r * x0r - wk1i * x0i;
+            a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i + y3r;
+            a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+            a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+            x0r = x1r - x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3r * x0r + wk3i * x0i;
+            a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i - y3r;
+            a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+            a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j0] + a[j2];
+            x0i = -a[j0 + 1] - a[j2 + 1];
+            x1r = a[j0] - a[j2];
+            x1i = -a[j0 + 1] + a[j2 + 1];
+            y0r = a[j0 - 2] + a[j2 - 2];
+            y0i = -a[j0 - 1] - a[j2 - 1];
+            y1r = a[j0 - 2] - a[j2 - 2];
+            y1i = -a[j0 - 1] + a[j2 - 1];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 - 2] + a[j3 - 2];
+            y2i = a[j1 - 1] + a[j3 - 1];
+            y3r = a[j1 - 2] - a[j3 - 2];
+            y3i = a[j1 - 1] - a[j3 - 1];
+            a[j0] = x0r + x2r;
+            a[j0 + 1] = x0i - x2i;
+            a[j0 - 2] = y0r + y2r;
+            a[j0 - 1] = y0i - y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i + x2i;
+            a[j1 - 2] = y0r - y2r;
+            a[j1 - 1] = y0i + y2i;
+            x0r = x1r + x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1i * x0r - wk1r * x0i;
+            a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i + y3r;
+            a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+            a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+            x0r = x1r - x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3i * x0r + wk3r * x0i;
+            a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i - y3r;
+            a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+            a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+        }
+        wk1r = csc1 * (wd1r + wn4r);
+        wk1i = csc1 * (wd1i + wn4r);
+        wk3r = csc3 * (wd3r - wn4r);
+        wk3i = csc3 * (wd3i - wn4r);
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0 - 2] + a[j2 - 2];
+        x0i = -a[j0 - 1] - a[j2 - 1];
+        x1r = a[j0 - 2] - a[j2 - 2];
+        x1i = -a[j0 - 1] + a[j2 - 1];
+        x2r = a[j1 - 2] + a[j3 - 2];
+        x2i = a[j1 - 1] + a[j3 - 1];
+        x3r = a[j1 - 2] - a[j3 - 2];
+        x3i = a[j1 - 1] - a[j3 - 1];
+        a[j0 - 2] = x0r + x2r;
+        a[j0 - 1] = x0i - x2i;
+        a[j1 - 2] = x0r - x2r;
+        a[j1 - 1] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+        a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+        a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+        x0r = a[j0] + a[j2];
+        x0i = -a[j0 + 1] - a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = -a[j0 + 1] + a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i - x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2] = wn4r * (x0r - x0i);
+        a[j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3] = -wn4r * (x0r + x0i);
+        a[j3 + 1] = -wn4r * (x0i - x0r);
+        x0r = a[j0 + 2] + a[j2 + 2];
+        x0i = -a[j0 + 3] - a[j2 + 3];
+        x1r = a[j0 + 2] - a[j2 + 2];
+        x1i = -a[j0 + 3] + a[j2 + 3];
+        x2r = a[j1 + 2] + a[j3 + 2];
+        x2i = a[j1 + 3] + a[j3 + 3];
+        x3r = a[j1 + 2] - a[j3 + 2];
+        x3i = a[j1 + 3] - a[j3 + 3];
+        a[j0 + 2] = x0r + x2r;
+        a[j0 + 3] = x0i - x2i;
+        a[j1 + 2] = x0r - x2r;
+        a[j1 + 3] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+    }
+
+    /** */
+    private void cftrec1(int n, double[] a, int aP, int nw, double[] w) {
+        int m;
+
+        m = n >> 2;
+        cftmdl1(n, a, aP, w, nw - 2 * m);
+        if (n > CDFT_RECURSIVE_N) {
+            cftrec1(m, a, aP, nw, w);
+            cftrec2(m, a, aP + m, nw, w);
+            cftrec1(m, a, aP + 2 * m, nw, w);
+            cftrec1(m, a, aP + 3 * m, nw, w);
+        } else {
+            cftexp1(n, a, aP, nw, w);
+        }
+    }
+
+    /** */
+    private void cftrec2(int n, double[] a, int aP, int nw, double[] w) {
+        int m;
+
+        m = n >> 2;
+        cftmdl2(n, a, aP, w, nw - n);
+        if (n > CDFT_RECURSIVE_N) {
+            cftrec1(m, a, aP, nw, w);
+            cftrec2(m, a, aP + m, nw, w);
+            cftrec1(m, a, aP + 2 * m, nw, w);
+            cftrec2(m, a, aP + 3 * m, nw, w);
+        } else {
+            cftexp2(n, a, aP, nw, w);
+        }
+    }
+
+    /** */
+    private void cftexp1(int n, double[] a, int aP, int nw, double[] w) {
+        int j, k, l;
+
+        l = n >> 2;
+        while (l > 128) {
+            for (k = l; k < n; k <<= 2) {
+                for (j = k - l; j < n; j += 4 * k) {
+                    cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                    cftmdl2(l, a, aP + k + j, w, nw - l);
+                    cftmdl1(l, a, aP + 2 * k + j, w, nw - (l >> 1));
+                }
+            }
+            cftmdl1(l, a, aP + n - l, w, nw - (l >> 1));
+            l >>= 2;
+        }
+        for (k = l; k < n; k <<= 2) {
+            for (j = k - l; j < n; j += 4 * k) {
+                cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + j, nw, w);
+                cftmdl2(l, a, aP + k + j, w, nw - l);
+                cftfx42(l, a, aP + k + j, nw, w);
+                cftmdl1(l, a, aP + 2 * k + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + 2 * k + j, nw, w);
+            }
+        }
+        cftmdl1(l, a, aP + n - l, w, nw - (l >> 1));
+        cftfx41(l, a, aP + n - l, nw, w);
+    }
+
+    /** */
+    private void cftexp2(int n, double[] a, int aP, int nw, double[] w) {
+        int j, k, l, m;
+
+        m = n >> 1;
+        l = n >> 2;
+        while (l > 128) {
+            for (k = l; k < m; k <<= 2) {
+                for (j = k - l; j < m; j += 2 * k) {
+                    cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                    cftmdl1(l, a, aP + m + j, w, nw - (l >> 1));
+                }
+                for (j = 2 * k - l; j < m; j += 4 * k) {
+                    cftmdl2(l, a, aP + j, w, nw - l);
+                    cftmdl2(l, a, aP + m + j, w, nw - l);
+                }
+            }
+            l >>= 2;
+        }
+        for (k = l; k < m; k <<= 2) {
+            for (j = k - l; j < m; j += 2 * k) {
+                cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + j, nw, w);
+                cftmdl1(l, a, aP + m + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + m + j, nw, w);
+            }
+            for (j = 2 * k - l; j < m; j += 4 * k) {
+                cftmdl2(l, a, aP + j, w, nw - l);
+                cftfx42(l, a, aP + j, nw, w);
+                cftmdl2(l, a, aP + m + j, w, nw - l);
+                cftfx42(l, a, aP + m + j, nw, w);
+            }
+        }
+    }
+
+    /** */
+    private final void cftmdl1(int n, double[] a, int aP, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, wk1r, wk1i, wk3r, wk3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + 0] + a[aP + j2];
+        x0i = a[aP + 1] + a[aP + j2 + 1];
+        x1r = a[aP + 0] - a[aP + j2];
+        x1i = a[aP + 1] - a[aP + j2 + 1];
+        x2r = a[aP + j1] + a[aP + j3];
+        x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+        x3r = a[aP + j1] - a[aP + j3];
+        x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+        a[aP + 0] = x0r + x2r;
+        a[aP + 1] = x0i + x2i;
+        a[aP + j1] = x0r - x2r;
+        a[aP + j1 + 1] = x0i - x2i;
+        a[aP + j2] = x1r - x3i;
+        a[aP + j2 + 1] = x1i + x3r;
+        a[aP + j3] = x1r + x3i;
+        a[aP + j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        k = 0;
+        for (j = 2; j < mh; j += 2) {
+            k += 4;
+            wk1r = w[wP + k];
+            wk1i = w[wP + k + 1];
+            wk3r = w[wP + k + 2];
+            wk3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j] + a[aP + j2];
+            x0i = a[aP + j + 1] + a[aP + j2 + 1];
+            x1r = a[aP + j] - a[aP + j2];
+            x1i = a[aP + j + 1] - a[aP + j2 + 1];
+            x2r = a[aP + j1] + a[aP + j3];
+            x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+            x3r = a[aP + j1] - a[aP + j3];
+            x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+            a[aP + j] = x0r + x2r;
+            a[aP + j + 1] = x0i + x2i;
+            a[aP + j1] = x0r - x2r;
+            a[aP + j1 + 1] = x0i - x2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[aP + j2] = wk1r * x0r - wk1i * x0i;
+            a[aP + j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[aP + j3] = wk3r * x0r + wk3i * x0i;
+            a[aP + j3 + 1] = wk3r * x0i - wk3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j0] + a[aP + j2];
+            x0i = a[aP + j0 + 1] + a[aP + j2 + 1];
+            x1r = a[aP + j0] - a[aP + j2];
+            x1i = a[aP + j0 + 1] - a[aP + j2 + 1];
+            x2r = a[aP + j1] + a[aP + j3];
+            x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+            x3r = a[aP + j1] - a[aP + j3];
+            x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+            a[aP + j0] = x0r + x2r;
+            a[aP + j0 + 1] = x0i + x2i;
+            a[aP + j1] = x0r - x2r;
+            a[aP + j1 + 1] = x0i - x2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[aP + j2] = wk1i * x0r - wk1r * x0i;
+            a[aP + j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[aP + j3] = wk3i * x0r + wk3r * x0i;
+            a[aP + j3 + 1] = wk3i * x0i - wk3r * x0r;
+        }
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + j0] + a[aP + j2];
+        x0i = a[aP + j0 + 1] + a[aP + j2 + 1];
+        x1r = a[aP + j0] - a[aP + j2];
+        x1i = a[aP + j0 + 1] - a[aP + j2 + 1];
+        x2r = a[aP + j1] + a[aP + j3];
+        x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+        x3r = a[aP + j1] - a[aP + j3];
+        x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+        a[aP + j0] = x0r + x2r;
+        a[aP + j0 + 1] = x0i + x2i;
+        a[aP + j1] = x0r - x2r;
+        a[aP + j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[aP + j2] = wn4r * (x0r - x0i);
+        a[aP + j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[aP + j3] = -wn4r * (x0r + x0i);
+        a[aP + j3 + 1] = -wn4r * (x0i - x0r);
+    }
+
+    /** */
+    private final void cftmdl2(int n, double[] a, int aP, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, kr, m, mh;
+        double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        wn4r = w[wP + 1];
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + 0] - a[aP + j2 + 1];
+        x0i = a[aP + 1] + a[aP + j2];
+        x1r = a[aP + 0] + a[aP + j2 + 1];
+        x1i = a[aP + 1] - a[aP + j2];
+        x2r = a[aP + j1] - a[aP + j3 + 1];
+        x2i = a[aP + j1 + 1] + a[aP + j3];
+        x3r = a[aP + j1] + a[aP + j3 + 1];
+        x3i = a[aP + j1 + 1] - a[aP + j3];
+        y0r = wn4r * (x2r - x2i);
+        y0i = wn4r * (x2i + x2r);
+        a[aP + 0] = x0r + y0r;
+        a[aP + 1] = x0i + y0i;
+        a[aP + j1] = x0r - y0r;
+        a[aP + j1 + 1] = x0i - y0i;
+        y0r = wn4r * (x3r - x3i);
+        y0i = wn4r * (x3i + x3r);
+        a[aP + j2] = x1r - y0i;
+        a[aP + j2 + 1] = x1i + y0r;
+        a[aP + j3] = x1r + y0i;
+        a[aP + j3 + 1] = x1i - y0r;
+        k = 0;
+        kr = 2 * m;
+        for (j = 2; j < mh; j += 2) {
+            k += 4;
+            wk1r = w[wP + k];
+            wk1i = w[wP + k + 1];
+            wk3r = w[wP + k + 2];
+            wk3i = -w[wP + k + 3];
+            kr -= 4;
+            wd1i = w[wP + kr];
+            wd1r = w[wP + kr + 1];
+            wd3i = w[wP + kr + 2];
+            wd3r = -w[wP + kr + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j] - a[aP + j2 + 1];
+            x0i = a[aP + j + 1] + a[aP + j2];
+            x1r = a[aP + j] + a[aP + j2 + 1];
+            x1i = a[aP + j + 1] - a[aP + j2];
+            x2r = a[aP + j1] - a[aP + j3 + 1];
+            x2i = a[aP + j1 + 1] + a[aP + j3];
+            x3r = a[aP + j1] + a[aP + j3 + 1];
+            x3i = a[aP + j1 + 1] - a[aP + j3];
+            y0r = wk1r * x0r - wk1i * x0i;
+            y0i = wk1r * x0i + wk1i * x0r;
+            y2r = wd1r * x2r - wd1i * x2i;
+            y2i = wd1r * x2i + wd1i * x2r;
+            a[aP + j] = y0r + y2r;
+            a[aP + j + 1] = y0i + y2i;
+            a[aP + j1] = y0r - y2r;
+            a[aP + j1 + 1] = y0i - y2i;
+            y0r = wk3r * x1r + wk3i * x1i;
+            y0i = wk3r * x1i - wk3i * x1r;
+            y2r = wd3r * x3r + wd3i * x3i;
+            y2i = wd3r * x3i - wd3i * x3r;
+            a[aP + j2] = y0r + y2r;
+            a[aP + j2 + 1] = y0i + y2i;
+            a[aP + j3] = y0r - y2r;
+            a[aP + j3 + 1] = y0i - y2i;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j0] - a[aP + j2 + 1];
+            x0i = a[aP + j0 + 1] + a[aP + j2];
+            x1r = a[aP + j0] + a[aP + j2 + 1];
+            x1i = a[aP + j0 + 1] - a[aP + j2];
+            x2r = a[aP + j1] - a[aP + j3 + 1];
+            x2i = a[aP + j1 + 1] + a[aP + j3];
+            x3r = a[aP + j1] + a[aP + j3 + 1];
+            x3i = a[aP + j1 + 1] - a[aP + j3];
+            y0r = wd1i * x0r - wd1r * x0i;
+            y0i = wd1i * x0i + wd1r * x0r;
+            y2r = wk1i * x2r - wk1r * x2i;
+            y2i = wk1i * x2i + wk1r * x2r;
+            a[aP + j0] = y0r + y2r;
+            a[aP + j0 + 1] = y0i + y2i;
+            a[aP + j1] = y0r - y2r;
+            a[aP + j1 + 1] = y0i - y2i;
+            y0r = wd3i * x1r + wd3r * x1i;
+            y0i = wd3i * x1i - wd3r * x1r;
+            y2r = wk3i * x3r + wk3r * x3i;
+            y2i = wk3i * x3i - wk3r * x3r;
+            a[aP + j2] = y0r + y2r;
+            a[aP + j2 + 1] = y0i + y2i;
+            a[aP + j3] = y0r - y2r;
+            a[aP + j3 + 1] = y0i - y2i;
+        }
+        wk1r = w[wP + m];
+        wk1i = w[wP + m + 1];
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + j0] - a[aP + j2 + 1];
+        x0i = a[aP + j0 + 1] + a[aP + j2];
+        x1r = a[aP + j0] + a[aP + j2 + 1];
+        x1i = a[aP + j0 + 1] - a[aP + j2];
+        x2r = a[aP + j1] - a[aP + j3 + 1];
+        x2i = a[aP + j1 + 1] + a[aP + j3];
+        x3r = a[aP + j1] + a[aP + j3 + 1];
+        x3i = a[aP + j1 + 1] - a[aP + j3];
+        y0r = wk1r * x0r - wk1i * x0i;
+        y0i = wk1r * x0i + wk1i * x0r;
+        y2r = wk1i * x2r - wk1r * x2i;
+        y2i = wk1i * x2i + wk1r * x2r;
+        a[aP + j0] = y0r + y2r;
+        a[aP + j0 + 1] = y0i + y2i;
+        a[aP + j1] = y0r - y2r;
+        a[aP + j1 + 1] = y0i - y2i;
+        y0r = wk1i * x1r - wk1r * x1i;
+        y0i = wk1i * x1i + wk1r * x1r;
+        y2r = wk1r * x3r - wk1i * x3i;
+        y2i = wk1r * x3i + wk1i * x3r;
+        a[aP + j2] = y0r - y2r;
+        a[aP + j2 + 1] = y0i - y2i;
+        a[aP + j3] = y0r + y2r;
+        a[aP + j3 + 1] = y0i + y2i;
+    }
+
+    /** */
+    private void cftfx41(int n, double[] a, int aP, int nw, double[] w) {
+        if (n == 128) {
+            cftf161(a, aP, w, nw - 8);
+            cftf162(a, aP + 32, w, nw - 32);
+            cftf161(a, aP + 64, w, nw - 8);
+            cftf161(a, aP + 96, w, nw - 8);
+        } else {
+            cftf081(a, aP, w, nw - 16);
+            cftf082(a, aP + 16, w, nw - 16);
+            cftf081(a, aP + 32, w, nw - 16);
+            cftf081(a, aP + 48, w, nw - 16);
+        }
+    }
+
+    /** */
+    private void cftfx42(int n, double[] a, int aP, int nw, double[] w) {
+        if (n == 128) {
+            cftf161(a, aP, w, nw - 8);
+            cftf162(a, aP + 32, w, nw - 32);
+            cftf161(a, aP + 64, w, nw - 8);
+            cftf162(a, aP + 96, w, nw - 32);
+        } else {
+            cftf081(a, aP, w, nw - 16);
+            cftf082(a, aP + 16, w, nw - 16);
+            cftf081(a, aP + 32, w, nw - 16);
+            cftf082(a, aP + 48, w, nw - 16);
+        }
+    }
+
+    /** */
+    private void cftf161(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+        wn4r = w[wP + 1];
+        wk1i = wn4r * w[wP + 2];
+        wk1r = wk1i + w[wP + 2];
+        x0r = a[aP + 0] + a[aP + 16];
+        x0i = a[aP + 1] + a[aP + 17];
+        x1r = a[aP + 0] - a[aP + 16];
+        x1i = a[aP + 1] - a[aP + 17];
+        x2r = a[aP + 8] + a[aP + 24];
+        x2i = a[aP + 9] + a[aP + 25];
+        x3r = a[aP + 8] - a[aP + 24];
+        x3i = a[aP + 9] - a[aP + 25];
+        y0r = x0r + x2r;
+        y0i = x0i + x2i;
+        y4r = x0r - x2r;
+        y4i = x0i - x2i;
+        y8r = x1r - x3i;
+        y8i = x1i + x3r;
+        y12r = x1r + x3i;
+        y12i = x1i - x3r;
+        x0r = a[aP + 2] + a[aP + 18];
+        x0i = a[aP + 3] + a[aP + 19];
+        x1r = a[aP + 2] - a[aP + 18];
+        x1i = a[aP + 3] - a[aP + 19];
+        x2r = a[aP + 10] + a[aP + 26];
+        x2i = a[aP + 11] + a[aP + 27];
+        x3r = a[aP + 10] - a[aP + 26];
+        x3i = a[aP + 11] - a[aP + 27];
+        y1r = x0r + x2r;
+        y1i = x0i + x2i;
+        y5r = x0r - x2r;
+        y5i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y9r = wk1r * x0r - wk1i * x0i;
+        y9i = wk1r * x0i + wk1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y13r = wk1i * x0r - wk1r * x0i;
+        y13i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 4] + a[aP + 20];
+        x0i = a[aP + 5] + a[aP + 21];
+        x1r = a[aP + 4] - a[aP + 20];
+        x1i = a[aP + 5] - a[aP + 21];
+        x2r = a[aP + 12] + a[aP + 28];
+        x2i = a[aP + 13] + a[aP + 29];
+        x3r = a[aP + 12] - a[aP + 28];
+        x3i = a[aP + 13] - a[aP + 29];
+        y2r = x0r + x2r;
+        y2i = x0i + x2i;
+        y6r = x0r - x2r;
+        y6i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y10r = wn4r * (x0r - x0i);
+        y10i = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y14r = wn4r * (x0r + x0i);
+        y14i = wn4r * (x0i - x0r);
+        x0r = a[aP + 6] + a[aP + 22];
+        x0i = a[aP + 7] + a[aP + 23];
+        x1r = a[aP + 6] - a[aP + 22];
+        x1i = a[aP + 7] - a[aP + 23];
+        x2r = a[aP + 14] + a[aP + 30];
+        x2i = a[aP + 15] + a[aP + 31];
+        x3r = a[aP + 14] - a[aP + 30];
+        x3i = a[aP + 15] - a[aP + 31];
+        y3r = x0r + x2r;
+        y3i = x0i + x2i;
+        y7r = x0r - x2r;
+        y7i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y11r = wk1i * x0r - wk1r * x0i;
+        y11i = wk1i * x0i + wk1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y15r = wk1r * x0r - wk1i * x0i;
+        y15i = wk1r * x0i + wk1i * x0r;
+        x0r = y12r - y14r;
+        x0i = y12i - y14i;
+        x1r = y12r + y14r;
+        x1i = y12i + y14i;
+        x2r = y13r - y15r;
+        x2i = y13i - y15i;
+        x3r = y13r + y15r;
+        x3i = y13i + y15i;
+        a[aP + 24] = x0r + x2r;
+        a[aP + 25] = x0i + x2i;
+        a[aP + 26] = x0r - x2r;
+        a[aP + 27] = x0i - x2i;
+        a[aP + 28] = x1r - x3i;
+        a[aP + 29] = x1i + x3r;
+        a[aP + 30] = x1r + x3i;
+        a[aP + 31] = x1i - x3r;
+        x0r = y8r + y10r;
+        x0i = y8i + y10i;
+        x1r = y8r - y10r;
+        x1i = y8i - y10i;
+        x2r = y9r + y11r;
+        x2i = y9i + y11i;
+        x3r = y9r - y11r;
+        x3i = y9i - y11i;
+        a[aP + 16] = x0r + x2r;
+        a[aP + 17] = x0i + x2i;
+        a[aP + 18] = x0r - x2r;
+        a[aP + 19] = x0i - x2i;
+        a[aP + 20] = x1r - x3i;
+        a[aP + 21] = x1i + x3r;
+        a[aP + 22] = x1r + x3i;
+        a[aP + 23] = x1i - x3r;
+        x0r = y5r - y7i;
+        x0i = y5i + y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        x0r = y5r + y7i;
+        x0i = y5i - y7r;
+        x3r = wn4r * (x0r - x0i);
+        x3i = wn4r * (x0i + x0r);
+        x0r = y4r - y6i;
+        x0i = y4i + y6r;
+        x1r = y4r + y6i;
+        x1i = y4i - y6r;
+        a[aP + 8] = x0r + x2r;
+        a[aP + 9] = x0i + x2i;
+        a[aP + 10] = x0r - x2r;
+        a[aP + 11] = x0i - x2i;
+        a[aP + 12] = x1r - x3i;
+        a[aP + 13] = x1i + x3r;
+        a[aP + 14] = x1r + x3i;
+        a[aP + 15] = x1i - x3r;
+        x0r = y0r + y2r;
+        x0i = y0i + y2i;
+        x1r = y0r - y2r;
+        x1i = y0i - y2i;
+        x2r = y1r + y3r;
+        x2i = y1i + y3i;
+        x3r = y1r - y3r;
+        x3i = y1i - y3i;
+        a[aP + 0] = x0r + x2r;
+        a[aP + 1] = x0i + x2i;
+        a[aP + 2] = x0r - x2r;
+        a[aP + 3] = x0i - x2i;
+        a[aP + 4] = x1r - x3i;
+        a[aP + 5] = x1i + x3r;
+        a[aP + 6] = x1r + x3i;
+        a[aP + 7] = x1i - x3r;
+    }
+
+    /** */
+    private void cftf162(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, x0r, x0i, x1r, x1i, x2r, x2i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+        wn4r = w[wP + 1];
+        wk1r = w[wP + 4];
+        wk1i = w[wP + 5];
+        wk3r = w[wP + 6];
+        wk3i = w[wP + 7];
+        wk2r = w[wP + 8];
+        wk2i = w[wP + 9];
+        x1r = a[aP + 0] - a[aP + 17];
+        x1i = a[aP + 1] + a[aP + 16];
+        x0r = a[aP + 8] - a[aP + 25];
+        x0i = a[aP + 9] + a[aP + 24];
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        y0r = x1r + x2r;
+        y0i = x1i + x2i;
+        y4r = x1r - x2r;
+        y4i = x1i - x2i;
+        x1r = a[aP + 0] + a[aP + 17];
+        x1i = a[aP + 1] - a[aP + 16];
+        x0r = a[aP + 8] + a[aP + 25];
+        x0i = a[aP + 9] - a[aP + 24];
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        y8r = x1r - x2i;
+        y8i = x1i + x2r;
+        y12r = x1r + x2i;
+        y12i = x1i - x2r;
+        x0r = a[aP + 2] - a[aP + 19];
+        x0i = a[aP + 3] + a[aP + 18];
+        x1r = wk1r * x0r - wk1i * x0i;
+        x1i = wk1r * x0i + wk1i * x0r;
+        x0r = a[aP + 10] - a[aP + 27];
+        x0i = a[aP + 11] + a[aP + 26];
+        x2r = wk3i * x0r - wk3r * x0i;
+        x2i = wk3i * x0i + wk3r * x0r;
+        y1r = x1r + x2r;
+        y1i = x1i + x2i;
+        y5r = x1r - x2r;
+        y5i = x1i - x2i;
+        x0r = a[aP + 2] + a[aP + 19];
+        x0i = a[aP + 3] - a[aP + 18];
+        x1r = wk3r * x0r - wk3i * x0i;
+        x1i = wk3r * x0i + wk3i * x0r;
+        x0r = a[aP + 10] + a[aP + 27];
+        x0i = a[aP + 11] - a[aP + 26];
+        x2r = wk1r * x0r + wk1i * x0i;
+        x2i = wk1r * x0i - wk1i * x0r;
+        y9r = x1r - x2r;
+        y9i = x1i - x2i;
+        y13r = x1r + x2r;
+        y13i = x1i + x2i;
+        x0r = a[aP + 4] - a[aP + 21];
+        x0i = a[aP + 5] + a[aP + 20];
+        x1r = wk2r * x0r - wk2i * x0i;
+        x1i = wk2r * x0i + wk2i * x0r;
+        x0r = a[aP + 12] - a[aP + 29];
+        x0i = a[aP + 13] + a[aP + 28];
+        x2r = wk2i * x0r - wk2r * x0i;
+        x2i = wk2i * x0i + wk2r * x0r;
+        y2r = x1r + x2r;
+        y2i = x1i + x2i;
+        y6r = x1r - x2r;
+        y6i = x1i - x2i;
+        x0r = a[aP + 4] + a[aP + 21];
+        x0i = a[aP + 5] - a[aP + 20];
+        x1r = wk2i * x0r - wk2r * x0i;
+        x1i = wk2i * x0i + wk2r * x0r;
+        x0r = a[aP + 12] + a[aP + 29];
+        x0i = a[aP + 13] - a[aP + 28];
+        x2r = wk2r * x0r - wk2i * x0i;
+        x2i = wk2r * x0i + wk2i * x0r;
+        y10r = x1r - x2r;
+        y10i = x1i - x2i;
+        y14r = x1r + x2r;
+        y14i = x1i + x2i;
+        x0r = a[aP + 6] - a[aP + 23];
+        x0i = a[aP + 7] + a[aP + 22];
+        x1r = wk3r * x0r - wk3i * x0i;
+        x1i = wk3r * x0i + wk3i * x0r;
+        x0r = a[aP + 14] - a[aP + 31];
+        x0i = a[aP + 15] + a[aP + 30];
+        x2r = wk1i * x0r - wk1r * x0i;
+        x2i = wk1i * x0i + wk1r * x0r;
+        y3r = x1r + x2r;
+        y3i = x1i + x2i;
+        y7r = x1r - x2r;
+        y7i = x1i - x2i;
+        x0r = a[aP + 6] + a[aP + 23];
+        x0i = a[aP + 7] - a[aP + 22];
+        x1r = wk1i * x0r + wk1r * x0i;
+        x1i = wk1i * x0i - wk1r * x0r;
+        x0r = a[aP + 14] + a[aP + 31];
+        x0i = a[aP + 15] - a[aP + 30];
+        x2r = wk3i * x0r - wk3r * x0i;
+        x2i = wk3i * x0i + wk3r * x0r;
+        y11r = x1r + x2r;
+        y11i = x1i + x2i;
+        y15r = x1r - x2r;
+        y15i = x1i - x2i;
+        x1r = y0r + y2r;
+        x1i = y0i + y2i;
+        x2r = y1r + y3r;
+        x2i = y1i + y3i;
+        a[aP + 0] = x1r + x2r;
+        a[aP + 1] = x1i + x2i;
+        a[aP + 2] = x1r - x2r;
+        a[aP + 3] = x1i - x2i;
+        x1r = y0r - y2r;
+        x1i = y0i - y2i;
+        x2r = y1r - y3r;
+        x2i = y1i - y3i;
+        a[aP + 4] = x1r - x2i;
+        a[aP + 5] = x1i + x2r;
+        a[aP + 6] = x1r + x2i;
+        a[aP + 7] = x1i - x2r;
+        x1r = y4r - y6i;
+        x1i = y4i + y6r;
+        x0r = y5r - y7i;
+        x0i = y5i + y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 8] = x1r + x2r;
+        a[aP + 9] = x1i + x2i;
+        a[aP + 10] = x1r - x2r;
+        a[aP + 11] = x1i - x2i;
+        x1r = y4r + y6i;
+        x1i = y4i - y6r;
+        x0r = y5r + y7i;
+        x0i = y5i - y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 12] = x1r - x2i;
+        a[aP + 13] = x1i + x2r;
+        a[aP + 14] = x1r + x2i;
+        a[aP + 15] = x1i - x2r;
+        x1r = y8r + y10r;
+        x1i = y8i + y10i;
+        x2r = y9r - y11r;
+        x2i = y9i - y11i;
+        a[aP + 16] = x1r + x2r;
+        a[aP + 17] = x1i + x2i;
+        a[aP + 18] = x1r - x2r;
+        a[aP + 19] = x1i - x2i;
+        x1r = y8r - y10r;
+        x1i = y8i - y10i;
+        x2r = y9r + y11r;
+        x2i = y9i + y11i;
+        a[aP + 20] = x1r - x2i;
+        a[aP + 21] = x1i + x2r;
+        a[aP + 22] = x1r + x2i;
+        a[aP + 23] = x1i - x2r;
+        x1r = y12r - y14i;
+        x1i = y12i + y14r;
+        x0r = y13r + y15i;
+        x0i = y13i - y15r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 24] = x1r + x2r;
+        a[aP + 25] = x1i + x2i;
+        a[aP + 26] = x1r - x2r;
+        a[aP + 27] = x1i - x2i;
+        x1r = y12r + y14i;
+        x1i = y12i - y14r;
+        x0r = y13r - y15i;
+        x0i = y13i + y15r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 28] = x1r - x2i;
+        a[aP + 29] = x1i + x2r;
+        a[aP + 30] = x1r + x2i;
+        a[aP + 31] = x1i - x2r;
+    }
+
+    /** */
+    private void cftf081(double[] a, int aP, double[] w, int wP) {
+        double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+        wn4r = w[wP + 1];
+        x0r = a[aP + 0] + a[aP + 8];
+        x0i = a[aP + 1] + a[aP + 9];
+        x1r = a[aP + 0] - a[aP + 8];
+        x1i = a[aP + 1] - a[aP + 9];
+        x2r = a[aP + 4] + a[aP + 12];
+        x2i = a[aP + 5] + a[aP + 13];
+        x3r = a[aP + 4] - a[aP + 12];
+        x3i = a[aP + 5] - a[aP + 13];
+        y0r = x0r + x2r;
+        y0i = x0i + x2i;
+        y2r = x0r - x2r;
+        y2i = x0i - x2i;
+        y1r = x1r - x3i;
+        y1i = x1i + x3r;
+        y3r = x1r + x3i;
+        y3i = x1i - x3r;
+        x0r = a[aP + 2] + a[aP + 10];
+        x0i = a[aP + 3] + a[aP + 11];
+        x1r = a[aP + 2] - a[aP + 10];
+        x1i = a[aP + 3] - a[aP + 11];
+        x2r = a[aP + 6] + a[aP + 14];
+        x2i = a[aP + 7] + a[aP + 15];
+        x3r = a[aP + 6] - a[aP + 14];
+        x3i = a[aP + 7] - a[aP + 15];
+        y4r = x0r + x2r;
+        y4i = x0i + x2i;
+        y6r = x0r - x2r;
+        y6i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        x2r = x1r + x3i;
+        x2i = x1i - x3r;
+        y5r = wn4r * (x0r - x0i);
+        y5i = wn4r * (x0r + x0i);
+        y7r = wn4r * (x2r - x2i);
+        y7i = wn4r * (x2r + x2i);
+        a[aP + 8] = y1r + y5r;
+        a[aP + 9] = y1i + y5i;
+        a[aP + 10] = y1r - y5r;
+        a[aP + 11] = y1i - y5i;
+        a[aP + 12] = y3r - y7i;
+        a[aP + 13] = y3i + y7r;
+        a[aP + 14] = y3r + y7i;
+        a[aP + 15] = y3i - y7r;
+        a[aP + 0] = y0r + y4r;
+        a[aP + 1] = y0i + y4i;
+        a[aP + 2] = y0r - y4r;
+        a[aP + 3] = y0i - y4i;
+        a[aP + 4] = y2r - y6i;
+        a[aP + 5] = y2i + y6r;
+        a[aP + 6] = y2r + y6i;
+        a[aP + 7] = y2i - y6r;
+    }
+
+    /** */
+    private void cftf082(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+        wn4r = w[wP + 1];
+        wk1r = w[wP + 4];
+        wk1i = w[wP + 5];
+        y0r = a[aP + 0] - a[aP + 9];
+        y0i = a[aP + 1] + a[aP + 8];
+        y1r = a[aP + 0] + a[aP + 9];
+        y1i = a[aP + 1] - a[aP + 8];
+        x0r = a[aP + 4] - a[aP + 13];
+        x0i = a[aP + 5] + a[aP + 12];
+        y2r = wn4r * (x0r - x0i);
+        y2i = wn4r * (x0i + x0r);
+        x0r = a[aP + 4] + a[aP + 13];
+        x0i = a[aP + 5] - a[aP + 12];
+        y3r = wn4r * (x0r - x0i);
+        y3i = wn4r * (x0i + x0r);
+        x0r = a[aP + 2] - a[aP + 11];
+        x0i = a[aP + 3] + a[aP + 10];
+        y4r = wk1r * x0r - wk1i * x0i;
+        y4i = wk1r * x0i + wk1i * x0r;
+        x0r = a[aP + 2] + a[aP + 11];
+        x0i = a[aP + 3] - a[aP + 10];
+        y5r = wk1i * x0r - wk1r * x0i;
+        y5i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 6] - a[aP + 15];
+        x0i = a[aP + 7] + a[aP + 14];
+        y6r = wk1i * x0r - wk1r * x0i;
+        y6i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 6] + a[aP + 15];
+        x0i = a[aP + 7] - a[aP + 14];
+        y7r = wk1r * x0r - wk1i * x0i;
+        y7i = wk1r * x0i + wk1i * x0r;
+        x0r = y0r + y2r;
+        x0i = y0i + y2i;
+        x1r = y4r + y6r;
+        x1i = y4i + y6i;
+        a[aP + 0] = x0r + x1r;
+        a[aP + 1] = x0i + x1i;
+        a[aP + 2] = x0r - x1r;
+        a[aP + 3] = x0i - x1i;
+        x0r = y0r - y2r;
+        x0i = y0i - y2i;
+        x1r = y4r - y6r;
+        x1i = y4i - y6i;
+        a[aP + 4] = x0r - x1i;
+        a[aP + 5] = x0i + x1r;
+        a[aP + 6] = x0r + x1i;
+        a[aP + 7] = x0i - x1r;
+        x0r = y1r - y3i;
+        x0i = y1i + y3r;
+        x1r = y5r - y7r;
+        x1i = y5i - y7i;
+        a[aP + 8] = x0r + x1r;
+        a[aP + 9] = x0i + x1i;
+        a[aP + 10] = x0r - x1r;
+        a[aP + 11] = x0i - x1i;
+        x0r = y1r + y3i;
+        x0i = y1i - y3r;
+        x1r = y5r + y7r;
+        x1i = y5i + y7i;
+        a[aP + 12] = x0r - x1i;
+        a[aP + 13] = x0i + x1r;
+        a[aP + 14] = x0r + x1i;
+        a[aP + 15] = x0i - x1r;
+    }
+
+    /**
+     * 3rd
+     * when n = 8.
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftf040(double[] a) {
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        x0r = a[0] + a[4];
+        x0i = a[1] + a[5];
+        x1r = a[0] - a[4];
+        x1i = a[1] - a[5];
+        x2r = a[2] + a[6];
+        x2i = a[3] + a[7];
+        x3r = a[2] - a[6];
+        x3i = a[3] - a[7];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[4] = x0r - x2r;
+        a[5] = x0i - x2i;
+        a[2] = x1r - x3i;
+        a[3] = x1i + x3r;
+        a[6] = x1r + x3i;
+        a[7] = x1i - x3r;
+    }
+
+    /**
+     * 3rd
+     * when n = 8.
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void cftb040(double[] a) {
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        x0r = a[0] + a[4];
+        x0i = a[1] + a[5];
+        x1r = a[0] - a[4];
+        x1i = a[1] - a[5];
+        x2r = a[2] + a[6];
+        x2i = a[3] + a[7];
+        x3r = a[2] - a[6];
+        x3i = a[3] - a[7];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[4] = x0r - x2r;
+        a[5] = x0i - x2i;
+        a[2] = x1r + x3i;
+        a[3] = x1i - x3r;
+        a[6] = x1r - x3i;
+        a[7] = x1i + x3r;
+    }
+
+    /**
+     * 3rd
+     * when n = 4.
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftx020(double[] a) {
+        double x0r, x0i;
+
+        x0r = a[0] - a[2];
+        x0i = a[1] - a[3];
+        a[0] += a[2];
+        a[1] += a[3];
+        a[2] = x0r;
+        a[3] = x0i;
+    }
+
+    /**
+     * 2nd
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void rftfsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr, xi, yr, yi;
+
+        m = n >> 1;
+        ks = 2 * nc / m;
+        kk = 0;
+        for (j = 2; j < m; j += 2) {
+            k = n - j;
+            kk += ks;
+            wkr = 0.5 - c[cP + nc - kk];
+            wki = c[cP + kk];
+            xr = a[j] - a[k];
+            xi = a[j + 1] + a[k + 1];
+            yr = wkr * xr - wki * xi;
+            yi = wkr * xi + wki * xr;
+            a[j] -= yr;
+            a[j + 1] -= yi;
+            a[k] += yr;
+            a[k + 1] -= yi;
+        }
+    }
+
+    /**
+     * 2nd
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     */
+    private void rftbsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr, xi, yr, yi;
+
+        m = n >> 1;
+        ks = 2 * nc / m;
+        kk = 0;
+        for (j = 2; j < m; j += 2) {
+            k = n - j;
+            kk += ks;
+            wkr = 0.5 - c[cP + nc - kk];
+            wki = c[cP + kk];
+            xr = a[j] - a[k];
+            xi = a[j + 1] + a[k + 1];
+            yr = wkr * xr + wki * xi;
+            yi = wkr * xi - wki * xr;
+            a[j] -= yr;
+            a[j + 1] -= yi;
+            a[k] += yr;
+            a[k + 1] -= yi;
+        }
+    }
+
+    /**
+     * 2nd
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void dctsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr;
+
+        m = n >> 1;
+        ks = nc / n;
+        kk = 0;
+        for (j = 1; j < m; j++) {
+            k = n - j;
+            kk += ks;
+            wkr = c[cP + kk] - c[cP + nc - kk];
+            wki = c[cP + kk] + c[cP + nc - kk];
+            xr = wki * a[j] - wkr * a[k];
+            a[j] = wkr * a[j] + wki * a[k];
+            a[k] = xr;
+        }
+        a[m] *= c[cP + 0];
+    }
+
+    /**
+     * 2nd
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     */
+    private void dstsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr;
+
+        m = n >> 1;
+        ks = nc / n;
+        kk = 0;
+        for (j = 1; j < m; j++) {
+            k = n - j;
+            kk += ks;
+            wkr = c[cP + kk] - c[cP + nc - kk];
+            wki = c[cP + kk] + c[cP + nc - kk];
+            xr = wki * a[k] - wkr * a[j];
+            a[k] = wkr * a[k] + wki * a[j];
+            a[j] = xr;
+        }
+        a[m] *= c[cP + 0];
+    }
+}
+
+/* */

+ 215 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/AudioComposer.java

@@ -0,0 +1,215 @@
+package cn.i2edu.dubbing_lib.audioUtils.compose;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Created by Administrator on 2016/12/20.
+ */
+
+public class AudioComposer {
+    private static final int SAMPLERATE = 44100;
+    private static final int CHANNEL = 2;
+    private static final int AUDIO_FORMAT = 16;
+    private static final int BYTE_SIZE = 8;
+
+    public interface ComposeAudioInterface {
+
+        void composeSuccess(String result);
+
+        void composeFail();
+    }
+
+    public static void composeAudio(String firstAudioFilePath, List<String> secondAudioFilePaths,
+                                    final String composeAudioFilePath, boolean deleteSource,
+                                    List<Long> millSeconds,
+                                    List<Long> durations,
+                                    final ComposeAudioInterface composeAudioInterface) {
+        final int byteBufferSize = 512;
+        // 待合成音频1的缓冲
+        byte[] firstAudioByteBuffer = new byte[byteBufferSize];
+        // 待合成音频2的缓冲
+        byte[] secondAudioByteBuffer = new byte[byteBufferSize];
+        // 由待合成音频1的缓冲和待合成音频2的缓冲组成的缓冲,用作合成时的原数据
+        byte[][] allAudioBytes = new byte[2][];
+        // 每次合成的结果缓冲数据
+        byte[] mixedAudioByte;
+        // 待合成音频1的已读取总数,用于判断是否合成结束
+        int firstAudioReadNumber;
+        // 待合成音频2的已读取总数,用于判断是否合成结束
+        int secondAudioReadNumber;
+        Handler handler = new Handler(Looper.getMainLooper());
+        FileInputStream firstAudioInputStream = FileFunction.GetFileInputStreamFromFile
+                (firstAudioFilePath);
+        FileInputStream secondAudioInputStream;
+        FileOutputStream composeAudioOutputStream = FileFunction.GetFileOutputStreamFromFile(composeAudioFilePath);
+        for (int i = 0; i < secondAudioFilePaths.size(); i++) {
+            // 由于录制的音频段时长不一定很准确,所以就以音频结束的时间点为基准向前来计算,
+            // 这样就能够保证录制的音频能够和字幕比较准确的对应,避免那种字幕与音频错开的情况
+            // 上一段录音结尾到本段录音结尾的总字节数
+            long audioSegSize = (long) (SAMPLERATE * 2.0f * 2.0f * (float) ((Number)millSeconds.get(i)).longValue() / 1000);
+            System.out.println("audioSegSize: " + audioSegSize);
+
+            // 每段录音准确的字节数
+            long desiredFileByteNum = (long) (SAMPLERATE * 2.0f * 2.0f * (float)  ((Number)durations.get(i)).longValue() / 1000);
+            System.out.println("desiredFileByteNum: " + desiredFileByteNum);
+
+            // 分段音频的长度与准确音频长度的差值
+            long audioOffset = audioSegSize - desiredFileByteNum;
+            System.out.println("audioOffset: " + audioOffset);
+            try {
+                // 写入背景音乐的空隙部分
+                //noinspection LoopConditionNotUpdatedInsideLoop
+                while (firstAudioInputStream != null) {
+                    if (audioOffset <= 0) {
+                        break;
+                    }
+
+                    firstAudioReadNumber = firstAudioInputStream.read(firstAudioByteBuffer);
+                    if (firstAudioReadNumber <= 0) {
+                        break;
+                    }
+                    composeAudioOutputStream.write(firstAudioByteBuffer);
+                    audioOffset -= firstAudioReadNumber;
+
+                }
+                secondAudioInputStream = FileFunction.GetFileInputStreamFromFile(secondAudioFilePaths.get(i));
+                // 合成并写入录音段
+                //noinspection LoopConditionNotUpdatedInsideLoop
+                while (secondAudioInputStream!=null) {
+
+                    secondAudioReadNumber = secondAudioInputStream.read(secondAudioByteBuffer);
+                    if (secondAudioReadNumber <= 0 || desiredFileByteNum <= 0) {
+                        break;
+                    }
+                    if (firstAudioInputStream != null){
+                        firstAudioInputStream.read(firstAudioByteBuffer);
+                        allAudioBytes[0] = firstAudioByteBuffer;
+                        allAudioBytes[1] = secondAudioByteBuffer;
+                        mixedAudioByte = mixRawAudioBytes(allAudioBytes);
+                    }else {
+                        mixedAudioByte = secondAudioByteBuffer;
+                    }
+
+                    composeAudioOutputStream.write(mixedAudioByte);
+                    desiredFileByteNum -= secondAudioReadNumber;
+                }
+                if (secondAudioInputStream != null)secondAudioInputStream.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+//                CustomBugReporter.reportError(AudioComposer.class.getName(), "mux audio and audio failed", e);
+                handler.post(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (composeAudioInterface != null) {
+                            composeAudioInterface.composeFail();
+                        }
+                    }
+                });
+                return;
+            }
+        }
+        //  写入背景音乐剩余部分数据
+        try {
+            //noinspection LoopConditionNotUpdatedInsideLoop
+            while (firstAudioInputStream!= null) {
+                firstAudioReadNumber = firstAudioInputStream.read(firstAudioByteBuffer);
+                if (firstAudioReadNumber < 0) break;
+                composeAudioOutputStream.write(firstAudioByteBuffer);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+            //            CustomBugReporter.reportError(AudioComposer.class.getName(), "write left bgm data error", e);
+            if (composeAudioInterface != null) {
+                composeAudioInterface.composeFail();
+            }
+        }
+
+        try {
+            composeAudioOutputStream.close();
+        } catch (Exception e) {
+            e.printStackTrace();
+//            CustomBugReporter.reportError(AudioComposer.class.getName(), "close composeAudioOutputStream failed1", e);
+        }
+
+        if (deleteSource) {
+            FileFunction.DeleteFile(firstAudioFilePath);
+            for (int i = 0; i < secondAudioFilePaths.size(); i++) {
+                FileFunction.DeleteFile(secondAudioFilePaths.get(i));
+            }
+        }
+        try {
+            if (firstAudioInputStream != null)firstAudioInputStream.close();
+            composeAudioOutputStream.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+//            CustomBugReporter.reportError(AudioComposer.class.getName(), "close firstAudioInputStream or composeAudioOutputStream failed!", e);
+        }
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (composeAudioInterface != null) {
+                    composeAudioInterface.composeSuccess(composeAudioFilePath);
+                }
+            }
+        });
+    }
+
+    /**
+     * 合成对应的byte
+     *
+     * @param bMulRoadAudioes 需要被合成的byte数组
+     */
+    private static byte[] mixRawAudioBytes(byte[][] bMulRoadAudioes) {
+
+        if (bMulRoadAudioes == null || bMulRoadAudioes.length == 0)
+            return null;
+
+        byte[] realMixAudio = bMulRoadAudioes[0];
+
+        if (bMulRoadAudioes.length == 1)
+            return realMixAudio;
+
+        for (int rw = 0; rw < bMulRoadAudioes.length; ++rw) {
+            if (bMulRoadAudioes[rw].length != realMixAudio.length) {
+                Log.e("app", "column of the road of audio + " + rw + " is diffrent.");
+                return null;
+            }
+        }
+
+        int row = bMulRoadAudioes.length;
+        int coloum = realMixAudio.length / 2;
+        short[][] sMulRoadAudioes = new short[row][coloum];
+
+        for (int r = 0; r < row; ++r) {
+            for (int c = 0; c < coloum; ++c) {
+                sMulRoadAudioes[r][c] = (short) ((bMulRoadAudioes[r][c * 2] & 0xff) | (bMulRoadAudioes[r][c * 2 + 1] & 0xff) << 8);
+            }
+        }
+
+        short[] sMixAudio = new short[coloum];
+        int mixVal;
+        int sr;
+        for (int sc = 0; sc < coloum; ++sc) {
+            mixVal = 0;
+            sr = 0;
+            for (; sr < row; ++sr) {
+                mixVal += sMulRoadAudioes[sr][sc];
+            }
+            sMixAudio[sc] = (short) (mixVal / row);
+        }
+
+        for (sr = 0; sr < coloum; ++sr) {
+            realMixAudio[sr * 2] = (byte) (sMixAudio[sr] & 0x00FF);
+            realMixAudio[sr * 2 + 1] = (byte) ((sMixAudio[sr] & 0xFF00) >> 8);
+        }
+
+        return realMixAudio;
+    }
+}

+ 66 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/FileFunction.java

@@ -0,0 +1,66 @@
+package cn.i2edu.dubbing_lib.audioUtils.compose;
+
+import android.text.TextUtils;
+
+//import com.choucheng.dubbing_lib.exceptionengine.CustomBugReporter;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+/**
+ * Created by zhengtongyu on 16/5/23.
+ */
+@SuppressWarnings("ResultOfMethodCallIgnored")
+public class FileFunction {
+
+    public static void DeleteFile(String path) {
+        if (!TextUtils.isEmpty(path)) {
+            File file = new File(path);
+
+            if (file.exists()) {
+                try {
+                    file.delete();
+                } catch (Exception e) {
+//                    LogFunction.error("删除本地文件失败", e);
+                }
+            }
+        }
+    }
+
+    public static FileInputStream GetFileInputStreamFromFile(String fileUrl) {
+        FileInputStream fileInputStream = null;
+
+        try {
+            File file = new File(fileUrl);
+
+            fileInputStream = new FileInputStream(file);
+        } catch (Exception e) {
+            e.printStackTrace();
+//            CustomBugReporter.reportError(FileFunction.class.getName(), "获取录音文件", e);
+        }
+
+        return fileInputStream;
+    }
+
+    public static FileOutputStream GetFileOutputStreamFromFile(String fileUrl) {
+        FileOutputStream bufferedOutputStream = null;
+
+        try {
+            File file = new File(fileUrl);
+
+            if (file.exists()) {
+                file.delete();
+            }
+
+            file.createNewFile();
+
+            bufferedOutputStream = new FileOutputStream(file);
+        } catch (Exception e) {
+//            LogFunction.error("GetFileOutputStreamFromFile异常", e);
+        }
+
+        return bufferedOutputStream;
+    }
+
+}

+ 155 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/I0Bessel.java

@@ -0,0 +1,155 @@
+/*
+ * Copyright(C) 1996 Takuya OOURA (email: ooura@mmm.t.u-tokyo.ac.jp).
+ * You may use, copy, modify this code for any purpose and
+ * without fee. You may distribute this ORIGINAL package.
+ */
+package cn.i2edu.dubbing_lib.audioUtils.compose;
+
+
+/**
+ * Bessel I_0().
+ *
+ * @author <a href="mailto:ooura@mmm.t.u-tokyo.ac.jp">Takuya OOURA</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+public class I0Bessel {
+    /** */
+    private static final double[] a = {
+            8.5246820682016865877e-11, 2.5966600546497407288e-9,
+            7.9689994568640180274e-8, 1.9906710409667748239e-6,
+            4.0312469446528002532e-5, 6.4499871606224265421e-4,
+            0.0079012345761930579108, 0.071111111109207045212,
+            0.444444444444724909, 1.7777777777777532045,
+            4.0000000000000011182, 3.99999999999999998,
+            1.0000000000000000001,
+            1.1520919130377195927e-10, 2.2287613013610985225e-9,
+            8.1903951930694585113e-8, 1.9821560631611544984e-6,
+            4.0335461940910133184e-5, 6.4495330974432203401e-4,
+            0.0079013012611467520626, 0.071111038160875566622,
+            0.44444450319062699316, 1.7777777439146450067,
+            4.0000000132337935071, 3.9999999968569015366,
+            1.0000000003426703174,
+            1.5476870780515238488e-10, 1.2685004214732975355e-9,
+            9.2776861851114223267e-8, 1.9063070109379044378e-6,
+            4.0698004389917945832e-5, 6.4370447244298070713e-4,
+            0.0079044749458444976958, 0.071105052411749363882,
+            0.44445280640924755082, 1.7777694934432109713,
+            4.0000055808824003386, 3.9999977081165740932,
+            1.0000004333949319118,
+            2.0675200625006793075e-10, -6.1689554705125681442e-10,
+            1.2436765915401571654e-7, 1.5830429403520613423e-6,
+            4.2947227560776583326e-5, 6.3249861665073441312e-4,
+            0.0079454472840953930811, 0.070994327785661860575,
+            0.44467219586283000332, 1.7774588182255374745,
+            4.0003038986252717972, 3.9998233869142057195,
+            1.0000472932961288324,
+            2.7475684794982708655e-10, -3.8991472076521332023e-9,
+            1.9730170483976049388e-7, 5.9651531561967674521e-7,
+            5.1992971474748995357e-5, 5.7327338675433770752e-4,
+            0.0082293143836530412024, 0.069990934858728039037,
+            0.44726764292723985087, 1.7726685170014087784,
+            4.0062907863712704432, 3.9952750700487845355,
+            1.0016354346654179322
+    };
+    /** */
+    private static final double[] b = {
+            6.7852367144945531383e-8, 4.6266061382821826854e-7,
+            6.9703135812354071774e-6, 7.6637663462953234134e-5,
+            7.9113515222612691636e-4, 0.0073401204731103808981,
+            0.060677114958668837046, 0.43994941411651569622,
+            2.7420017097661750609, 14.289661921740860534,
+            59.820609640320710779, 188.78998681199150629,
+            399.8731367825601118, 427.56411572180478514,
+            1.8042097874891098754e-7, 1.2277164312044637357e-6,
+            1.8484393221474274861e-5, 2.0293995900091309208e-4,
+            0.0020918539850246207459, 0.019375315654033949297,
+            0.15985869016767185908, 1.1565260527420641724,
+            7.1896341224206072113, 37.354773811947484532,
+            155.80993164266268457, 489.5211371158540918,
+            1030.9147225169564806, 1093.5883545113746958,
+            4.8017305613187493564e-7, 3.261317843912380074e-6,
+            4.9073137508166159639e-5, 5.3806506676487583755e-4,
+            0.0055387918291051866561, 0.051223717488786549025,
+            0.42190298621367914765, 3.0463625987357355872,
+            18.895299447327733204, 97.915189029455461554,
+            407.13940115493494659, 1274.3088990480582632,
+            2670.9883037012547506, 2815.7166284662544712,
+            1.2789926338424623394e-6, 8.6718263067604918916e-6,
+            1.3041508821299929489e-4, 0.001428224737372747892,
+            0.014684070635768789378, 0.13561403190404185755,
+            1.1152592585977393953, 8.0387088559465389038,
+            49.761318895895479206, 257.2684232313529138,
+            1066.8543146269566231, 3328.3874581009636362,
+            6948.8586598121634874, 7288.4893398212481055,
+            3.409350368197032893e-6, 2.3079025203103376076e-5,
+            3.4691373283901830239e-4, 0.003794994977222908545,
+            0.038974209677945602145, 0.3594948380414878371,
+            2.9522878893539528226, 21.246564609514287056,
+            131.28727387146173141, 677.38107093296675421,
+            2802.3724744545046518, 8718.5731420798254081,
+            18141.348781638832286, 18948.925349296308859
+    };
+    /** */
+    private static final double[] c = {
+            2.5568678676452702768e-15, 3.0393953792305924324e-14,
+            6.3343751991094840009e-13, 1.5041298011833009649e-11,
+            4.4569436918556541414e-10, 1.746393051427167951e-8,
+            1.0059224011079852317e-6, 1.0729838945088577089e-4,
+            0.05150322693642527738,
+            5.2527963991711562216e-15, 7.202118481421005641e-15,
+            7.2561421229904797156e-13, 1.482312146673104251e-11,
+            4.4602670450376245434e-10, 1.7463600061788679671e-8,
+            1.005922609132234756e-6, 1.0729838937545111487e-4,
+            0.051503226936437300716,
+            1.3365917359358069908e-14, -1.2932643065888544835e-13,
+            1.7450199447905602915e-12, 1.0419051209056979788e-11,
+            4.58047881980598326e-10, 1.7442405450073548966e-8,
+            1.0059461453281292278e-6, 1.0729837434500161228e-4,
+            0.051503226940658446941,
+            5.3771611477352308649e-14, -1.1396193006413731702e-12,
+            1.2858641335221653409e-11, -5.9802086004570057703e-11,
+            7.3666894305929510222e-10, 1.6731837150730356448e-8,
+            1.0070831435812128922e-6, 1.0729733111203704813e-4,
+            0.051503227360726294675,
+            3.7819492084858931093e-14, -4.8600496888588034879e-13,
+            1.6898350504817224909e-12, 4.5884624327524255865e-11,
+            1.2521615963377513729e-10, 1.8959658437754727957e-8,
+            1.0020716710561353622e-6, 1.073037119856927559e-4,
+            0.05150322383300230775
+    };
+
+    public static double value(double x) {
+        int k;
+        double w, t, y;
+        w = Math.abs(x);
+        if (w < 8.5) {
+            t = w * w * 0.0625;
+            k = 13 * ((int) t);
+            y = (((((((((((a[k] * t + a[k + 1]) * t +
+                    a[k + 2]) * t + a[k + 3]) * t + a[k + 4]) * t +
+                    a[k + 5]) * t + a[k + 6]) * t + a[k + 7]) * t +
+                    a[k + 8]) * t + a[k + 9]) * t + a[k + 10]) * t +
+                    a[k + 11]) * t + a[k + 12];
+        } else if (w < 12.5) {
+            k = (int) w;
+            t = w - k;
+            k = 14 * (k - 8);
+            y = ((((((((((((b[k] * t + b[k + 1]) * t +
+                    b[k + 2]) * t + b[k + 3]) * t + b[k + 4]) * t +
+                    b[k + 5]) * t + b[k + 6]) * t + b[k + 7]) * t +
+                    b[k + 8]) * t + b[k + 9]) * t + b[k + 10]) * t +
+                    b[k + 11]) * t + b[k + 12]) * t + b[k + 13];
+        } else {
+            t = 60 / w;
+            k = 9 * ((int) t);
+            y = ((((((((c[k] * t + c[k + 1]) * t +
+                    c[k + 2]) * t + c[k + 3]) * t + c[k + 4]) * t +
+                    c[k + 5]) * t + c[k + 6]) * t + c[k + 7]) * t +
+                    c[k + 8]) * Math.sqrt(t) * Math.exp(w);
+        }
+        return y;
+    }
+}
+
+/* */

+ 2557 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/SSRC.java

@@ -0,0 +1,2557 @@
+/*
+ * This program(except FFT and Bessel function part) is distributed under
+ * LGPL. See LGPL.txt for details. But, if you make a new program with derived
+ * code from this program,I strongly wish that my name and derived code are
+ * indicated explicitly.
+ */
+
+package cn.i2edu.dubbing_lib.audioUtils.compose;
+
+
+import android.annotation.SuppressLint;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+import java.util.Random;
+
+/**
+ * Shibatch Sampling Rate Converter.
+ *
+ * @author <a href="shibatch@users.sourceforge.net">Naoki Shibata</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @author Maksim Khadkevich (a couple of minor changes)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+@SuppressWarnings({"SuspiciousNameCombination", "ConstantConditions", "UnnecessaryLocalVariable", "RedundantStringFormatCall", "UnusedAssignment", "StatementWithEmptyBody", "PointlessArithmeticExpression", "DanglingJavadoc", "JavaDoc", "ResultOfMethodCallIgnored", "PointlessBitwiseExpression"})
+public class SSRC {
+
+    /** */
+    private final ByteOrder byteOrder = ByteOrder.LITTLE_ENDIAN;
+
+    /** */
+    private final SplitRadixFft fft = new SplitRadixFft();
+
+    /** */
+    private static final String VERSION = "1.30";
+
+    /** */
+    private double AA = 170;
+
+    /** */
+    private double DF = 100;
+
+    /** */
+    private int FFTFIRLEN = 65536;
+
+    /** */
+    //  private static final int M = 15;
+
+    /** */
+    private static final int RANDBUFLEN = 65536;
+
+    /** */
+    private int RINT(double x) {
+        return ((x) >= 0 ? ((int) ((x) + 0.5)) : ((int) ((x) - 0.5)));
+    }
+
+    /** */
+    private static final int scoeffreq[] = {0, 48000, 44100, 37800, 32000, 22050, 48000, 44100};
+
+    /** */
+    private static final int scoeflen[] = {1, 16, 20, 16, 16, 15, 16, 15};
+
+    /** */
+    private static final int samp[] = {8, 18, 27, 8, 8, 8, 10, 9};
+
+    /** */
+    private static final double[][] shapercoefs = {{-1}, // triangular dither
+
+            {-2.8720729351043701172, 5.0413231849670410156, -6.2442994117736816406,
+                    5.8483986854553222656, -3.7067542076110839844, 1.0495119094848632812,
+                    1.1830236911773681641, -2.1126792430877685547, 1.9094531536102294922,
+                    -0.99913084506988525391, 0.17090806365013122559, 0.32615602016448974609,
+                    -0.39127644896507263184, 0.26876461505889892578, -0.097676105797290802002,
+                    0.023473845794796943665,}, // 48k, N=16, amp=18
+
+            {-2.6773197650909423828, 4.8308925628662109375, -6.570110321044921875,
+                    7.4572014808654785156, -6.7263274192810058594, 4.8481650352478027344,
+                    -2.0412089824676513672, -0.7006359100341796875, 2.9537565708160400391,
+                    -4.0800385475158691406, 4.1845216751098632812, -3.3311812877655029297,
+                    2.1179926395416259766, -0.879302978515625, 0.031759146600961685181,
+                    0.42382788658142089844, -0.47882103919982910156, 0.35490813851356506348,
+                    -0.17496839165687561035, 0.060908168554306030273,}, // 44.1k, N=20, amp=27
+
+            {-1.6335992813110351562, 2.2615492343902587891, -2.4077029228210449219,
+                    2.6341717243194580078, -2.1440362930297851562, 1.8153258562088012695,
+                    -1.0816224813461303711, 0.70302653312683105469, -0.15991993248462677002,
+                    -0.041549518704414367676, 0.29416576027870178223, -0.2518316805362701416,
+                    0.27766478061676025391, -0.15785403549671173096, 0.10165894031524658203,
+                    -0.016833892092108726501,}, // 37.8k, N=16
+
+            {-0.82901298999786376953, 0.98922657966613769531, -0.59825712442398071289,
+                    1.0028809309005737305, -0.59938216209411621094, 0.79502451419830322266,
+                    -0.42723315954208374023, 0.54492527246475219727, -0.30792605876922607422,
+                    0.36871799826622009277, -0.18792048096656799316, 0.2261127084493637085,
+                    -0.10573341697454452515, 0.11435490846633911133, -0.038800679147243499756,
+                    0.040842197835445404053,}, // 32k, N=16
+
+            {-0.065229974687099456787, 0.54981261491775512695, 0.40278548002243041992,
+                    0.31783768534660339355, 0.28201797604560852051, 0.16985194385051727295,
+                    0.15433363616466522217, 0.12507140636444091797, 0.08903945237398147583,
+                    0.064410120248794555664, 0.047146003693342208862, 0.032805237919092178345,
+                    0.028495194390416145325, 0.011695005930960178375, 0.011831838637590408325,},
+            // 22.05k, N=15
+
+            {-2.3925774097442626953, 3.4350297451019287109, -3.1853709220886230469,
+                    1.8117271661758422852, 0.20124770700931549072, -1.4759907722473144531,
+                    1.7210904359817504883, -0.97746700048446655273, 0.13790138065814971924,
+                    0.38185903429985046387, -0.27421241998672485352, -0.066584214568138122559,
+                    0.35223302245140075684, -0.37672343850135803223, 0.23964276909828186035,
+                    -0.068674825131893157959,}, // 48k, N=16, amp=10
+
+            {-2.0833916664123535156, 3.0418450832366943359, -3.2047898769378662109,
+                    2.7571926116943359375, -1.4978630542755126953, 0.3427594602108001709,
+                    0.71733748912811279297, -1.0737057924270629883, 1.0225815773010253906,
+                    -0.56649994850158691406, 0.20968692004680633545, 0.065378531813621520996,
+                    -0.10322438180446624756, 0.067442022264003753662, 0.00495197344571352005,},
+            // 44.1k, N=15, amp=9
+    };
+
+    /** */
+    private double[][] shapebuf;
+
+    /** */
+    private int shaper_type, shaper_len, shaper_clipmin, shaper_clipmax;
+
+    /** */
+    private double[] randbuf;
+
+    /** */
+    private int randptr;
+
+    /** */
+    private boolean quiet = false;
+
+    /** */
+    private int lastshowed2;
+
+    /** */
+    private long starttime, lastshowed;
+
+    /** */
+    private static final int POOLSIZE = 97;
+
+    /** */
+    public int init_shaper(int freq, int nch, int min, int max, int dtype, int pdf,
+                           double noiseamp) {
+        int i;
+        int[] pool = new int[POOLSIZE];
+
+        for (i = 1; i < 6; i++) {
+            if (freq == scoeffreq[i]) {
+                break;
+            }
+        }
+        if ((dtype == 3 || dtype == 4) && i == 6) {
+            System.err
+                    .printf("Warning: ATH based noise shaping for destination frequency %dHz is not available, using triangular dither\n",
+                            freq);
+        }
+        if (dtype == 2 || i == 6) {
+            i = 0;
+        }
+        if (dtype == 4 && (i == 1 || i == 2)) {
+            i += 5;
+        }
+
+        shaper_type = i;
+
+        shapebuf = new double[nch][];
+        shaper_len = scoeflen[shaper_type];
+
+        for (i = 0; i < nch; i++) {
+            shapebuf[i] = new double[shaper_len];
+        }
+
+        shaper_clipmin = min;
+        shaper_clipmax = max;
+
+        randbuf = new double[RANDBUFLEN];
+
+        Random random = new Random(System.currentTimeMillis());
+        for (i = 0; i < POOLSIZE; i++) {
+            pool[i] = random.nextInt();
+        }
+
+        switch (pdf) {
+            case 0: // rectangular
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    int r, p;
+
+                    p = random.nextInt() % POOLSIZE;
+                    r = pool[p];
+                    pool[p] = random.nextInt();
+                    randbuf[i] = noiseamp * (((double) r) / Integer.MAX_VALUE - 0.5);
+                }
+                break;
+
+            case 1: // triangular
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    int r1, r2, p;
+
+                    p = random.nextInt() % POOLSIZE;
+                    r1 = pool[p];
+                    pool[p] = random.nextInt();
+                    p = random.nextInt() % POOLSIZE;
+                    r2 = pool[p];
+                    pool[p] = random.nextInt();
+                    randbuf[i] = noiseamp * ((((double) r1) / Integer.MAX_VALUE) -
+                            (((double) r2) / Integer.MAX_VALUE));
+                }
+                break;
+
+            case 2: // gaussian
+            {
+                int sw = 0;
+                double t = 0, u = 0;
+
+                for (i = 0; i < RANDBUFLEN; i++) {
+                    double r;
+                    int p;
+
+                    if (sw == 0) {
+                        sw = 1;
+
+                        p = random.nextInt() % POOLSIZE;
+                        r = ((double) pool[p]) / Integer.MAX_VALUE;
+                        pool[p] = random.nextInt();
+                        if (r == 1.0) {
+                            r = 0.0;
+                        }
+
+                        t = Math.sqrt(-2 * Math.log(1 - r));
+
+                        p = random.nextInt() % POOLSIZE;
+                        r = ((double) pool[p]) / Integer.MAX_VALUE;
+                        pool[p] = random.nextInt();
+
+                        u = 2 * Math.PI * r;
+
+                        randbuf[i] = noiseamp * t * Math.cos(u);
+                    } else {
+                        sw = 0;
+
+                        randbuf[i] = noiseamp * t * Math.sin(u);
+                    }
+                }
+            }
+            break;
+        }
+
+        randptr = 0;
+
+        if (dtype == 0 || dtype == 1) {
+            return 1;
+        }
+        return samp[shaper_type];
+    }
+
+    /** */
+    public int do_shaping(double s, double[] peak, int dtype, int ch) {
+        double u, h;
+        int i;
+
+        if (dtype == 1) {
+            s += randbuf[randptr++ & (RANDBUFLEN - 1)];
+
+            if (s < shaper_clipmin) {
+                double d = s / shaper_clipmin;
+                peak[0] = peak[0] < d ? d : peak[0];
+                s = shaper_clipmin;
+            }
+            if (s > shaper_clipmax) {
+                double d = s / shaper_clipmax;
+                peak[0] = peak[0] < d ? d : peak[0];
+                s = shaper_clipmax;
+            }
+
+            return RINT(s);
+        }
+
+        h = 0;
+        for (i = 0; i < shaper_len; i++) {
+            h += shapercoefs[shaper_type][i] * shapebuf[ch][i];
+        }
+        s += h;
+        u = s;
+        s += randbuf[randptr++ & (RANDBUFLEN - 1)];
+
+        for (i = shaper_len - 2; i >= 0; i--) {
+            shapebuf[ch][i + 1] = shapebuf[ch][i];
+        }
+
+        if (s < shaper_clipmin) {
+            double d = s / shaper_clipmin;
+            peak[0] = peak[0] < d ? d : peak[0];
+            s = shaper_clipmin;
+            shapebuf[ch][0] = s - u;
+
+            if (shapebuf[ch][0] > 1) {
+                shapebuf[ch][0] = 1;
+            }
+            if (shapebuf[ch][0] < -1) {
+                shapebuf[ch][0] = -1;
+            }
+        } else if (s > shaper_clipmax) {
+            double d = s / shaper_clipmax;
+            peak[0] = peak[0] < d ? d : peak[0];
+            s = shaper_clipmax;
+            shapebuf[ch][0] = s - u;
+
+            if (shapebuf[ch][0] > 1) {
+                shapebuf[ch][0] = 1;
+            }
+            if (shapebuf[ch][0] < -1) {
+                shapebuf[ch][0] = -1;
+            }
+        } else {
+            s = RINT(s);
+            shapebuf[ch][0] = s - u;
+        }
+
+        return (int) s;
+    }
+
+    /** */
+    private void quit_shaper(int nch) {
+    }
+
+    /** */
+    private double alpha(double a) {
+        if (a <= 21) {
+            return 0;
+        }
+        if (a <= 50) {
+            return 0.5842 * Math.pow(a - 21, 0.4) + 0.07886 * (a - 21);
+        }
+        return 0.1102 * (a - 8.7);
+    }
+
+    /** */
+    private double win(double n, int len, double alp, double iza) {
+        return I0Bessel
+                .value(alp * Math.sqrt(1 - 4 * n * n / (((double) len - 1) * ((double) len - 1)))) /
+                iza;
+    }
+
+    /** */
+    private double sinc(double x) {
+        return x == 0 ? 1 : Math.sin(x) / x;
+    }
+
+    /** */
+    private double hn_lpf(int n, double lpf, double fs) {
+        double t = 1 / fs;
+        double omega = 2 * Math.PI * lpf;
+        return 2 * lpf * t * sinc(n * omega * t);
+    }
+
+    /** */
+    private void usage() {
+        System.err.printf("http://shibatch.sourceforge.net/\n\n");
+        System.err.printf("usage: ssrc [<options>] <source wav file> <destination wav file>\n");
+        System.err.printf("options : --rate <sampling rate>     output sample rate\n");
+        System.err.printf("          --att <attenuation(dB)>    attenuate signal\n");
+        System.err.printf("          --bits <number of bits>    output quantization bit length\n");
+        System.err.printf("          --tmpfile <file name>      specify temporal file\n");
+        System.err
+                .printf("          --twopass                  two pass processing to avoid clipping\n");
+        System.err.printf("          --normalize                normalize the wave file\n");
+        System.err.printf("          --quiet                    nothing displayed except error\n");
+        System.err.printf("          --dither [<type>]          dithering\n");
+        System.err.printf("                                       0 : no dither\n");
+        System.err.printf("                                       1 : no noise shaping\n");
+        System.err.printf("                                       2 : triangular spectral shape\n");
+        System.err.printf("                                       3 : ATH based noise shaping\n");
+        System.err
+                .printf("                                       4 : less dither amplitude than type 3\n");
+        System.err.printf("          --pdf <type> [<amp>]       select p.d.f. of noise\n");
+        System.err.printf("                                       0 : rectangular\n");
+        System.err.printf("                                       1 : triangular\n");
+        System.err.printf("                                       2 : Gaussian\n");
+        System.err.printf("          --profile <type>           specify profile\n");
+        System.err
+                .printf("                                       standard : the default quality\n");
+        System.err
+                .printf("                                       fast     : fast, not so bad quality\n");
+    }
+
+    /** */
+    private void fmterr(int x) {
+        throw new IllegalStateException("unknown error " + x);
+    }
+
+    /** */
+    private void setstarttime() {
+        starttime = System.currentTimeMillis();
+        lastshowed = 0;
+        lastshowed2 = -1;
+    }
+
+    /** */
+    private void showprogress(double p) {
+        int eta, pc;
+        long t;
+        if (quiet) {
+            return;
+        }
+
+        t = System.currentTimeMillis() - starttime;
+        if (p == 0) {
+            eta = 0;
+        } else {
+            eta = (int) (t * (1 - p) / p);
+        }
+
+        pc = (int) (p * 100);
+
+        if (pc != lastshowed2 || t != lastshowed) {
+            System.err.printf(" %3d%% processed", pc);
+            lastshowed2 = pc;
+        }
+        if (t != lastshowed) {
+            System.err.printf(", ETA =%4dmsec", eta);
+            lastshowed = t;
+        }
+        System.err.printf("\r");
+        System.err.flush();
+    }
+
+    /** */
+    private int gcd(int x, int y) {
+        int t;
+
+        while (y != 0) {
+            t = x % y;
+            x = y;
+            y = t;
+        }
+        return x;
+    }
+
+    /**
+     * @param fpi
+     * @param fpo
+     * @param nch
+     * @param bps
+     * @param dbps     sizeof(double)?
+     * @param sfrq
+     * @param dfrq
+     * @param gain
+     * @param chanklen
+     * @param twopass
+     * @param dither
+     * @return
+     * @throws IOException
+     */
+    @SuppressLint("Assert")
+    public double upsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, int sfrq,
+                           int dfrq, double gain, int chanklen, boolean twopass, int dither)
+            throws IOException {
+        int frqgcd, osf = 0, fs1, fs2;
+        double[][] stage1;
+        double[] stage2;
+        int n1, n1x, n1y, n2, n2b;
+        int filter2len;
+        int[] f1order, f1inc;
+        int[] fft_ip = null;
+        double[] fft_w = null;
+        ByteBuffer rawinbuf, rawoutbuf;
+        double[] inbuf, outbuf;
+        double[][] buf1, buf2;
+        double[] peak = new double[]{0};
+        int spcount = 0;
+        int i, j;
+
+        //        System.err.println("upsample");
+
+        filter2len = FFTFIRLEN; // stage 2 filter length
+
+        // Make stage 1 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            double guard = 2;
+
+            frqgcd = gcd(sfrq, dfrq);
+
+            fs1 = sfrq / frqgcd * dfrq;
+
+            if (fs1 / dfrq == 1) {
+                osf = 1;
+            } else if (fs1 / dfrq % 2 == 0) {
+                osf = 2;
+            } else if (fs1 / dfrq % 3 == 0) {
+                osf = 3;
+            } else {
+                throw new IllegalArgumentException(String.format(
+                        "Resampling from %dHz to %dHz is not supported.\n" +
+                                "%d/gcd(%d,%d)=%d must be divided by 2 or 3.\n", sfrq, dfrq, sfrq,
+                        sfrq, dfrq, fs1 / dfrq));
+            }
+
+            df = (dfrq * osf / 2 - sfrq / 2) * 2 / guard;
+            lpf = sfrq / 2 + (dfrq * osf / 2 - sfrq / 2) / guard;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n1 = (int) (fs1 / df * d + 1);
+            if (n1 % 2 == 0) {
+                n1++;
+            }
+
+            alp = alpha(aa);
+            iza = I0Bessel.value(alp);
+            // System.err.printf("iza = %g\n",iza);
+
+            n1y = fs1 / sfrq;
+            n1x = n1 / n1y + 1;
+
+            f1order = new int[n1y * osf];
+            for (i = 0; i < n1y * osf; i++) {
+                f1order[i] = fs1 / sfrq - (i * (fs1 / (dfrq * osf))) % (fs1 / sfrq);
+                if (f1order[i] == fs1 / sfrq) {
+                    f1order[i] = 0;
+                }
+            }
+
+            f1inc = new int[n1y * osf];
+            for (i = 0; i < n1y * osf; i++) {
+                f1inc[i] = f1order[i] < fs1 / (dfrq * osf) ? nch : 0;
+                if (f1order[i] == fs1 / sfrq) {
+                    f1order[i] = 0;
+                }
+            }
+
+            stage1 = new double[n1y][n1x];
+
+            for (i = -(n1 / 2); i <= n1 / 2; i++) {
+                stage1[(i + n1 / 2) % n1y][(i + n1 / 2) / n1y] =
+                        win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq;
+            }
+        }
+
+        // Make stage 2 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            int ipsize, wsize;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            fs2 = dfrq * osf;
+
+            for (i = 1; ; i = i * 2) {
+                n2 = filter2len * i;
+                if (n2 % 2 == 0) {
+                    n2--;
+                }
+                df = (fs2 * d) / (n2 - 1);
+                lpf = sfrq / 2;
+                if (df < DF) {
+                    break;
+                }
+            }
+
+            alp = alpha(aa);
+
+            iza = I0Bessel.value(alp);
+
+            for (n2b = 1; n2b < n2; n2b *= 2) {
+            }
+            n2b *= 2;
+
+            stage2 = new double[n2b];
+
+            for (i = -(n2 / 2); i <= n2 / 2; i++) {
+                stage2[i + n2 / 2] = win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) / n2b * 2;
+            }
+
+            ipsize = (int) (2 + Math.sqrt(n2b));
+            fft_ip = new int[ipsize];
+            fft_ip[0] = 0;
+            wsize = n2b / 2;
+            fft_w = new double[wsize];
+
+            fft.rdft(n2b, 1, stage2, fft_ip, fft_w);
+        }
+
+        // Apply filters
+
+        setstarttime();
+
+        {
+            int n2b2 = n2b / 2;
+            // inbuffs1Tv???
+            int rp;
+            // disposesfrqTv?
+            int ds;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt1;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt2 = 0;
+            // stage1 filter?oTv?n1y*osf]
+            int s1p;
+            boolean init;
+            boolean ending;
+            int sumread, sumwrite;
+            int osc;
+            int ip, ip_backup;
+            int s1p_backup, osc_backup;
+            int ch, p;
+            int inbuflen;
+            int delay = 0;
+
+            buf1 = new double[nch][n2b2 / osf + 1];
+
+            buf2 = new double[nch][n2b];
+
+            rawinbuf = ByteBuffer.allocate(nch * (n2b2 + n1x) * bps); // ,bps
+            rawoutbuf = ByteBuffer.allocate(nch * (n2b2 / osf + 1) * dbps); // ,dbps
+
+            inbuf = new double[nch * (n2b2 + n1x)];
+            outbuf = new double[nch * (n2b2 / osf + 1)];
+
+            s1p = 0;
+            rp = 0;
+            ds = 0;
+            osc = 0;
+
+            init = true;
+            ending = false;
+            inbuflen = n1 / 2 / (fs1 / sfrq) + 1;
+            delay = (int) ((double) n2 / 2 / (fs2 / dfrq));
+
+            sumread = sumwrite = 0;
+
+            while (true) {
+                int nsmplread, toberead, toberead2;
+
+                toberead2 = toberead =
+                        (int) (Math.floor((double) n2b2 * sfrq / (dfrq * osf)) + 1 + n1x -
+                                inbuflen);
+                if (toberead + sumread > chanklen) {
+                    toberead = chanklen - sumread;
+                }
+
+                rawinbuf.position(0);
+                rawinbuf.limit(Math.min(rawinbuf.limit(), bps * nch * toberead));
+                //                rawinbuf.limit(bps * nch * toberead);
+
+                byte[] tempData = new byte[rawinbuf.limit()];
+                nsmplread = fpi.read(tempData);
+                if (nsmplread < 0) {
+                    nsmplread = 0;
+                }
+
+                if (nsmplread < rawinbuf.limit()) {
+                    chanklen = sumread + nsmplread / bps * nch;
+                }
+
+                rawinbuf.limit(nsmplread);
+                rawinbuf = ByteBuffer.wrap(tempData);
+                rawinbuf.position(nsmplread);
+
+                rawinbuf.flip();
+                nsmplread /= bps * nch;
+
+                switch (bps) {
+                    case 1:
+                        for (i = 0; i < nsmplread * nch; i++)
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7f) * ((double) rawinbuf.get(i) - 128);
+                        break;
+
+                    case 2:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asShortBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v;
+                        }
+                        break;
+
+                    case 3:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7fffff) * ((rawinbuf.get(i * 3) << 0) |
+                                            (rawinbuf.get(i * 3 + 1) << 8) |
+                                            (rawinbuf.get(i * 3 + 2) << 16));
+                        }
+                        break;
+
+                    case 4:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asIntBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v;
+                        }
+                        break;
+                }
+
+                for (; i < nch * toberead2; i++) {
+                    inbuf[nch * inbuflen + i] = 0;
+                }
+
+                inbuflen += toberead2;
+
+                sumread += nsmplread;
+
+                ending = sumread >= chanklen;
+                //                ending = fpi.available() == 0 || sumread >= chanklen;
+
+                //              nsmplwrt1 = ((rp - 1) * sfrq / fs1 + inbuflen - n1x) * dfrq * osf / sfrq;
+                //              if (nsmplwrt1 > n2b2) { nsmplwrt1 = n2b2; }
+                nsmplwrt1 = n2b2;
+
+                // apply stage 1 filter
+
+                ip = ((sfrq * (rp - 1) + fs1) / fs1) * nch; // inbuf
+
+                s1p_backup = s1p;
+                ip_backup = ip;
+                osc_backup = osc;
+
+                for (ch = 0; ch < nch; ch++) {
+                    int op = ch; // outbuf
+                    //                  int fdo = fs1 / (dfrq * osf);
+                    int no = n1y * osf;
+
+                    s1p = s1p_backup;
+                    ip = ip_backup + ch;
+
+                    switch (n1x) {
+                        case 7:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                int s1o = f1order[s1p];
+
+                                buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] +
+                                        stage1[s1o][1] * inbuf[ip + 1 * nch] +
+                                        stage1[s1o][2] * inbuf[ip + 2 * nch] +
+                                        stage1[s1o][3] * inbuf[ip + 3 * nch] +
+                                        stage1[s1o][4] * inbuf[ip + 4 * nch] +
+                                        stage1[s1o][5] * inbuf[ip + 5 * nch] +
+                                        stage1[s1o][6] * inbuf[ip + 6 * nch];
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+
+                        case 9:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                int s1o = f1order[s1p];
+
+                                buf2[ch][p] = stage1[s1o][0] * inbuf[ip + 0 * nch] +
+                                        stage1[s1o][1] * inbuf[ip + 1 * nch] +
+                                        stage1[s1o][2] * inbuf[ip + 2 * nch] +
+                                        stage1[s1o][3] * inbuf[ip + 3 * nch] +
+                                        stage1[s1o][4] * inbuf[ip + 4 * nch] +
+                                        stage1[s1o][5] * inbuf[ip + 5 * nch] +
+                                        stage1[s1o][6] * inbuf[ip + 6 * nch] +
+                                        stage1[s1o][7] * inbuf[ip + 7 * nch] +
+                                        stage1[s1o][8] * inbuf[ip + 8 * nch];
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+
+                        default:
+                            for (p = 0; p < nsmplwrt1; p++) {
+                                double tmp = 0;
+                                int ip2 = ip;
+
+                                int s1o = f1order[s1p];
+
+                                for (i = 0; i < n1x; i++) {
+                                    tmp += stage1[s1o][i] * inbuf[ip2];
+                                    ip2 += nch;
+                                }
+                                buf2[ch][p] = tmp;
+
+                                ip += f1inc[s1p];
+
+                                s1p++;
+                                if (s1p == no) {
+                                    s1p = 0;
+                                }
+                            }
+                            break;
+                    }
+
+                    osc = osc_backup;
+
+                    // apply stage 2 filter
+
+                    for (p = nsmplwrt1; p < n2b; p++) {
+                        buf2[ch][p] = 0;
+                    }
+
+                    //for(i=0;i<n2b2;i++) { System.err.printf("%d:%g",i,buf2[ch][i]); }
+
+                    fft.rdft(n2b, 1, buf2[ch], fft_ip, fft_w);
+
+                    buf2[ch][0] = stage2[0] * buf2[ch][0];
+                    buf2[ch][1] = stage2[1] * buf2[ch][1];
+
+                    for (i = 1; i < n2b / 2; i++) {
+                        double re, im;
+
+                        re = stage2[i * 2] * buf2[ch][i * 2] -
+                                stage2[i * 2 + 1] * buf2[ch][i * 2 + 1];
+                        im = stage2[i * 2 + 1] * buf2[ch][i * 2] +
+                                stage2[i * 2] * buf2[ch][i * 2 + 1];
+
+                        //System.err.printf("%d : %g %g %g %g %g %g\n",i,stage2[i*2],stage2[i*2+1],buf2[ch][i*2],buf2[ch][i*2+1],re,im);
+
+                        buf2[ch][i * 2] = re;
+                        buf2[ch][i * 2 + 1] = im;
+                    }
+
+                    fft.rdft(n2b, -1, buf2[ch], fft_ip, fft_w);
+
+                    for (i = osc, j = 0; i < n2b2; i += osf, j++) {
+                        double f = (buf1[ch][j] + buf2[ch][i]);
+                        outbuf[op + j * nch] = f;
+                    }
+
+                    nsmplwrt2 = j;
+
+                    osc = i - n2b2;
+
+                    for (j = 0; i < n2b; i += osf, j++) {
+                        buf1[ch][j] = buf2[ch][i];
+                    }
+                }
+
+                rp += nsmplwrt1 * (sfrq / frqgcd) / osf;
+
+                rawoutbuf.clear();
+                if (twopass) {
+                    for (i = 0; i < nsmplwrt2 * nch; i++) {
+                        double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i];
+                        peak[0] = peak[0] < f ? f : peak[0];
+                        rawoutbuf.asDoubleBuffer().put(i, outbuf[i]);
+                    }
+                } else {
+                    switch (dbps) {
+                        case 1: {
+                            double gain2 = gain * 0x7f;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x80) {
+                                        double d = (double) s / -0x80;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x80;
+                                    }
+                                    if (0x7f < s) {
+                                        double d = (double) s / 0x7f;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7f;
+                                    }
+                                }
+
+                                rawoutbuf.put(i, (byte) (s + 0x80));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 2: {
+                            double gain2 = gain * 0x7fff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x8000) {
+                                        double d = (double) s / -0x8000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x8000;
+                                    }
+                                    if (0x7fff < s) {
+                                        double d = (double) s / 0x7fff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fff;
+                                    }
+                                }
+
+                                rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s);
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 3: {
+                            double gain2 = gain * 0x7fffff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x800000) {
+                                        double d = (double) s / -0x800000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x800000;
+                                    }
+                                    if (0x7fffff < s) {
+                                        double d = (double) s / 0x7fffff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fffff;
+                                    }
+                                }
+
+                                rawoutbuf.put(i * 3, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 1, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 2, (byte) (s & 255));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                    }
+                }
+
+                if (!init) {
+                    if (ending) {
+                        if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) {
+                            rawoutbuf.position(0);
+                            rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2;
+                        } else {
+                            rawoutbuf.position(0);
+                            int limitData = (int) (dbps * nch *
+                                    (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite));
+                            if (limitData > 0) {
+                                rawoutbuf.limit(limitData);
+                                writeBuffers(fpo, rawoutbuf);
+                            }
+                            break;
+                        }
+                    } else {
+                        rawoutbuf.position(0);
+                        rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                        writeBuffers(fpo, rawoutbuf);
+                        sumwrite += nsmplwrt2;
+                    }
+                } else {
+
+                    if (nsmplwrt2 < delay) {
+                        delay -= nsmplwrt2;
+                    } else {
+                        if (ending) {
+                            if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit(dbps * nch * (nsmplwrt2 - delay));
+                                writeBuffers(fpo, rawoutbuf);
+                                sumwrite += nsmplwrt2 - delay;
+                            } else {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit((int) (dbps * nch *
+                                        (Math.floor((double) sumread * dfrq / sfrq) + 2 + sumwrite +
+                                                nsmplwrt2 - delay)));
+                                writeBuffers(fpo, rawoutbuf);
+                                break;
+                            }
+                        } else {
+                            rawoutbuf.position(dbps * nch * delay);
+                            rawoutbuf.limit(dbps * nch * (nsmplwrt2));
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2 - delay;
+                            init = false;
+                        }
+                    }
+                }
+
+                {
+                    ds = (rp - 1) / (fs1 / sfrq);
+
+                    assert (inbuflen >= ds);
+
+                    System.arraycopy(inbuf, nch * ds, inbuf, 0,
+                            nch * (inbuflen - ds)); // memmove TODO overlap
+                    inbuflen -= ds;
+                    rp -= ds * (fs1 / sfrq);
+                }
+
+                if ((spcount++ & 7) == 7) {
+                    showprogress((double) sumread / chanklen);
+                }
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    @SuppressLint("Assert")
+    public double downsample(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps,
+                             int sfrq, int dfrq, double gain, int chanklen, boolean twopass,
+                             int dither) throws IOException {
+        int frqgcd, osf = 0, fs1, fs2;
+        double[] stage1;
+        double[][] stage2;
+        int n2, n2x, n2y, n1, n1b;
+        int filter1len;
+        int[] f2order, f2inc;
+        int[] fft_ip = null;
+        double[] fft_w = null;
+        ByteBuffer rawinbuf, rawoutbuf;
+        double[] inbuf, outbuf;
+        double[][] buf1, buf2;
+        int i, j;
+        int spcount = 0;
+        double[] peak = new double[]{0};
+
+        //        System.err.println("downsample");
+
+        filter1len = FFTFIRLEN; // stage 1 filter length
+
+        // Make stage 1 filter
+
+        {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            int ipsize, wsize;
+
+            frqgcd = gcd(sfrq, dfrq);
+
+            if (dfrq / frqgcd == 1) {
+                osf = 1;
+            } else if (dfrq / frqgcd % 2 == 0) {
+                osf = 2;
+            } else if (dfrq / frqgcd % 3 == 0) {
+                osf = 3;
+            } else {
+                throw new IllegalArgumentException(String.format(
+                        "Resampling from %dHz to %dHz is not supported.\n" +
+                                "%d/gcd(%d,%d)=%d must be divided by 2 or 3.", sfrq, dfrq, dfrq,
+                        sfrq, dfrq, dfrq / frqgcd));
+            }
+
+            fs1 = sfrq * osf;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n1 = filter1len;
+            for (i = 1; ; i = i * 2) {
+                n1 = filter1len * i;
+                if (n1 % 2 == 0) {
+                    n1--;
+                }
+                df = (fs1 * d) / (n1 - 1);
+                lpf = (dfrq - df) / 2;
+                if (df < DF) {
+                    break;
+                }
+            }
+
+            alp = alpha(aa);
+
+            iza = I0Bessel.value(alp);
+//            LogFunction.log("须打log", "不打的话在某些机型上会卡住");
+            //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK
+
+            for (n1b = 1; n1b < n1; n1b *= 2) {
+            }
+            n1b *= 2;
+
+            stage1 = new double[n1b];
+
+            for (i = -(n1 / 2); i <= n1 / 2; i++) {
+                stage1[i + n1 / 2] =
+                        win(i, n1, alp, iza) * hn_lpf(i, lpf, fs1) * fs1 / sfrq / n1b * 2;
+                //System.err.printf("1: %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]); // OK
+            }
+
+            ipsize = (int) (2 + Math.sqrt(n1b));
+            fft_ip = new int[ipsize];
+            fft_ip[0] = 0;
+            wsize = n1b / 2;
+            fft_w = new double[wsize];
+
+            fft.rdft(n1b, 1, stage1, fft_ip, fft_w);
+            //for (i = -(n1 / 2); i <= n1 / 2; i++) {
+            // System.err.printf("1': %06d: %e\n", i + n1 / 2, stage1[i + n1 / 2]);
+            //}
+            //for (i = 0; i < ipsize; i++) {
+            // System.err.printf("ip: %06d: %d\n", i, fft_ip[i]); // OK
+            //}
+            //for (i = 0; i < wsize; i++) {
+            // System.err.printf("w: %06d: %e\n", i, fft_w[i]); // OK
+            //}
+        }
+
+        // Make stage 2 filter
+
+        if (osf == 1) {
+            fs2 = sfrq / frqgcd * dfrq;
+            n2 = 1;
+            n2y = n2x = 1;
+            f2order = new int[n2y];
+            f2order[0] = 0;
+            f2inc = new int[n2y];
+            f2inc[0] = sfrq / dfrq;
+            stage2 = new double[n2y][n2x];
+            stage2[0][0] = 1;
+        } else {
+            double aa = AA; // stop band attenuation(dB)
+            double lpf, d, df, alp, iza;
+            //          double delta;
+            double guard = 2;
+
+            fs2 = sfrq / frqgcd * dfrq;
+
+            df = (fs1 / 2 - sfrq / 2) * 2 / guard;
+            lpf = sfrq / 2 + (fs1 / 2 - sfrq / 2) / guard;
+
+            //          delta = Math.pow(10, -aa / 20);
+            if (aa <= 21) {
+                d = 0.9222;
+            } else {
+                d = (aa - 7.95) / 14.36;
+            }
+
+            n2 = (int) (fs2 / df * d + 1);
+            if (n2 % 2 == 0) {
+                n2++;
+            }
+
+            alp = alpha(aa);
+            iza = I0Bessel.value(alp);
+            //System.err.printf("iza %f, alp: %f\n", iza, alp); // OK
+
+            n2y = fs2 / fs1; // 0Tvfs2Tv?H
+            n2x = n2 / n2y + 1;
+
+            f2order = new int[n2y];
+            for (i = 0; i < n2y; i++) {
+                f2order[i] = fs2 / fs1 - (i * (fs2 / dfrq)) % (fs2 / fs1);
+                if (f2order[i] == fs2 / fs1) {
+                    f2order[i] = 0;
+                }
+            }
+
+            f2inc = new int[n2y];
+            for (i = 0; i < n2y; i++) {
+                f2inc[i] = (fs2 / dfrq - f2order[i]) / (fs2 / fs1) + 1;
+                if (f2order[i + 1 == n2y ? 0 : i + 1] == 0) {
+                    f2inc[i]--;
+                }
+            }
+
+            stage2 = new double[n2y][n2x];
+
+            //System.err.printf("n2y: %d, n2: %d\n", n2y, n2);
+            for (i = -(n2 / 2); i <= n2 / 2; i++) {
+                stage2[(i + n2 / 2) % n2y][(i + n2 / 2) / n2y] =
+                        win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1;
+                //System.err.printf(" stage2[%02d][%02d]: %f\n", (i + n2 / 2) % n2y, (i + n2 / 2) / n2y, win(i, n2, alp, iza) * hn_lpf(i, lpf, fs2) * fs2 / fs1); // OK
+            }
+        }
+
+        // Apply filters
+
+        setstarttime();
+
+        {
+            int n1b2 = n1b / 2;
+            int rp; // inbuffs1Tv???
+            int rps; // rp(fs1/sfrq=osf)]
+            int rp2; // buf2fs2Tv???
+            int ds; // disposesfrqTv?
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            //          int nsmplwrt1;
+            // ?t@Cinbuf?lvZ stage2 filternTv?
+            int nsmplwrt2 = 0;
+            int s2p; // stage1 filter?oTv?n1y*osf]
+            boolean init, ending;
+            //          int osc;
+            int bp; // rp2vZ?Dbuf2Tvu
+            int rps_backup, s2p_backup;
+            int k, ch, p;
+            int inbuflen = 0;
+            int sumread, sumwrite;
+            int delay = 0;
+            int op;
+
+            // |....B....|....C....| buf1 n1b2+n1b2
+            // |.A.|....D....| buf2 n2x+n1b2
+            //
+            // inbufBosf{TvORs?[
+            // CNA
+            // BCstage 1 filter
+            // DB
+            // ADstage 2 filter
+            // DA
+            // CDRs?[
+
+            buf1 =
+                    new double[nch][n1b];                                      //rawoutbuf = calloc(nch*(n2b2/osf+1),dbps);
+
+            buf2 = new double[nch][n2x + 1 + n1b2];
+
+            rawinbuf = ByteBuffer.allocate((nch * (n1b2 / osf + osf + 1)) * bps);
+            //System.err.println((double) n1b2 * sfrq / dfrq + 1);
+            rawoutbuf =
+                    ByteBuffer.allocate((int) (((double) n1b2 * dfrq / sfrq + 1) * (dbps * nch)));
+            inbuf = new double[nch * (n1b2 / osf + osf + 1)];
+            outbuf = new double[(int) (nch * ((double) n1b2 * dfrq / sfrq + 1))];
+
+            op = 0; // outbuf
+
+            s2p = 0;
+            rp = 0;
+            rps = 0;
+            ds = 0;
+            //          osc = 0;
+            rp2 = 0;
+
+            init = true;
+            ending = false;
+            delay = (int) ((double) n1 / 2 / ((double) fs1 / dfrq) +
+                    (double) n2 / 2 / ((double) fs2 / dfrq));
+            sumread = sumwrite = 0;
+
+            while (true) {
+                int nsmplread;
+                int toberead;
+                rps = 0;   //TODO settings this parameter to zero fixed a lot of problems
+                toberead = (n1b2 - rps - 1) / osf + 1;
+                if (toberead + sumread > chanklen) {
+                    toberead = chanklen - sumread;
+                }
+
+                rawinbuf.position(0);
+                rawinbuf.limit(bps * nch * toberead);
+
+                byte[] tempData = new byte[rawinbuf.limit()];
+                nsmplread = fpi.read(tempData);
+                if (nsmplread < 0) {
+                    nsmplread = 0;
+                }
+
+                //TODO sometimes happens, investigate around it
+                if (nsmplread < rawinbuf.limit()) {
+                    chanklen = sumread + nsmplread / bps * nch;
+                }
+
+                rawinbuf.limit(nsmplread);
+                rawinbuf = ByteBuffer.wrap(tempData);
+                rawinbuf.position(nsmplread);
+
+                rawinbuf.flip();
+                nsmplread /= bps * nch;
+
+                switch (bps) {
+                    case 1:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7f) * ((rawinbuf.get(i) & 0xff) - 128);
+                        }
+                        break;
+
+                    case 2:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).asShortBuffer().get(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fff) * v;
+                            //                            System.err.printf("I: %f\n", inbuf[nch * inbuflen + i]);
+                        }
+                        break;
+
+                    case 3:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            inbuf[nch * inbuflen + i] =
+                                    (1 / (double) 0x7fffff) * (((rawinbuf.get(i * 3) & 0xff) << 0) |
+                                            ((rawinbuf.get(i * 3 + 1) & 0xff) << 8) |
+                                            ((rawinbuf.get(i * 3 + 2) & 0xff) << 16));
+                        }
+                        break;
+
+                    case 4:
+                        for (i = 0; i < nsmplread * nch; i++) {
+                            int v = rawinbuf.order(byteOrder).getInt(i);
+                            inbuf[nch * inbuflen + i] = (1 / (double) 0x7fffffff) * v;
+                        }
+                        break;
+                }
+
+                for (; i < nch * toberead; i++) {
+                    inbuf[i] = 0;
+                }
+
+                sumread += nsmplread;
+
+                //                ending = sumread >= chanklen;
+                ending = fpi.available() < 0 || sumread >= chanklen;
+
+                rps_backup = rps;
+                s2p_backup = s2p;
+
+                for (ch = 0; ch < nch; ch++) {
+                    rps = rps_backup;
+
+                    for (k = 0; k < rps; k++) {
+                        buf1[ch][k] = 0;
+                    }
+
+                    for (i = rps, j = 0; i < n1b2; i += osf, j++) {
+                        assert (j < ((n1b2 - rps - 1) / osf + 1));
+
+                        buf1[ch][i] = inbuf[j * nch + ch];
+
+                        for (k = i + 1; k < i + osf; k++) {
+                            buf1[ch][k] = 0;
+                        }
+                    }
+
+                    assert (j == ((n1b2 - rps - 1) / osf + 1));
+
+                    for (k = n1b2; k < n1b; k++) {
+                        buf1[ch][k] = 0;
+                    }
+
+                    rps = i - n1b2;
+                    rp += j;
+
+                    fft.rdft(n1b, 1, buf1[ch], fft_ip, fft_w);
+
+                    buf1[ch][0] = stage1[0] * buf1[ch][0];
+                    buf1[ch][1] = stage1[1] * buf1[ch][1];
+
+                    for (i = 1; i < n1b2; i++) {
+                        double re, im;
+
+                        re = stage1[i * 2] * buf1[ch][i * 2] -
+                                stage1[i * 2 + 1] * buf1[ch][i * 2 + 1];
+                        im = stage1[i * 2 + 1] * buf1[ch][i * 2] +
+                                stage1[i * 2] * buf1[ch][i * 2 + 1];
+
+                        buf1[ch][i * 2] = re;
+                        buf1[ch][i * 2 + 1] = im;
+                    }
+
+                    fft.rdft(n1b, -1, buf1[ch], fft_ip, fft_w);
+
+                    for (i = 0; i < n1b2; i++) {
+                        buf2[ch][n2x + 1 + i] += buf1[ch][i];
+                    }
+
+                    {
+                        int t1 = rp2 / (fs2 / fs1);
+                        if (rp2 % (fs2 / fs1) != 0) {
+                            t1++;
+                        }
+
+                        bp = buf2[0].length * ch + t1; // &(buf2[ch][t1]);
+                    }
+
+                    s2p = s2p_backup;
+
+                    for (p = 0; bp - (buf2[0].length * ch) < n1b2 + 1; p++) { // buf2[ch]
+                        double tmp = 0;
+                        int bp2;
+                        int s2o;
+
+                        bp2 = bp;
+                        s2o = f2order[s2p];
+                        bp += f2inc[s2p];
+                        s2p++;
+
+                        if (s2p == n2y) {
+                            s2p = 0;
+                        }
+
+                        assert ((bp2 - (buf2[0].length * ch)) * (fs2 / fs1) -
+                                (rp2 + p * (fs2 / dfrq)) == s2o); // &(buf2[ch][0])
+                        for (i = 0; i < n2x; i++) {
+                            //System.err.printf("%d (%d, %d)\n", i, bp2 / buf2[0].length, bp2 % buf2[0].length);
+                            tmp += stage2[s2o][i] *
+                                    buf2[bp2 / buf2[0].length][bp2 % buf2[0].length]; // *bp2++
+                            bp2++;
+                        }
+
+                        outbuf[op + p * nch + ch] = tmp;
+                        //System.err.printf("O: %06d: %f\n", op + p * nch + ch, tmp);
+                    }
+
+                    nsmplwrt2 = p;
+                }
+
+                rp2 += nsmplwrt2 * (fs2 / dfrq);
+
+                rawoutbuf.clear();
+                if (twopass) {
+                    for (i = 0; i < nsmplwrt2 * nch; i++) {
+                        double f = outbuf[i] > 0 ? outbuf[i] : -outbuf[i];
+                        peak[0] = peak[0] < f ? f : peak[0];
+                        //System.err.println("p: " + rawoutbuf.position() + ", l: " + rawoutbuf.limit());
+                        rawoutbuf.asDoubleBuffer().put(i, outbuf[i]);
+                        //if (i < 100) {
+                        // System.err.printf("1: %06d: %f\n", i, outbuf[i]);
+                        //}
+                        //System.err.print(StringUtil.getDump(rawoutbuf, i, 8));
+                    }
+                } else {
+                    switch (dbps) {
+                        case 1: {
+                            double gain2 = gain * 0x7f;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x80) {
+                                        double d = (double) s / -0x80;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x80;
+                                    }
+                                    if (0x7f < s) {
+                                        double d = (double) s / 0x7f;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7f;
+                                    }
+                                }
+
+                                rawoutbuf.put(i, (byte) (s + 0x80));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 2: {
+                            double gain2 = gain * 0x7fff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x8000) {
+                                        double d = (double) s / -0x8000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x8000;
+                                    }
+                                    if (0x7fff < s) {
+                                        double d = (double) s / 0x7fff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fff;
+                                    }
+                                }
+
+                                rawoutbuf.order(byteOrder).asShortBuffer().put(i, (short) s);
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                        case 3: {
+                            double gain2 = gain * 0x7fffff;
+                            ch = 0;
+
+                            for (i = 0; i < nsmplwrt2 * nch; i++) {
+                                int s;
+
+                                if (dither != 0) {
+                                    s = do_shaping(outbuf[i] * gain2, peak, dither, ch);
+                                } else {
+                                    s = RINT(outbuf[i] * gain2);
+
+                                    if (s < -0x800000) {
+                                        double d = (double) s / -0x800000;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = -0x800000;
+                                    }
+                                    if (0x7fffff < s) {
+                                        double d = (double) s / 0x7fffff;
+                                        peak[0] = peak[0] < d ? d : peak[0];
+                                        s = 0x7fffff;
+                                    }
+                                }
+
+                                rawoutbuf.put(i * 3, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 1, (byte) (s & 255));
+                                s >>= 8;
+                                rawoutbuf.put(i * 3 + 2, (byte) (s & 255));
+
+                                ch++;
+                                if (ch == nch) {
+                                    ch = 0;
+                                }
+                            }
+                        }
+                        break;
+
+                    }
+                }
+
+                if (!init) {
+                    if (ending) {
+                        if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2) {
+                            rawoutbuf.position(0);
+                            rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2;
+                        } else {
+                            rawoutbuf.position(0);
+                            int limitData = (int) (dbps * nch *
+                                    (Math.floor((double) sumread * dfrq / sfrq) + 2 - sumwrite));
+                            if (limitData > 0) {
+                                rawoutbuf.limit(limitData);
+                                writeBuffers(fpo, rawoutbuf);
+                            }
+                            break;
+                        }
+                    } else {
+                        rawoutbuf.position(0);
+                        rawoutbuf.limit(dbps * nch * nsmplwrt2);
+                        writeBuffers(fpo, rawoutbuf);
+                        sumwrite += nsmplwrt2;
+                    }
+                } else {
+                    if (nsmplwrt2 < delay) {
+                        delay -= nsmplwrt2;
+                    } else {
+                        if (ending) {
+                            if ((double) sumread * dfrq / sfrq + 2 > sumwrite + nsmplwrt2 - delay) {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit(dbps * nch * (nsmplwrt2 - delay));
+                                writeBuffers(fpo, rawoutbuf);
+                                sumwrite += nsmplwrt2 - delay;
+                            } else {
+                                rawoutbuf.position(dbps * nch * delay);
+                                rawoutbuf.limit((int) (dbps * nch *
+                                        (Math.floor((double) sumread * dfrq / sfrq) + 2 + sumwrite +
+                                                nsmplwrt2 -
+                                                delay)));  //TODO fails with short signals (think that fixed this)
+                                writeBuffers(fpo, rawoutbuf);
+                                break;
+                            }
+                        } else {
+                            rawoutbuf.position(dbps * nch * delay);
+                            rawoutbuf.limit(dbps * nch * (nsmplwrt2));
+                            writeBuffers(fpo, rawoutbuf);
+                            sumwrite += nsmplwrt2 - delay;
+                            init = false;
+                        }
+                    }
+                }
+
+                {
+                    ds = (rp2 - 1) / (fs2 / fs1);
+
+                    if (ds > n1b2) {
+                        ds = n1b2;
+                    }
+
+                    for (ch = 0; ch < nch; ch++) {
+                        System.arraycopy(buf2[ch], ds, buf2[ch], 0,
+                                n2x + 1 + n1b2 - ds); // memmove TODO overlap
+                    }
+
+                    rp2 -= ds * (fs2 / fs1);
+                }
+
+                for (ch = 0; ch < nch; ch++) {
+                    System.arraycopy(buf1[ch], n1b2, buf2[ch], n2x + 1, n1b2);
+                }
+
+                if ((spcount++ & 7) == 7) {
+                    showprogress((double) sumread / chanklen);
+                }
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    public double no_src(InputStream fpi, OutputStream fpo, int nch, int bps, int dbps, double gain,
+                         int chanklen, boolean twopass, int dither) throws IOException {
+        double[] peak = new double[]{0};
+        int ch = 0, sumread = 0;
+
+        setstarttime();
+
+        ByteBuffer leos = null;
+        if (twopass) {
+            leos = ByteBuffer.allocate(8);
+        }
+
+        ByteBuffer buf = ByteBuffer.allocate(4);
+        while (sumread < chanklen * nch) {
+            double f = 0;
+            int s;
+
+            switch (bps) {
+                case 1:
+                    buf.position(0);
+                    buf.limit(1);
+
+                    byte[] tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+
+                    buf.flip();
+                    f = (1 / (double) 0x7f) * (buf.get(0) - 128);
+                    break;
+                case 2:
+                    buf.position(0);
+                    buf.limit(2);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    s = buf.order(byteOrder).asShortBuffer().get(0);
+                    f = (1 / (double) 0x7fff) * s;
+                    break;
+                case 3:
+                    buf.position(0);
+                    buf.limit(3);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    f = (1 / (double) 0x7fffff) * (((buf.get(0) & 0xff) << 0) |
+                            ((buf.get(1) & 0xff) << 8) |
+                            ((buf.get(2) & 0xff) << 16));
+                    break;
+                case 4:
+                    buf.position(0);
+                    buf.limit(4);
+
+                    tempData = new byte[buf.limit()];
+                    fpi.read(tempData);
+                    buf = ByteBuffer.wrap(tempData);
+                    buf.position(buf.limit());
+
+                    buf.flip();
+                    s = buf.order(byteOrder).asIntBuffer().get(0);
+                    f = (1 / (double) 0x7fffffff) * s;
+                    break;
+            }
+
+            if (fpi.available() == 0) {
+                //            if (fpi.position() == fpi.size()) {
+                break;
+            }
+            f *= gain;
+
+            if (!twopass) {
+                switch (dbps) {
+                    case 1:
+                        f *= 0x7f;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(1);
+                        buf.put(0, (byte) (s + 128));
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                    case 2:
+                        f *= 0x7fff;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(2);
+                        buf.asShortBuffer().put(0, (short) s);
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                    case 3:
+                        f *= 0x7fffff;
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+                        buf.position(0);
+                        buf.limit(3);
+                        buf.put(0, (byte) (s & 255));
+                        s >>= 8;
+                        buf.put(1, (byte) (s & 255));
+                        s >>= 8;
+                        buf.put(2, (byte) (s & 255));
+                        buf.flip();
+                        writeBuffers(fpo, buf);
+                        break;
+                }
+            } else {
+                double p = f > 0 ? f : -f;
+                peak[0] = peak[0] < p ? p : peak[0];
+                leos.position(0);
+                leos.putDouble(f);
+                leos.flip();
+                writeBuffers(fpo, leos);
+            }
+
+            ch++;
+            if (ch == nch) {
+                ch = 0;
+            }
+            sumread++;
+
+            if ((sumread & 0x3ffff) == 0) {
+                showprogress((double) sumread / (chanklen * nch));
+            }
+        }
+
+        showprogress(1);
+
+        return peak[0];
+    }
+
+    /** */
+    public static void main(String[] args) throws Exception {
+        new SSRC(args);
+    }
+
+    /** */
+    private static final double presets[] = {0.7, 0.9, 0.18};
+
+    public SSRC() {
+    }
+
+    /** */
+    SSRC(String[] argv) throws IOException {
+        String sfn, dfn, tmpfn = null;
+        FileInputStream fpi = null;
+        File fo = null;
+        FileOutputStream fpo = null;
+        File ft = null;
+        FileOutputStream fpto = null;
+        boolean twopass, normalize;
+        int dither, pdf, samp = 0;
+        int nch, bps;
+        int length;
+        int sfrq, dfrq, dbps;
+        double att, noiseamp;
+        double[] peak = new double[]{0};
+        int i;
+
+        // parse command line options
+
+        dfrq = -1;
+        att = 0;
+        dbps = -1;
+        twopass = false;
+        normalize = false;
+        dither = 0;
+        pdf = 0;
+        noiseamp = 0.18;
+
+        for (i = 0; i < argv.length; i++) {
+            if (argv[i].charAt(0) != '-') {
+                break;
+            }
+
+            if (argv[i].equals("--rate")) {
+                dfrq = Integer.parseInt(argv[++i]);
+                //System.err.printf("dfrq: %d\n", dfrq);
+                continue;
+            }
+
+            if (argv[i].equals("--att")) {
+                att = Float.parseFloat(argv[++i]);
+                continue;
+            }
+
+            if (argv[i].equals("--bits")) {
+                dbps = Integer.parseInt(argv[++i]);
+                if (dbps != 8 && dbps != 16 && dbps != 24) {
+                    throw new IllegalArgumentException(
+                            "Error: Only 8bit, 16bit and 24bit PCM are supported.");
+                }
+                dbps /= 8;
+                continue;
+            }
+
+            if (argv[i].equals("--twopass")) {
+                twopass = true;
+                continue;
+            }
+
+            if (argv[i].equals("--normalize")) {
+                twopass = true;
+                normalize = true;
+                continue;
+            }
+
+            if (argv[i].equals("--dither")) {
+                try {
+                    dither = Integer.parseInt(argv[i + 1]);
+                    if (dither < 0 || dither > 4) {
+                        throw new IllegalArgumentException(
+                                "unrecognized dither type : " + argv[i + 1]);
+                    }
+                    i++;
+                } catch (NumberFormatException e) {
+                    dither = -1;
+                }
+                continue;
+            }
+
+            if (argv[i].equals("--pdf")) {
+                try {
+                    pdf = Integer.parseInt(argv[i + 1]);
+                    if (pdf < 0 || pdf > 2) {
+                        throw new IllegalArgumentException(
+                                "unrecognized p.d.f. type : " + argv[i + 1]);
+                    }
+                    i++;
+                } catch (NumberFormatException e) {
+                    throw new IllegalArgumentException("unrecognized p.d.f. type : " + argv[i + 1]);
+                }
+
+                try {
+                    noiseamp = Double.parseDouble(argv[i + 1]);
+                    i++;
+                } catch (NumberFormatException e) {
+                    noiseamp = presets[pdf];
+                }
+
+                continue;
+            }
+
+            if (argv[i].equals("--quiet")) {
+                quiet = true;
+                continue;
+            }
+
+            if (argv[i].equals("--tmpfile")) {
+                tmpfn = argv[++i];
+                continue;
+            }
+
+            if (argv[i].equals("--profile")) {
+                //noinspection IfCanBeSwitch
+                if (argv[i + 1].equals("fast")) {
+                    AA = 96;
+                    DF = 8000;
+                    FFTFIRLEN = 1024;
+                } else if (argv[i + 1].equals("standard")) {
+                    /* nothing to do */
+                } else {
+                    throw new IllegalArgumentException("unrecognized profile : " + argv[i + 1]);
+                }
+                i++;
+                continue;
+            }
+
+            throw new IllegalArgumentException("unrecognized option : " + argv[i]);
+        }
+
+        if (!quiet) {
+            System.err.printf("Shibatch sampling rate converter version " + VERSION +
+                    "(high precision/nio)\n\n");
+        }
+
+        if (argv.length - i != 2) {
+            usage();
+            throw new IllegalStateException("too few arguments");
+        }
+
+        sfn = argv[i];
+        dfn = argv[i + 1];
+
+        try {
+            fpi = new FileInputStream(sfn);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("cannot open input file.");
+        }
+
+        // read wav header
+
+        {
+            @SuppressWarnings("unused") short word;
+            @SuppressWarnings("unused") int dword;
+
+            ByteBuffer bb = ByteBuffer.allocate(256).order(ByteOrder.LITTLE_ENDIAN);
+            bb.limit(36);
+            fpi.getChannel().read(bb);
+            bb.flip();
+            System.err.println("p: " + bb.position() + ", l: " + bb.limit());
+            if (bb.get() != 'R') {
+                fmterr(1);
+            }
+            if (bb.get() != 'I') {
+                fmterr(1);
+            }
+            if (bb.get() != 'F') {
+                fmterr(1);
+            }
+            if (bb.get() != 'F') {
+                fmterr(1);
+            }
+
+            dword = bb.getInt();
+
+            if (bb.get() != 'W') {
+                fmterr(2);
+            }
+            if (bb.get() != 'A') {
+                fmterr(2);
+            }
+            if (bb.get() != 'V') {
+                fmterr(2);
+            }
+            if (bb.get() != 'E') {
+                fmterr(2);
+            }
+            if (bb.get() != 'f') {
+                fmterr(2);
+            }
+            if (bb.get() != 'm') {
+                fmterr(2);
+            }
+            if (bb.get() != 't') {
+                fmterr(2);
+            }
+            if (bb.get() != ' ') {
+                fmterr(2);
+            }
+
+            int sizeOfFmt = bb.getInt();
+
+            if (bb.getShort() != 1) {
+                throw new IllegalStateException("Error: Only PCM is supported.");
+            }
+            nch = bb.getShort();
+            sfrq = bb.getInt();
+            bps = bb.getInt();
+            if (bps % sfrq * nch != 0) {
+                fmterr(4);
+            }
+
+            word = bb.getShort();
+            word = bb.getShort();
+
+            bps /= sfrq * nch;
+
+            if (sizeOfFmt > 16) {
+                bb.position(0);
+                bb.limit(2);
+                fpi.read(getDataFromByteBuffer(bb));
+                bb.flip();
+                int sizeofExtended = bb.getShort();
+                fpi.getChannel().position(fpi.getChannel().position() + sizeofExtended);
+            }
+
+            while (true) {
+                bb.position(0);
+                bb.limit(8);
+                fpi.getChannel().read(bb);
+                bb.flip();
+                int c0 = bb.get();
+                int c1 = bb.get();
+                int c2 = bb.get();
+                int c3 = bb.get();
+                length = bb.getInt();
+                System.err.printf("chunk: %c%c%c%c\n", c0, c1, c2, c3);
+                if (c0 == 'd' && c1 == 'a' && c2 == 't' && c3 == 'a') {
+                    break;
+                }
+                if (fpi.getChannel().position() == fpi.getChannel().size()) {
+                    break;
+                }
+                fpi.getChannel().position(fpi.getChannel().position() + length);
+            }
+            if (fpi.getChannel().position() == fpi.getChannel().size()) {
+                throw new IllegalStateException("Couldn't find data chank");
+            }
+        }
+
+        if (bps != 1 && bps != 2 && bps != 3 && bps != 4) {
+            throw new IllegalStateException(
+                    "Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported.");
+        }
+
+        if (dbps == -1) {
+            if (bps != 1) {
+                dbps = bps;
+            } else {
+                dbps = 2;
+            }
+            if (dbps == 4) {
+                dbps = 3;
+            }
+        }
+
+        if (dfrq == -1) {
+            dfrq = sfrq;
+        }
+
+        if (dither == -1) {
+            if (dbps < bps) {
+                if (dbps == 1) {
+                    dither = 4;
+                } else {
+                    dither = 3;
+                }
+            } else {
+                dither = 1;
+            }
+        }
+
+        if (!quiet) {
+            final String[] dtype = {"none", "no noise shaping", "triangular spectral shape",
+                    "ATH based noise shaping", "ATH based noise shaping(less amplitude)"};
+            final String[] ptype = {"rectangular", "triangular", "gaussian"};
+            System.err.printf("frequency : %d -> %d\n", sfrq, dfrq);
+            System.err.printf("attenuation : %gdB\n", att);
+            System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8);
+            System.err.printf("nchannels : %d\n", nch);
+            System.err.printf("length : %d bytes, %g secs\n", length,
+                    (double) length / bps / nch / sfrq);
+            if (dither == 0) {
+                System.err.printf("dither type : none\n");
+            } else {
+                System.err
+                        .printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf],
+                                noiseamp);
+            }
+            System.err.printf("\n");
+        }
+
+        if (twopass) {
+        }
+
+        try {
+            fo = new File(dfn);
+            fpo = new FileOutputStream(fo);
+        } catch (IOException e) {
+            throw new IllegalArgumentException("cannot open output file.");
+        }
+
+        // generate wav header
+
+        {
+            short word;
+            int dword;
+
+            ByteBuffer leos = ByteBuffer.allocate(44).order(ByteOrder.LITTLE_ENDIAN);
+
+            leos.put("RIFF".getBytes());
+            dword = 0;
+            leos.putInt(dword);
+
+            leos.put("WAVEfmt ".getBytes());
+            dword = 16;
+            leos.putInt(dword);
+            word = 1;
+            leos.putShort(word); // inAudioFormat category, PCM
+            word = (short) nch;
+            leos.putShort(word); // channels
+            dword = dfrq;
+            leos.putInt(dword); // sampling rate
+            dword = dfrq * nch * dbps;
+            leos.putInt(dword); // bytes per sec
+            word = (short) (dbps * nch);
+            leos.putShort(word); // block alignment
+            word = (short) (dbps * 8);
+            leos.putShort(word); // bits per sample
+
+            leos.put("data".getBytes());
+            dword = 0;
+            leos.putInt(dword);
+
+            leos.flip();
+            writeBuffers(fpo, leos);
+        }
+
+        if (dither != 0) {
+            int min = 0, max = 0;
+            if (dbps == 1) {
+                min = -0x80;
+                max = 0x7f;
+            }
+            if (dbps == 2) {
+                min = -0x8000;
+                max = 0x7fff;
+            }
+            if (dbps == 3) {
+                min = -0x800000;
+                max = 0x7fffff;
+            }
+            if (dbps == 4) {
+                min = -0x80000000;
+                max = 0x7fffffff;
+            }
+
+            samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp);
+        }
+
+        if (twopass) {
+            double gain = 0;
+            int ch = 0;
+            int fptlen, sumread;
+
+            if (!quiet) {
+                System.err.printf("Pass 1\n");
+            }
+
+            try {
+                if (tmpfn != null) {
+                    ft = new File(tmpfn);
+                } else {
+                    ft = File.createTempFile("ssrc_", ".tmp");
+                }
+                fpto = new FileOutputStream(ft);
+            } catch (IOException e) {
+                throw new IllegalStateException("cannot open temporary file.");
+            }
+
+            //System.err.printf("nch: %d, bps: %d, size: %d, sfrq: %d, dfrq: %d, ???: %d, ???: %d, twopass: %b, dither: %d\n", nch, bps, 8, sfrq, dfrq, 1, length / bps / nch, twopass, dither);
+            if (normalize) {
+                if (sfrq < dfrq) {
+                    peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch,
+                            twopass, dither);
+                } else if (sfrq > dfrq) {
+                    peak[0] = downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, 1, length / bps / nch,
+                            twopass, dither);
+                } else {
+                    peak[0] =
+                            no_src(fpi, fpto, nch, bps, 8, 1, length / bps / nch, twopass, dither);
+                }
+            } else {
+                if (sfrq < dfrq) {
+                    peak[0] = upsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20),
+                            length / bps / nch, twopass, dither);
+                } else if (sfrq > dfrq) {
+                    peak[0] =
+                            downsample(fpi, fpto, nch, bps, 8, sfrq, dfrq, Math.pow(10, -att / 20),
+                                    length / bps / nch, twopass, dither);
+                } else {
+                    peak[0] = no_src(fpi, fpto, nch, bps, 8, Math.pow(10, -att / 20),
+                            length / bps / nch, twopass, dither);
+                }
+            }
+
+            fpto.close();
+
+            if (!quiet) {
+                System.err.printf("\npeak : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+
+            if (!normalize) {
+                if (peak[0] < Math.pow(10, -att / 20)) {
+                    peak[0] = 1;
+                } else {
+                    peak[0] *= Math.pow(10, att / 20);
+                }
+            } else {
+                peak[0] *= Math.pow(10, att / 20);
+            }
+
+            if (!quiet) {
+                System.err.printf("\nPass 2\n");
+            }
+
+            if (dither != 0) {
+                switch (dbps) {
+                    case 1:
+                        gain = (normalize || peak[0] >= (0x7f - samp) / (double) 0x7f) ?
+                                1 / peak[0] * (0x7f - samp) : 1 / peak[0] * 0x7f;
+                        break;
+                    case 2:
+                        gain = (normalize || peak[0] >= (0x7fff - samp) / (double) 0x7fff) ?
+                                1 / peak[0] * (0x7fff - samp) : 1 / peak[0] * 0x7fff;
+                        break;
+                    case 3:
+                        gain = (normalize || peak[0] >= (0x7fffff - samp) / (double) 0x7fffff) ?
+                                1 / peak[0] * (0x7fffff - samp) : 1 / peak[0] * 0x7fffff;
+                        break;
+                }
+            } else {
+                switch (dbps) {
+                    case 1:
+                        gain = 1 / peak[0] * 0x7f;
+                        break;
+                    case 2:
+                        gain = 1 / peak[0] * 0x7fff;
+                        break;
+                    case 3:
+                        gain = 1 / peak[0] * 0x7fffff;
+                        break;
+                }
+            }
+            randptr = 0;
+
+            setstarttime();
+
+            fptlen = (int) (ft.length() / 8);
+            //System.err.println("tmp: " + fpt.getFilePointer());
+
+            FileChannel fpti = new FileInputStream(ft).getChannel();
+            ByteBuffer leis = ByteBuffer.allocate(8);
+            for (sumread = 0; sumread < fptlen; ) {
+                double f;
+                int s;
+
+                leis.clear();
+                fpti.read(leis);
+                leis.flip();
+                f = leis.getDouble();
+                //if (sumread < 100) {
+                // System.err.printf("2: %06d: %f\n", sumread, f);
+                //}
+                f *= gain;
+                sumread++;
+
+                switch (dbps) {
+                    case 1: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(1);
+                        buf.put((byte) (s + 128));
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                    case 2: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
+                        buf.putShort((short) s);
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                    case 3: {
+                        s = dither != 0 ? do_shaping(f, peak, dither, ch) : RINT(f);
+
+                        ByteBuffer buf = ByteBuffer.allocate(3);
+                        buf.put((byte) (s & 255));
+                        s >>= 8;
+                        buf.put((byte) (s & 255));
+                        s >>= 8;
+                        buf.put((byte) (s & 255));
+                        buf.flip();
+
+                        writeBuffers(fpo, buf);
+                    }
+                    break;
+                }
+
+                ch++;
+                if (ch == nch) {
+                    ch = 0;
+                }
+
+                if ((sumread & 0x3ffff) == 0) {
+                    showprogress((double) sumread / fptlen);
+                }
+            }
+            showprogress(1);
+            if (!quiet) {
+                System.err.printf("\n");
+            }
+            fpti.close();
+            if (ft != null) {
+                //System.err.println("ft: " + ft);
+                if (!ft.delete()) {
+                    System.err.printf("Failed to remove %s\n", ft);
+                }
+            }
+        } else {
+            if (sfrq < dfrq) {
+                peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            } else if (sfrq > dfrq) {
+                peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            } else {
+                peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20),
+                        length / bps / nch, twopass, dither);
+            }
+            if (!quiet) {
+                System.err.printf("\n");
+            }
+        }
+
+        if (dither != 0) {
+            quit_shaper(nch);
+        }
+
+        if (!twopass && peak[0] > 1) {
+            if (!quiet) {
+                System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+        }
+
+        {
+            int dword;
+            int len;
+
+            fpo.close();
+
+            fo = new File(dfn);
+
+            len = (int) fo.length();
+            FileChannel fpo1 = new RandomAccessFile(fo, "rw").getChannel();
+            ByteBuffer leos = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN);
+
+            dword = len - 8;
+            leos.position(0);
+            leos.limit(4);
+            leos.putInt(dword);
+            leos.flip();
+            fpo1.write(leos, 4);
+
+            dword = len - 44;
+            leos.position(0);
+            leos.limit(4);
+            leos.putInt(dword);
+            leos.flip();
+            fpo1.write(leos, 40);
+
+            fpo1.close();
+        }
+    }
+
+
+    /** */
+    public SSRC(InputStream fpi, OutputStream fpo, int sfrq, int dfrq, int bps, int dbps, int nch,
+                int length, double att, int dither, boolean quiet_) throws IOException {
+        String tmpfn = null;
+        boolean twopass, normalize;
+        int pdf, samp = 0;
+        double noiseamp;
+        double[] peak = new double[]{0};
+        int i;
+
+        // parse command line options
+
+        twopass = false;
+        normalize = false;
+
+        pdf = 0;
+        noiseamp = 0.18;
+
+        if (dither < 0 || dither > 4) {
+            throw new IllegalArgumentException("unrecognized dither type : " + dither);
+        }
+        this.quiet = quiet_;
+
+        if (bps != 1 && bps != 2 && bps != 3 && bps != 4) {
+            throw new IllegalStateException(
+                    "Error : Only 8bit, 16bit, 24bit and 32bit PCM are supported.");
+        }
+
+        if (dbps == -1) {
+            if (bps != 1) {
+                dbps = bps;
+            } else {
+                dbps = 2;
+            }
+            if (dbps == 4) {
+                dbps = 3;
+            }
+        }
+
+        if (dfrq == -1) {
+            dfrq = sfrq;
+        }
+
+        if (dither == -1) {
+            if (dbps < bps) {
+                if (dbps == 1) {
+                    dither = 4;
+                } else {
+                    dither = 3;
+                }
+            } else {
+                dither = 1;
+            }
+        }
+
+        if (!quiet) {
+            final String[] dtype = {"none", "no noise shaping", "triangular spectral shape",
+                    "ATH based noise shaping", "ATH based noise shaping(less amplitude)"};
+            final String[] ptype = {"rectangular", "triangular", "gaussian"};
+            System.err.printf("frequency : %d -> %d\n", sfrq, dfrq);
+            System.err.printf("attenuation : %gdB\n", att);
+            System.err.printf("bits per sample : %d -> %d\n", bps * 8, dbps * 8);
+            System.err.printf("nchannels : %d\n", nch);
+            System.err.printf("length : %d bytes, %g secs\n", length,
+                    (double) length / bps / nch / sfrq);
+            if (dither == 0) {
+                System.err.printf("dither type : none\n");
+            } else {
+                System.err
+                        .printf("dither type : %s, %s p.d.f, amp = %g\n", dtype[dither], ptype[pdf],
+                                noiseamp);
+            }
+            System.err.printf("\n");
+        }
+
+
+        if (dither != 0) {
+            int min = 0, max = 0;
+            if (dbps == 1) {
+                min = -0x80;
+                max = 0x7f;
+            }
+            if (dbps == 2) {
+                min = -0x8000;
+                max = 0x7fff;
+            }
+            if (dbps == 3) {
+                min = -0x800000;
+                max = 0x7fffff;
+            }
+            if (dbps == 4) {
+                min = -0x80000000;
+                max = 0x7fffffff;
+            }
+
+            samp = init_shaper(dfrq, nch, min, max, dither, pdf, noiseamp);
+        }
+
+        if (sfrq < dfrq) {
+            peak[0] = upsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                    length / bps / nch, twopass, dither);
+        } else if (sfrq > dfrq) {
+            peak[0] = downsample(fpi, fpo, nch, bps, dbps, sfrq, dfrq, Math.pow(10, -att / 20),
+                    length / bps / nch, twopass, dither);
+        } else {
+            peak[0] = no_src(fpi, fpo, nch, bps, dbps, Math.pow(10, -att / 20), length / bps / nch,
+                    twopass, dither);
+        }
+
+        if (!quiet) {
+            System.err.printf("\n");
+        }
+
+        if (dither != 0) {
+            quit_shaper(nch);
+        }
+
+        if (!twopass && peak[0] > 1) {
+            if (!quiet) {
+                System.err.printf("clipping detected : %gdB\n", 20 * Math.log10(peak[0]));
+            }
+        }
+    }
+
+    protected byte[] getDataFromByteBuffer(ByteBuffer rawoutbuf) {
+        byte[] tempDataWrt = new byte[rawoutbuf.limit() - rawoutbuf.position()];
+        rawoutbuf.get(tempDataWrt, 0, tempDataWrt.length);
+
+        return tempDataWrt;
+    }
+
+    protected void writeBuffers(OutputStream fpo, ByteBuffer rawoutbuf) {
+        try {
+            fpo.write(getDataFromByteBuffer(rawoutbuf));
+        } catch (IOException e) {
+            // Some problems (Read end dead)
+        }
+    }
+}
+
+/* */

+ 2626 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/audioUtils/compose/SplitRadixFft.java

@@ -0,0 +1,2626 @@
+/*
+ * Copyright Takuya OOURA, 1996-2001
+ *
+ * You may use, copy, modify and distribute this code
+ * for any purpose (include commercial use) and without fee.
+ * Please refer to this package when you modify this code.
+ */
+package cn.i2edu.dubbing_lib.audioUtils.compose;
+
+
+/**
+ * Fast Fourier/Cosine/Sine Transform.
+ * <pre>
+ *  dimension   :one
+ *  data length :power of 2
+ *  decimation  :frequency
+ *  radix       :<b>split-radix</b>
+ *  data        :inplace
+ *  table       :use
+ * </pre>
+ * <h4>Appendix:</h4>
+ * <p>
+ * The cos/sin table is recalculated when the larger table required.
+ * w[] and ip[] are compatible with all routines.
+ * </p>
+ *
+ * @author <a href="mailto:ooura@mmm.t.u-tokyo.ac.jp">Takuya OOURA</a>
+ * @author <a href="mailto:vavivavi@yahoo.co.jp">Naohide Sano</a> (nsano)
+ * @version 0.00 060127 nsano port to java version <br>
+ */
+@SuppressWarnings({"PointlessArithmeticExpression", "JavaDoc", "SameParameterValue", "FinalPrivateMethod"})
+public class SplitRadixFft {
+
+    /** */
+    private static final int CDFT_RECURSIVE_N = 512;
+
+    /**
+     * Complex Discrete Fourier Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt;
+     *          X[k] = sum_j=0&amp;circ;n-1 x[j]*exp(2*pi*i*j*k/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt;
+     *          X[k] = sum_j=0&amp;circ;n-1 x[j]*exp(-2*pi*i*j*k/n), 0&lt;=k&lt;n
+     *      (notes: sum_j=0&amp;circ;n-1 is a summation from j=0 to n-1)
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          cdft(2*n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          cdft(2*n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          cdft(2*n, -1, a, ip, w);
+     *      is
+     *          cdft(2*n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= 2 * n - 1; j++) {
+     *              a[j] *= 1.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n    2*n data length (int)
+     *             n &gt;= 1, n = power of 2
+     * @param isgn
+     * @param a    a[0...2*n-1] input/output data (REAL *)
+     *             input data
+     *             a[2*j] = Re(x[j]),
+     *             a[2*j+1] = Im(x[j]), 0&lt;=j&lt;n
+     *             output data
+     *             a[2*k] = Re(X[k]),
+     *             a[2*k+1] = Im(X[k]), 0&lt;=k&lt;n
+     * @param ip   ip[0...*] work area for bit reversal (int *)
+     *             length of ip &gt;= 2+sqrt(n)
+     *             strictly,
+     *             length of ip &gt;=
+     *             2+(1&lt;&lt;(int)(log(n+0.5)/log(2))/2).
+     *             ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w    w[0...n/2-1] cos/sin table (REAL *)
+     *             w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void cdft(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int nw;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        if (isgn >= 0) {
+            cftfsub(n, a, ip, 2, nw, w);
+        } else {
+            cftbsub(n, a, ip, 2, nw, w);
+        }
+    }
+
+    /**
+     * Real Discrete Fourier Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; RDFT
+     *          R[k] = sum_j = 0 &amp; &circ; (n - 1) a[j] * cos(2 * pi * j * k / n), 0 &lt;= k &lt;= n / 2
+     *          I[k] = sum_j = 0 &amp; &circ; (n - 1) a[j] * sin(2 * pi * j * k / n), 0 &lt; k &lt; n / 2
+     *      &lt;case2&gt; IRDFT (excluding scale)
+     *          a[k] = (R[0] + R[n / 2] * cos(pi * k)) / 2 +
+     *              sum_j = 1 &amp; &circ; (n / 2 - 1) R[j] * cos(2 * pi * j * k / n) +
+     *              sum_j = 1 &amp; &circ; (n / 2 - 1) I[j] * sin(2 * pi * j * k / n), 0 &lt;= k &lt; n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          rdft(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          rdft(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          rdft(n, 1, a, ip, w);
+     *      is
+     *          rdft(n, -1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n    data length <br>
+     *             n &gt;= 2, n = power of 2
+     * @param isgn
+     * @param a    [0...n-1] input/output data
+     *             <pre>
+     *              &lt;case1&gt;
+     *                  output data
+     *                      a[2 * k] = R[k], 0 &lt;= k &lt; n / 2
+     *                      a[2 * k + 1] = I[k], 0 &lt; k &lt; n / 2
+     *                      a[1] = R[n/2]
+     *              &lt;case2&gt;
+     *                  input data
+     *                      a[2 * j] = R[j], 0 &lt;= j &lt; n / 2
+     *                      a[2 * j + 1] = I[j], 0 &lt; j &lt; n / 2
+     *                      a[1] = R[n / 2]
+     *             </pre>
+     * @param ip   [0...*] work area for bit reversal
+     *             <pre>
+     *              length of ip &gt;= 2 + sqrt(n / 2)
+     *              strictly,
+     *              length of ip &gt;=
+     *                  2 + (1 &lt;&lt; (int) (log(n / 2 + 0.5) / log(2)) / 2).
+     *             </pre>
+     *             ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w    [0...n/2-1] cos/sin table <br>
+     *             w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void rdft(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int nw, nc;
+        double xi;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 2)) {
+            nc = n >> 2;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xi = a[0] - a[1];
+            a[0] += a[1];
+            a[1] = xi;
+        } else {
+            a[1] = 0.5 * (a[0] - a[1]);
+            a[0] -= a[1];
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+    }
+
+    /**
+     * Discrete Cosine Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; IDCT (excluding scale)
+     *          C[k] = sum_j=0&amp;circ;n-1 a[j]*cos(pi*j*(k+1/2)/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt; DCT
+     *          C[k] = sum_j=0&amp;circ;n-1 a[j]*cos(pi*(j+1/2)*k/n), 0&lt;=k&lt;n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          ddct(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          ddct(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          ddct(n, -1, a, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          ddct(n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n    data length (int)
+     *             <pre>
+     *              n &gt;= 2, n = power of 2
+     *             </pre>
+     * @param isgn
+     * @param a    [0...n-1] input/output data (REAL *)
+     *             <pre>
+     *              output data
+     *                  a[k] = C[k], 0&lt;=k&lt;n
+     *             </pre>
+     * @param ip   [0...*] work area for bit reversal (int *)
+     *             <pre>
+     *              length of ip &gt;= 2+sqrt(n/2)
+     *              strictly,
+     *              length of ip &gt;=
+     *                  2+(1&lt;&lt;(int)(log(n/2+0.5)/log(2))/2).
+     *              ip[0],ip[1] are pointers of the cos/sin table.
+     *             </pre>
+     * @param w    [0...n*5/4-1] cos/sin table (REAL *)
+     *             <pre>
+     *              w[],ip[] are initialized if ip[0] == 0.
+     *             </pre>
+     */
+    public void ddct(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int j, nw, nc;
+        double xr;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > nc) {
+            nc = n;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn < 0) {
+            xr = a[n - 1];
+            for (j = n - 2; j >= 2; j -= 2) {
+                a[j + 1] = a[j] - a[j - 1];
+                a[j] += a[j - 1];
+            }
+            a[1] = a[0] - xr;
+            a[0] += xr;
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+        dctsub(n, a, nc, w, nw);
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xr = a[0] - a[1];
+            a[0] += a[1];
+            for (j = 2; j < n; j += 2) {
+                a[j - 1] = a[j] - a[j + 1];
+                a[j] += a[j + 1];
+            }
+            a[n - 1] = xr;
+        }
+    }
+
+    /**
+     * Discrete Sine Transform.
+     * <pre>
+     *  [definition]
+     *      &lt;case1&gt; IDST (excluding scale)
+     *          S[k] = sum_j=1&circ;n A[j]*sin(pi*j*(k+1/2)/n), 0&lt;=k&lt;n
+     *      &lt;case2&gt; DST
+     *          S[k] = sum_j=0&circ;n-1 a[j]*sin(pi*(j+1/2)*k/n), 0&lt;k&lt;=n
+     *  [usage]
+     *      &lt;case1&gt;
+     *          ip[0] = 0; // first time only
+     *          ddst(n, 1, a, ip, w);
+     *      &lt;case2&gt;
+     *          ip[0] = 0; // first time only
+     *          ddst(n, -1, a, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          ddst(n, -1, a, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          ddst(n, 1, a, ip, w);
+     *          for (j = 0; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n    data length (int)
+     *             n &gt;= 2, n = power of 2
+     * @param isgn
+     * @param a    [0...n-1] input/output data (REAL *)
+     *             &lt;case1&gt;
+     *             input data
+     *             a[j] = A[j], 0&lt;j&lt;n
+     *             a[0] = A[n]
+     *             output data
+     *             a[k] = S[k], 0&lt;=k&lt;n
+     *             &lt;case2&gt;
+     *             output data
+     *             a[k] = S[k], 0&lt;k&lt;n
+     *             a[0] = S[n]
+     * @param ip   [0...*] work area for bit reversal (int *)
+     *             length of ip &gt;= 2+sqrt(n/2)
+     *             strictly,
+     *             length of ip &gt;=
+     *             2+(1&lt;&lt;(int)(log(n/2+0.5)/log(2))/2).
+     *             ip[0],ip[1] are pointers of the cos/sin table.
+     * @param w    [0...n*5/4-1] cos/sin table (REAL *)
+     *             w[],ip[] are initialized if ip[0] == 0.
+     */
+    public void ddst(int n, int isgn, double[] a, int[] ip, double[] w) {
+        int j, nw, nc;
+        double xr;
+
+        nw = ip[0];
+        if (n > (nw << 2)) {
+            nw = n >> 2;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > nc) {
+            nc = n;
+            makect(nc, ip, w, nw);
+        }
+        if (isgn < 0) {
+            xr = a[n - 1];
+            for (j = n - 2; j >= 2; j -= 2) {
+                a[j + 1] = -a[j] - a[j - 1];
+                a[j] -= a[j - 1];
+            }
+            a[1] = a[0] + xr;
+            a[0] -= xr;
+            if (n > 4) {
+                rftbsub(n, a, nc, w, nw);
+                cftbsub(n, a, ip, 2, nw, w);
+            } else if (n == 4) {
+                cftbsub(n, a, ip, 2, nw, w);
+            }
+        }
+        dstsub(n, a, nc, w, nw);
+        if (isgn >= 0) {
+            if (n > 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+                rftfsub(n, a, nc, w, nw);
+            } else if (n == 4) {
+                cftfsub(n, a, ip, 2, nw, w);
+            }
+            xr = a[0] - a[1];
+            a[0] += a[1];
+            for (j = 2; j < n; j += 2) {
+                a[j - 1] = -a[j] - a[j + 1];
+                a[j] -= a[j + 1];
+            }
+            a[n - 1] = -xr;
+        }
+    }
+
+    /**
+     * Cosine Transform of RDFT (Real Symmetric DFT).
+     * <pre>
+     *  [definition]
+     *      C[k] = sum_j=0&circ;n a[j]*cos(pi*j*k/n), 0&lt;=k&lt;=n
+     *  [usage]
+     *      ip[0] = 0; // first time only
+     *      dfct(n, a, t, ip, w);
+     *  [parameters]
+     *  [remark]
+     *      Inverse of
+     *          a[0] *= 0.5;
+     *          a[n] *= 0.5;
+     *          dfct(n, a, t, ip, w);
+     *      is
+     *          a[0] *= 0.5;
+     *          a[n] *= 0.5;
+     *          dfct(n, a, t, ip, w);
+     *          for (j = 0; j &lt;= n; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n  data length - 1 (int)
+     *           <pre>
+     *            n &gt;= 2, n = power of 2
+     *           </pre>
+     * @param a  [0...n] input/output data (REAL *)
+     *           <pre>
+     *            output data
+     *                a[k] = C[k], 0&lt;=k&lt;=n
+     *           </pre>
+     * @param t  [0...n/2] work area (REAL *)
+     * @param ip [0...*] work area for bit reversal (int *)
+     *           <pre>
+     *            length of ip &gt;= 2+sqrt(n/4)
+     *            strictly,
+     *            length of ip &gt;=
+     *                2+(1&lt;&lt;(int)(log(n/4+0.5)/log(2))/2).
+     *            ip[0],ip[1] are pointers of the cos/sin table.
+     *           </pre>
+     * @param w  [0...n*5/8-1] cos/sin table (REAL *)
+     *           <pre>
+     *            w[],ip[] are initialized if ip[0] == 0.
+     *           </pre>
+     */
+    public void dfct(int n, double[] a, double[] t, int[] ip, double[] w) {
+        int j, k, l, m, mh, nw, nc;
+        double xr, xi, yr, yi;
+
+        nw = ip[0];
+        if (n > (nw << 3)) {
+            nw = n >> 3;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 1)) {
+            nc = n >> 1;
+            makect(nc, ip, w, nw);
+        }
+        m = n >> 1;
+        yi = a[m];
+        xi = a[0] + a[n];
+        a[0] -= a[n];
+        t[0] = xi - yi;
+        t[m] = xi + yi;
+        if (n > 2) {
+            mh = m >> 1;
+            for (j = 1; j < mh; j++) {
+                k = m - j;
+                xr = a[j] - a[n - j];
+                xi = a[j] + a[n - j];
+                yr = a[k] - a[n - k];
+                yi = a[k] + a[n - k];
+                a[j] = xr;
+                a[k] = yr;
+                t[j] = xi - yi;
+                t[k] = xi + yi;
+            }
+            t[mh] = a[mh] + a[n - mh];
+            a[mh] -= a[n - mh];
+            dctsub(m, a, nc, w, nw);
+            if (m > 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+                rftfsub(m, a, nc, w, nw);
+            } else if (m == 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+            }
+            a[n - 1] = a[0] - a[1];
+            a[1] = a[0] + a[1];
+            for (j = m - 2; j >= 2; j -= 2) {
+                a[2 * j + 1] = a[j] + a[j + 1];
+                a[2 * j - 1] = a[j] - a[j + 1];
+            }
+            l = 2;
+            m = mh;
+            while (m >= 2) {
+                dctsub(m, t, nc, w, nw);
+                if (m > 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                    rftfsub(m, t, nc, w, nw);
+                } else if (m == 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                }
+                a[n - l] = t[0] - t[1];
+                a[l] = t[0] + t[1];
+                k = 0;
+                for (j = 2; j < m; j += 2) {
+                    k += l << 2;
+                    a[k - l] = t[j] - t[j + 1];
+                    a[k + l] = t[j] + t[j + 1];
+                }
+                l <<= 1;
+                mh = m >> 1;
+                for (j = 0; j < mh; j++) {
+                    k = m - j;
+                    t[j] = t[m + k] - t[m + j];
+                    t[k] = t[m + k] + t[m + j];
+                }
+                t[mh] = t[m + mh];
+                m = mh;
+            }
+            a[l] = t[0];
+            a[n] = t[2] - t[1];
+            a[0] = t[2] + t[1];
+        } else {
+            a[1] = a[0];
+            a[2] = t[0];
+            a[0] = t[1];
+        }
+    }
+
+    /**
+     * Sine Transform of RDFT (Real Anti-symmetric DFT).
+     * <pre>
+     *  [definition]
+     *      S[k] = sum_j=1&amp;circ;n-1 a[j]*sin(pi*j*k/n), 0&lt;k&lt;n
+     *  [usage]
+     *      ip[0] = 0; // first time only
+     *      dfst(n, a, t, ip, w);
+     *  [remark]
+     *      Inverse of
+     *          dfst(n, a, t, ip, w);
+     *      is
+     *          dfst(n, a, t, ip, w);
+     *          for (j = 1; j &lt;= n - 1; j++) {
+     *              a[j] *= 2.0 / n;
+     *          }
+     *      .
+     * </pre>
+     *
+     * @param n  data length + 1 (int)
+     *           <pre>
+     *            n &gt;= 2, n = power of 2
+     *           </pre>
+     * @param a  [0...n-1] input/output data (REAL *)
+     *           <pre>
+     *            output data
+     *                a[k] = S[k], 0&lt;k&lt;n
+     *                (a[0] is used for work area)
+     *           </pre>
+     * @param t  [0...n/2-1] work area (REAL *)
+     * @param ip [0...*] work area for bit reversal (int *)
+     *           <pre>
+     *            length of ip &gt;= 2+sqrt(n/4)
+     *            strictly,
+     *            length of ip &gt;=
+     *                2+(1&lt;&lt;(int)(log(n/4+0.5)/log(2))/2).
+     *            ip[0],ip[1] are pointers of the cos/sin table.
+     *           </pre>
+     * @param w  [0...n*5/8-1] cos/sin table (REAL *)
+     *           <pre>
+     *            w[],ip[] are initialized if ip[0] == 0.
+     *           </pre>
+     */
+    public void dfst(int n, double[] a, double[] t, int[] ip, double[] w) {
+        int j, k, l, m, mh, nw, nc;
+        double xr, xi, yr, yi;
+
+        nw = ip[0];
+        if (n > (nw << 3)) {
+            nw = n >> 3;
+            makewt(nw, ip, w);
+        }
+        nc = ip[1];
+        if (n > (nc << 1)) {
+            nc = n >> 1;
+            makect(nc, ip, w, nw);
+        }
+        if (n > 2) {
+            m = n >> 1;
+            mh = m >> 1;
+            for (j = 1; j < mh; j++) {
+                k = m - j;
+                xr = a[j] + a[n - j];
+                xi = a[j] - a[n - j];
+                yr = a[k] + a[n - k];
+                yi = a[k] - a[n - k];
+                a[j] = xr;
+                a[k] = yr;
+                t[j] = xi + yi;
+                t[k] = xi - yi;
+            }
+            t[0] = a[mh] - a[n - mh];
+            a[mh] += a[n - mh];
+            a[0] = a[m];
+            dstsub(m, a, nc, w, nw);
+            if (m > 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+                rftfsub(m, a, nc, w, nw);
+            } else if (m == 4) {
+                cftfsub(m, a, ip, 2, nw, w);
+            }
+            a[n - 1] = a[1] - a[0];
+            a[1] = a[0] + a[1];
+            for (j = m - 2; j >= 2; j -= 2) {
+                a[2 * j + 1] = a[j] - a[j + 1];
+                a[2 * j - 1] = -a[j] - a[j + 1];
+            }
+            l = 2;
+            m = mh;
+            while (m >= 2) {
+                dstsub(m, t, nc, w, nw);
+                if (m > 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                    rftfsub(m, t, nc, w, nw);
+                } else if (m == 4) {
+                    cftfsub(m, t, ip, 2, nw, w);
+                }
+                a[n - l] = t[1] - t[0];
+                a[l] = t[0] + t[1];
+                k = 0;
+                for (j = 2; j < m; j += 2) {
+                    k += l << 2;
+                    a[k - l] = -t[j] - t[j + 1];
+                    a[k + l] = t[j] - t[j + 1];
+                }
+                l <<= 1;
+                mh = m >> 1;
+                for (j = 1; j < mh; j++) {
+                    k = m - j;
+                    t[j] = t[m + k] + t[m + j];
+                    t[k] = t[m + k] - t[m + j];
+                }
+                t[0] = t[m + mh];
+                m = mh;
+            }
+            a[l] = t[0];
+        }
+        a[0] = 0;
+    }
+
+    // -------- initializing routines --------
+
+    /** */
+    private void makewt(int nw, int[] ip, double[] w) {
+        int j, nwh, nw0, nw1;
+        double delta, wn4r, wk1r, wk1i, wk3r, wk3i;
+
+        ip[0] = nw;
+        ip[1] = 1;
+        if (nw > 2) {
+            nwh = nw >> 1;
+//          delta = Math.atan(1.0) / nwh;
+            delta = Math.PI / 4 / nwh;
+            wn4r = Math.cos(delta * nwh);
+            w[0] = 1;
+            w[1] = wn4r;
+            if (nwh >= 4) {
+                w[2] = 0.5 / Math.cos(delta * 2);
+                w[3] = 0.5 / Math.cos(delta * 6);
+            }
+            for (j = 4; j < nwh; j += 4) {
+                w[j] = Math.cos(delta * j);
+                w[j + 1] = Math.sin(delta * j);
+                w[j + 2] = Math.cos(3 * delta * j);
+                w[j + 3] = Math.sin(3 * delta * j);
+            }
+            nw0 = 0;
+            while (nwh > 2) {
+                nw1 = nw0 + nwh;
+                nwh >>= 1;
+                w[nw1] = 1;
+                w[nw1 + 1] = wn4r;
+                if (nwh >= 4) {
+                    wk1r = w[nw0 + 4];
+                    wk3r = w[nw0 + 6];
+                    w[nw1 + 2] = 0.5 / wk1r;
+                    w[nw1 + 3] = 0.5 / wk3r;
+                }
+                for (j = 4; j < nwh; j += 4) {
+                    wk1r = w[nw0 + 2 * j];
+                    wk1i = w[nw0 + 2 * j + 1];
+                    wk3r = w[nw0 + 2 * j + 2];
+                    wk3i = w[nw0 + 2 * j + 3];
+                    w[nw1 + j] = wk1r;
+                    w[nw1 + j + 1] = wk1i;
+                    w[nw1 + j + 2] = wk3r;
+                    w[nw1 + j + 3] = wk3i;
+                }
+                nw0 = nw1;
+            }
+        }
+    }
+
+    /** */
+    private void makect(int nc, int[] ip, double[] c, int cP) {
+        int j, nch;
+        double delta;
+
+        ip[1] = nc;
+        if (nc > 1) {
+            nch = nc >> 1;
+//          delta = Math.atan(1.0) / nch;
+            delta = Math.PI / 4 / nch;
+            c[cP + 0] = Math.cos(delta * nch);
+            c[cP + nch] = 0.5 * c[cP + 0];
+            for (j = 1; j < nch; j++) {
+                c[cP + j] = 0.5 * Math.cos(delta * j);
+                c[cP + nc - j] = 0.5 * Math.sin(delta * j);
+            }
+        }
+    }
+
+    // -------- child routines --------
+
+    /**
+     * 2nd
+     *
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #cdft(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void cftfsub(int n, double[] a, int[] ip, int ipP, int nw, double[] w) {
+        int m;
+
+        if (n > 32) {
+            m = n >> 2;
+            cftf1st(n, a, w, nw - m);
+            if (n > CDFT_RECURSIVE_N) {
+                cftrec1(m, a, 0, nw, w);
+                cftrec2(m, a, m, nw, w);
+                cftrec1(m, a, 2 * m, nw, w);
+                cftrec1(m, a, 3 * m, nw, w);
+            } else if (m > 32) {
+                cftexp1(n, a, 0, nw, w);
+            } else {
+                cftfx41(n, a, 0, nw, w);
+            }
+            bitrv2(n, ip, ipP, a);
+        } else if (n > 8) {
+            if (n == 32) {
+                cftf161(a, 0, w, nw - 8);
+                bitrv216(a);
+            } else {
+                cftf081(a, 0, w, 0);
+                bitrv208(a);
+            }
+        } else if (n == 8) {
+            cftf040(a);
+        } else if (n == 4) {
+            cftx020(a);
+        }
+    }
+
+    /**
+     * 2nd
+     *
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #cdft(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     */
+    private void cftbsub(int n, double[] a, int[] ip, int ipP, int nw, double[] w) {
+        int m;
+
+        if (n > 32) {
+            m = n >> 2;
+            cftb1st(n, a, w, nw - m);
+            if (n > CDFT_RECURSIVE_N) {
+                cftrec1(m, a, 0, nw, w);
+                cftrec2(m, a, m, nw, w);
+                cftrec1(m, a, 2 * m, nw, w);
+                cftrec1(m, a, 3 * m, nw, w);
+            } else if (m > 32) {
+                cftexp1(n, a, 0, nw, w);
+            } else {
+                cftfx41(n, a, 0, nw, w);
+            }
+            bitrv2conj(n, ip, ipP, a);
+        } else if (n > 8) {
+            if (n == 32) {
+                cftf161(a, 0, w, nw - 8);
+                bitrv216neg(a);
+            } else {
+                cftf081(a, 0, w, 0);
+                bitrv208neg(a);
+            }
+        } else if (n == 8) {
+            cftb040(a);
+        } else if (n == 4) {
+            cftx020(a);
+        }
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private final void bitrv2(int n, int[] ip, int ipP, double[] a) {
+        int j, j1, k, k1, l, m, m2;
+        double xr, xi, yr, yi;
+
+        ip[ipP + 0] = 0;
+        l = n;
+        m = 1;
+        while ((m << 3) < l) {
+            l >>= 1;
+            for (j = 0; j < m; j++) {
+                ip[ipP + m + j] = ip[ipP + j] + l;
+            }
+            m <<= 1;
+        }
+        m2 = 2 * m;
+        if ((m << 3) == l) {
+            for (k = 0; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 -= m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                j1 = 2 * k + m2 + ip[ipP + k];
+                k1 = j1 + m2;
+                xr = a[j1];
+                xi = a[j1 + 1];
+                yr = a[k1];
+                yi = a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+            }
+        } else {
+            for (k = 1; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += m2;
+                    xr = a[j1];
+                    xi = a[j1 + 1];
+                    yr = a[k1];
+                    yi = a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+            }
+        }
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private final void bitrv2conj(int n, int[] ip, int ipP, double[] a) {
+        int j, j1, k, k1, l, m, m2;
+        double xr, xi, yr, yi;
+
+        ip[ipP + 0] = 0;
+        l = n;
+        m = 1;
+        while ((m << 3) < l) {
+            l >>= 1;
+            for (j = 0; j < m; j++) {
+                ip[ipP + m + j] = ip[ipP + j] + l;
+            }
+            m <<= 1;
+        }
+        m2 = 2 * m;
+        if ((m << 3) == l) {
+            for (k = 0; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 -= m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += 2 * m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                k1 = 2 * k + ip[ipP + k];
+                a[k1 + 1] = -a[k1 + 1];
+                j1 = k1 + m2;
+                k1 = j1 + m2;
+                xr = a[j1];
+                xi = -a[j1 + 1];
+                yr = a[k1];
+                yi = -a[k1 + 1];
+                a[j1] = yr;
+                a[j1 + 1] = yi;
+                a[k1] = xr;
+                a[k1 + 1] = xi;
+                k1 += m2;
+                a[k1 + 1] = -a[k1 + 1];
+            }
+        } else {
+            a[1] = -a[1];
+            a[m2 + 1] = -a[m2 + 1];
+            for (k = 1; k < m; k++) {
+                for (j = 0; j < k; j++) {
+                    j1 = 2 * j + ip[ipP + k];
+                    k1 = 2 * k + ip[ipP + j];
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                    j1 += m2;
+                    k1 += m2;
+                    xr = a[j1];
+                    xi = -a[j1 + 1];
+                    yr = a[k1];
+                    yi = -a[k1 + 1];
+                    a[j1] = yr;
+                    a[j1 + 1] = yi;
+                    a[k1] = xr;
+                    a[k1 + 1] = xi;
+                }
+                k1 = 2 * k + ip[ipP + k];
+                a[k1 + 1] = -a[k1 + 1];
+                a[k1 + m2 + 1] = -a[k1 + m2 + 1];
+            }
+        }
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv216(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x7r = a[14];
+        x7i = a[15];
+        x8r = a[16];
+        x8i = a[17];
+        x10r = a[20];
+        x10i = a[21];
+        x11r = a[22];
+        x11i = a[23];
+        x12r = a[24];
+        x12i = a[25];
+        x13r = a[26];
+        x13i = a[27];
+        x14r = a[28];
+        x14i = a[29];
+        a[2] = x8r;
+        a[3] = x8i;
+        a[4] = x4r;
+        a[5] = x4i;
+        a[6] = x12r;
+        a[7] = x12i;
+        a[8] = x2r;
+        a[9] = x2i;
+        a[10] = x10r;
+        a[11] = x10i;
+        a[14] = x14r;
+        a[15] = x14i;
+        a[16] = x1r;
+        a[17] = x1i;
+        a[20] = x5r;
+        a[21] = x5i;
+        a[22] = x13r;
+        a[23] = x13i;
+        a[24] = x3r;
+        a[25] = x3i;
+        a[26] = x11r;
+        a[27] = x11i;
+        a[28] = x7r;
+        a[29] = x7i;
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv216neg(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i, x15r, x15i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x6r = a[12];
+        x6i = a[13];
+        x7r = a[14];
+        x7i = a[15];
+        x8r = a[16];
+        x8i = a[17];
+        x9r = a[18];
+        x9i = a[19];
+        x10r = a[20];
+        x10i = a[21];
+        x11r = a[22];
+        x11i = a[23];
+        x12r = a[24];
+        x12i = a[25];
+        x13r = a[26];
+        x13i = a[27];
+        x14r = a[28];
+        x14i = a[29];
+        x15r = a[30];
+        x15i = a[31];
+        a[2] = x15r;
+        a[3] = x15i;
+        a[4] = x7r;
+        a[5] = x7i;
+        a[6] = x11r;
+        a[7] = x11i;
+        a[8] = x3r;
+        a[9] = x3i;
+        a[10] = x13r;
+        a[11] = x13i;
+        a[12] = x5r;
+        a[13] = x5i;
+        a[14] = x9r;
+        a[15] = x9i;
+        a[16] = x1r;
+        a[17] = x1i;
+        a[18] = x14r;
+        a[19] = x14i;
+        a[20] = x6r;
+        a[21] = x6i;
+        a[22] = x10r;
+        a[23] = x10i;
+        a[24] = x2r;
+        a[25] = x2i;
+        a[26] = x12r;
+        a[27] = x12i;
+        a[28] = x4r;
+        a[29] = x4i;
+        a[30] = x8r;
+        a[31] = x8i;
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv208(double[] a) {
+        double x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x6r = a[12];
+        x6i = a[13];
+        a[2] = x4r;
+        a[3] = x4i;
+        a[6] = x6r;
+        a[7] = x6i;
+        a[8] = x1r;
+        a[9] = x1i;
+        a[12] = x3r;
+        a[13] = x3i;
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void bitrv208neg(double[] a) {
+        double x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i;
+
+        x1r = a[2];
+        x1i = a[3];
+        x2r = a[4];
+        x2i = a[5];
+        x3r = a[6];
+        x3i = a[7];
+        x4r = a[8];
+        x4i = a[9];
+        x5r = a[10];
+        x5i = a[11];
+        x6r = a[12];
+        x6i = a[13];
+        x7r = a[14];
+        x7i = a[15];
+        a[2] = x7r;
+        a[3] = x7i;
+        a[4] = x3r;
+        a[5] = x3i;
+        a[6] = x5r;
+        a[7] = x5i;
+        a[8] = x1r;
+        a[9] = x1i;
+        a[10] = x6r;
+        a[11] = x6i;
+        a[12] = x2r;
+        a[13] = x2i;
+        a[14] = x4r;
+        a[15] = x4i;
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftf1st(int n, double[] a, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[0] + a[j2];
+        x0i = a[1] + a[j2 + 1];
+        x1r = a[0] - a[j2];
+        x1i = a[1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        a[j2] = x1r - x3i;
+        a[j2 + 1] = x1i + x3r;
+        a[j3] = x1r + x3i;
+        a[j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        csc1 = w[wP + 2];
+        csc3 = w[wP + 3];
+        wd1r = 1;
+        wd1i = 0;
+        wd3r = 1;
+        wd3i = 0;
+        k = 0;
+        for (j = 2; j < mh - 2; j += 4) {
+            k += 4;
+            wk1r = csc1 * (wd1r + w[wP + k]);
+            wk1i = csc1 * (wd1i + w[wP + k + 1]);
+            wk3r = csc3 * (wd3r + w[wP + k + 2]);
+            wk3i = csc3 * (wd3i - w[wP + k + 3]);
+            wd1r = w[wP + k];
+            wd1i = w[wP + k + 1];
+            wd3r = w[wP + k + 2];
+            wd3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j] + a[j2];
+            x0i = a[j + 1] + a[j2 + 1];
+            x1r = a[j] - a[j2];
+            x1i = a[j + 1] - a[j2 + 1];
+            y0r = a[j + 2] + a[j2 + 2];
+            y0i = a[j + 3] + a[j2 + 3];
+            y1r = a[j + 2] - a[j2 + 2];
+            y1i = a[j + 3] - a[j2 + 3];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 + 2] + a[j3 + 2];
+            y2i = a[j1 + 3] + a[j3 + 3];
+            y3r = a[j1 + 2] - a[j3 + 2];
+            y3i = a[j1 + 3] - a[j3 + 3];
+            a[j] = x0r + x2r;
+            a[j + 1] = x0i + x2i;
+            a[j + 2] = y0r + y2r;
+            a[j + 3] = y0i + y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i - x2i;
+            a[j1 + 2] = y0r - y2r;
+            a[j1 + 3] = y0i - y2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1r * x0r - wk1i * x0i;
+            a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i + y3r;
+            a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+            a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3r * x0r + wk3i * x0i;
+            a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i - y3r;
+            a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+            a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j0] + a[j2];
+            x0i = a[j0 + 1] + a[j2 + 1];
+            x1r = a[j0] - a[j2];
+            x1i = a[j0 + 1] - a[j2 + 1];
+            y0r = a[j0 - 2] + a[j2 - 2];
+            y0i = a[j0 - 1] + a[j2 - 1];
+            y1r = a[j0 - 2] - a[j2 - 2];
+            y1i = a[j0 - 1] - a[j2 - 1];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 - 2] + a[j3 - 2];
+            y2i = a[j1 - 1] + a[j3 - 1];
+            y3r = a[j1 - 2] - a[j3 - 2];
+            y3i = a[j1 - 1] - a[j3 - 1];
+            a[j0] = x0r + x2r;
+            a[j0 + 1] = x0i + x2i;
+            a[j0 - 2] = y0r + y2r;
+            a[j0 - 1] = y0i + y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i - x2i;
+            a[j1 - 2] = y0r - y2r;
+            a[j1 - 1] = y0i - y2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1i * x0r - wk1r * x0i;
+            a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i + y3r;
+            a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+            a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3i * x0r + wk3r * x0i;
+            a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i - y3r;
+            a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+            a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+        }
+        wk1r = csc1 * (wd1r + wn4r);
+        wk1i = csc1 * (wd1i + wn4r);
+        wk3r = csc3 * (wd3r - wn4r);
+        wk3i = csc3 * (wd3i - wn4r);
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0 - 2] + a[j2 - 2];
+        x0i = a[j0 - 1] + a[j2 - 1];
+        x1r = a[j0 - 2] - a[j2 - 2];
+        x1i = a[j0 - 1] - a[j2 - 1];
+        x2r = a[j1 - 2] + a[j3 - 2];
+        x2i = a[j1 - 1] + a[j3 - 1];
+        x3r = a[j1 - 2] - a[j3 - 2];
+        x3i = a[j1 - 1] - a[j3 - 1];
+        a[j0 - 2] = x0r + x2r;
+        a[j0 - 1] = x0i + x2i;
+        a[j1 - 2] = x0r - x2r;
+        a[j1 - 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+        a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+        a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+        x0r = a[j0] + a[j2];
+        x0i = a[j0 + 1] + a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = a[j0 + 1] - a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i + x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2] = wn4r * (x0r - x0i);
+        a[j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3] = -wn4r * (x0r + x0i);
+        a[j3 + 1] = -wn4r * (x0i - x0r);
+        x0r = a[j0 + 2] + a[j2 + 2];
+        x0i = a[j0 + 3] + a[j2 + 3];
+        x1r = a[j0 + 2] - a[j2 + 2];
+        x1i = a[j0 + 3] - a[j2 + 3];
+        x2r = a[j1 + 2] + a[j3 + 2];
+        x2i = a[j1 + 3] + a[j3 + 3];
+        x3r = a[j1 + 2] - a[j3 + 2];
+        x3i = a[j1 + 3] - a[j3 + 3];
+        a[j0 + 2] = x0r + x2r;
+        a[j0 + 3] = x0i + x2i;
+        a[j1 + 2] = x0r - x2r;
+        a[j1 + 3] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+    }
+
+    /**
+     * 3rd
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private final void cftb1st(int n, double[] a, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[0] + a[j2];
+        x0i = -a[1] - a[j2 + 1];
+        x1r = a[0] - a[j2];
+        x1i = -a[1] + a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[0] = x0r + x2r;
+        a[1] = x0i - x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        a[j2] = x1r + x3i;
+        a[j2 + 1] = x1i + x3r;
+        a[j3] = x1r - x3i;
+        a[j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        csc1 = w[wP + 2];
+        csc3 = w[wP + 3];
+        wd1r = 1;
+        wd1i = 0;
+        wd3r = 1;
+        wd3i = 0;
+        k = 0;
+        for (j = 2; j < mh - 2; j += 4) {
+            k += 4;
+            wk1r = csc1 * (wd1r + w[wP + k]);
+            wk1i = csc1 * (wd1i + w[wP + k + 1]);
+            wk3r = csc3 * (wd3r + w[wP + k + 2]);
+            wk3i = csc3 * (wd3i - w[wP + k + 3]);
+            wd1r = w[wP + k];
+            wd1i = w[wP + k + 1];
+            wd3r = w[wP + k + 2];
+            wd3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j] + a[j2];
+            x0i = -a[j + 1] - a[j2 + 1];
+            x1r = a[j] - a[j2];
+            x1i = -a[j + 1] + a[j2 + 1];
+            y0r = a[j + 2] + a[j2 + 2];
+            y0i = -a[j + 3] - a[j2 + 3];
+            y1r = a[j + 2] - a[j2 + 2];
+            y1i = -a[j + 3] + a[j2 + 3];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 + 2] + a[j3 + 2];
+            y2i = a[j1 + 3] + a[j3 + 3];
+            y3r = a[j1 + 2] - a[j3 + 2];
+            y3i = a[j1 + 3] - a[j3 + 3];
+            a[j] = x0r + x2r;
+            a[j + 1] = x0i - x2i;
+            a[j + 2] = y0r + y2r;
+            a[j + 3] = y0i - y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i + x2i;
+            a[j1 + 2] = y0r - y2r;
+            a[j1 + 3] = y0i + y2i;
+            x0r = x1r + x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1r * x0r - wk1i * x0i;
+            a[j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i + y3r;
+            a[j2 + 2] = wd1r * x0r - wd1i * x0i;
+            a[j2 + 3] = wd1r * x0i + wd1i * x0r;
+            x0r = x1r - x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3r * x0r + wk3i * x0i;
+            a[j3 + 1] = wk3r * x0i - wk3i * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i - y3r;
+            a[j3 + 2] = wd3r * x0r + wd3i * x0i;
+            a[j3 + 3] = wd3r * x0i - wd3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[j0] + a[j2];
+            x0i = -a[j0 + 1] - a[j2 + 1];
+            x1r = a[j0] - a[j2];
+            x1i = -a[j0 + 1] + a[j2 + 1];
+            y0r = a[j0 - 2] + a[j2 - 2];
+            y0i = -a[j0 - 1] - a[j2 - 1];
+            y1r = a[j0 - 2] - a[j2 - 2];
+            y1i = -a[j0 - 1] + a[j2 - 1];
+            x2r = a[j1] + a[j3];
+            x2i = a[j1 + 1] + a[j3 + 1];
+            x3r = a[j1] - a[j3];
+            x3i = a[j1 + 1] - a[j3 + 1];
+            y2r = a[j1 - 2] + a[j3 - 2];
+            y2i = a[j1 - 1] + a[j3 - 1];
+            y3r = a[j1 - 2] - a[j3 - 2];
+            y3i = a[j1 - 1] - a[j3 - 1];
+            a[j0] = x0r + x2r;
+            a[j0 + 1] = x0i - x2i;
+            a[j0 - 2] = y0r + y2r;
+            a[j0 - 1] = y0i - y2i;
+            a[j1] = x0r - x2r;
+            a[j1 + 1] = x0i + x2i;
+            a[j1 - 2] = y0r - y2r;
+            a[j1 - 1] = y0i + y2i;
+            x0r = x1r + x3i;
+            x0i = x1i + x3r;
+            a[j2] = wk1i * x0r - wk1r * x0i;
+            a[j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = y1r + y3i;
+            x0i = y1i + y3r;
+            a[j2 - 2] = wd1i * x0r - wd1r * x0i;
+            a[j2 - 1] = wd1i * x0i + wd1r * x0r;
+            x0r = x1r - x3i;
+            x0i = x1i - x3r;
+            a[j3] = wk3i * x0r + wk3r * x0i;
+            a[j3 + 1] = wk3i * x0i - wk3r * x0r;
+            x0r = y1r - y3i;
+            x0i = y1i - y3r;
+            a[j3 - 2] = wd3i * x0r + wd3r * x0i;
+            a[j3 - 1] = wd3i * x0i - wd3r * x0r;
+        }
+        wk1r = csc1 * (wd1r + wn4r);
+        wk1i = csc1 * (wd1i + wn4r);
+        wk3r = csc3 * (wd3r - wn4r);
+        wk3i = csc3 * (wd3i - wn4r);
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[j0 - 2] + a[j2 - 2];
+        x0i = -a[j0 - 1] - a[j2 - 1];
+        x1r = a[j0 - 2] - a[j2 - 2];
+        x1i = -a[j0 - 1] + a[j2 - 1];
+        x2r = a[j1 - 2] + a[j3 - 2];
+        x2i = a[j1 - 1] + a[j3 - 1];
+        x3r = a[j1 - 2] - a[j3 - 2];
+        x3i = a[j1 - 1] - a[j3 - 1];
+        a[j0 - 2] = x0r + x2r;
+        a[j0 - 1] = x0i - x2i;
+        a[j1 - 2] = x0r - x2r;
+        a[j1 - 1] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2 - 2] = wk1r * x0r - wk1i * x0i;
+        a[j2 - 1] = wk1r * x0i + wk1i * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3 - 2] = wk3r * x0r + wk3i * x0i;
+        a[j3 - 1] = wk3r * x0i - wk3i * x0r;
+        x0r = a[j0] + a[j2];
+        x0i = -a[j0 + 1] - a[j2 + 1];
+        x1r = a[j0] - a[j2];
+        x1i = -a[j0 + 1] + a[j2 + 1];
+        x2r = a[j1] + a[j3];
+        x2i = a[j1 + 1] + a[j3 + 1];
+        x3r = a[j1] - a[j3];
+        x3i = a[j1 + 1] - a[j3 + 1];
+        a[j0] = x0r + x2r;
+        a[j0 + 1] = x0i - x2i;
+        a[j1] = x0r - x2r;
+        a[j1 + 1] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2] = wn4r * (x0r - x0i);
+        a[j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3] = -wn4r * (x0r + x0i);
+        a[j3 + 1] = -wn4r * (x0i - x0r);
+        x0r = a[j0 + 2] + a[j2 + 2];
+        x0i = -a[j0 + 3] - a[j2 + 3];
+        x1r = a[j0 + 2] - a[j2 + 2];
+        x1i = -a[j0 + 3] + a[j2 + 3];
+        x2r = a[j1 + 2] + a[j3 + 2];
+        x2i = a[j1 + 3] + a[j3 + 3];
+        x3r = a[j1 + 2] - a[j3 + 2];
+        x3i = a[j1 + 3] - a[j3 + 3];
+        a[j0 + 2] = x0r + x2r;
+        a[j0 + 3] = x0i - x2i;
+        a[j1 + 2] = x0r - x2r;
+        a[j1 + 3] = x0i + x2i;
+        x0r = x1r + x3i;
+        x0i = x1i + x3r;
+        a[j2 + 2] = wk1i * x0r - wk1r * x0i;
+        a[j2 + 3] = wk1i * x0i + wk1r * x0r;
+        x0r = x1r - x3i;
+        x0i = x1i - x3r;
+        a[j3 + 2] = wk3i * x0r + wk3r * x0i;
+        a[j3 + 3] = wk3i * x0i - wk3r * x0r;
+    }
+
+    /** */
+    private void cftrec1(int n, double[] a, int aP, int nw, double[] w) {
+        int m;
+
+        m = n >> 2;
+        cftmdl1(n, a, aP, w, nw - 2 * m);
+        if (n > CDFT_RECURSIVE_N) {
+            cftrec1(m, a, aP, nw, w);
+            cftrec2(m, a, aP + m, nw, w);
+            cftrec1(m, a, aP + 2 * m, nw, w);
+            cftrec1(m, a, aP + 3 * m, nw, w);
+        } else {
+            cftexp1(n, a, aP, nw, w);
+        }
+    }
+
+    /** */
+    private void cftrec2(int n, double[] a, int aP, int nw, double[] w) {
+        int m;
+
+        m = n >> 2;
+        cftmdl2(n, a, aP, w, nw - n);
+        if (n > CDFT_RECURSIVE_N) {
+            cftrec1(m, a, aP, nw, w);
+            cftrec2(m, a, aP + m, nw, w);
+            cftrec1(m, a, aP + 2 * m, nw, w);
+            cftrec2(m, a, aP + 3 * m, nw, w);
+        } else {
+            cftexp2(n, a, aP, nw, w);
+        }
+    }
+
+    /** */
+    private void cftexp1(int n, double[] a, int aP, int nw, double[] w) {
+        int j, k, l;
+
+        l = n >> 2;
+        while (l > 128) {
+            for (k = l; k < n; k <<= 2) {
+                for (j = k - l; j < n; j += 4 * k) {
+                    cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                    cftmdl2(l, a, aP + k + j, w, nw - l);
+                    cftmdl1(l, a, aP + 2 * k + j, w, nw - (l >> 1));
+                }
+            }
+            cftmdl1(l, a, aP + n - l, w, nw - (l >> 1));
+            l >>= 2;
+        }
+        for (k = l; k < n; k <<= 2) {
+            for (j = k - l; j < n; j += 4 * k) {
+                cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + j, nw, w);
+                cftmdl2(l, a, aP + k + j, w, nw - l);
+                cftfx42(l, a, aP + k + j, nw, w);
+                cftmdl1(l, a, aP + 2 * k + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + 2 * k + j, nw, w);
+            }
+        }
+        cftmdl1(l, a, aP + n - l, w, nw - (l >> 1));
+        cftfx41(l, a, aP + n - l, nw, w);
+    }
+
+    /** */
+    private void cftexp2(int n, double[] a, int aP, int nw, double[] w) {
+        int j, k, l, m;
+
+        m = n >> 1;
+        l = n >> 2;
+        while (l > 128) {
+            for (k = l; k < m; k <<= 2) {
+                for (j = k - l; j < m; j += 2 * k) {
+                    cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                    cftmdl1(l, a, aP + m + j, w, nw - (l >> 1));
+                }
+                for (j = 2 * k - l; j < m; j += 4 * k) {
+                    cftmdl2(l, a, aP + j, w, nw - l);
+                    cftmdl2(l, a, aP + m + j, w, nw - l);
+                }
+            }
+            l >>= 2;
+        }
+        for (k = l; k < m; k <<= 2) {
+            for (j = k - l; j < m; j += 2 * k) {
+                cftmdl1(l, a, aP + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + j, nw, w);
+                cftmdl1(l, a, aP + m + j, w, nw - (l >> 1));
+                cftfx41(l, a, aP + m + j, nw, w);
+            }
+            for (j = 2 * k - l; j < m; j += 4 * k) {
+                cftmdl2(l, a, aP + j, w, nw - l);
+                cftfx42(l, a, aP + j, nw, w);
+                cftmdl2(l, a, aP + m + j, w, nw - l);
+                cftfx42(l, a, aP + m + j, nw, w);
+            }
+        }
+    }
+
+    /** */
+    private final void cftmdl1(int n, double[] a, int aP, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, m, mh;
+        double wn4r, wk1r, wk1i, wk3r, wk3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + 0] + a[aP + j2];
+        x0i = a[aP + 1] + a[aP + j2 + 1];
+        x1r = a[aP + 0] - a[aP + j2];
+        x1i = a[aP + 1] - a[aP + j2 + 1];
+        x2r = a[aP + j1] + a[aP + j3];
+        x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+        x3r = a[aP + j1] - a[aP + j3];
+        x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+        a[aP + 0] = x0r + x2r;
+        a[aP + 1] = x0i + x2i;
+        a[aP + j1] = x0r - x2r;
+        a[aP + j1 + 1] = x0i - x2i;
+        a[aP + j2] = x1r - x3i;
+        a[aP + j2 + 1] = x1i + x3r;
+        a[aP + j3] = x1r + x3i;
+        a[aP + j3 + 1] = x1i - x3r;
+        wn4r = w[wP + 1];
+        k = 0;
+        for (j = 2; j < mh; j += 2) {
+            k += 4;
+            wk1r = w[wP + k];
+            wk1i = w[wP + k + 1];
+            wk3r = w[wP + k + 2];
+            wk3i = -w[wP + k + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j] + a[aP + j2];
+            x0i = a[aP + j + 1] + a[aP + j2 + 1];
+            x1r = a[aP + j] - a[aP + j2];
+            x1i = a[aP + j + 1] - a[aP + j2 + 1];
+            x2r = a[aP + j1] + a[aP + j3];
+            x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+            x3r = a[aP + j1] - a[aP + j3];
+            x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+            a[aP + j] = x0r + x2r;
+            a[aP + j + 1] = x0i + x2i;
+            a[aP + j1] = x0r - x2r;
+            a[aP + j1 + 1] = x0i - x2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[aP + j2] = wk1r * x0r - wk1i * x0i;
+            a[aP + j2 + 1] = wk1r * x0i + wk1i * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[aP + j3] = wk3r * x0r + wk3i * x0i;
+            a[aP + j3 + 1] = wk3r * x0i - wk3i * x0r;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j0] + a[aP + j2];
+            x0i = a[aP + j0 + 1] + a[aP + j2 + 1];
+            x1r = a[aP + j0] - a[aP + j2];
+            x1i = a[aP + j0 + 1] - a[aP + j2 + 1];
+            x2r = a[aP + j1] + a[aP + j3];
+            x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+            x3r = a[aP + j1] - a[aP + j3];
+            x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+            a[aP + j0] = x0r + x2r;
+            a[aP + j0 + 1] = x0i + x2i;
+            a[aP + j1] = x0r - x2r;
+            a[aP + j1 + 1] = x0i - x2i;
+            x0r = x1r - x3i;
+            x0i = x1i + x3r;
+            a[aP + j2] = wk1i * x0r - wk1r * x0i;
+            a[aP + j2 + 1] = wk1i * x0i + wk1r * x0r;
+            x0r = x1r + x3i;
+            x0i = x1i - x3r;
+            a[aP + j3] = wk3i * x0r + wk3r * x0i;
+            a[aP + j3 + 1] = wk3i * x0i - wk3r * x0r;
+        }
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + j0] + a[aP + j2];
+        x0i = a[aP + j0 + 1] + a[aP + j2 + 1];
+        x1r = a[aP + j0] - a[aP + j2];
+        x1i = a[aP + j0 + 1] - a[aP + j2 + 1];
+        x2r = a[aP + j1] + a[aP + j3];
+        x2i = a[aP + j1 + 1] + a[aP + j3 + 1];
+        x3r = a[aP + j1] - a[aP + j3];
+        x3i = a[aP + j1 + 1] - a[aP + j3 + 1];
+        a[aP + j0] = x0r + x2r;
+        a[aP + j0 + 1] = x0i + x2i;
+        a[aP + j1] = x0r - x2r;
+        a[aP + j1 + 1] = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        a[aP + j2] = wn4r * (x0r - x0i);
+        a[aP + j2 + 1] = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        a[aP + j3] = -wn4r * (x0r + x0i);
+        a[aP + j3 + 1] = -wn4r * (x0i - x0r);
+    }
+
+    /** */
+    private final void cftmdl2(int n, double[] a, int aP, double[] w, int wP) {
+        int j, j0, j1, j2, j3, k, kr, m, mh;
+        double wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;
+
+        mh = n >> 3;
+        m = 2 * mh;
+        wn4r = w[wP + 1];
+        j1 = m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + 0] - a[aP + j2 + 1];
+        x0i = a[aP + 1] + a[aP + j2];
+        x1r = a[aP + 0] + a[aP + j2 + 1];
+        x1i = a[aP + 1] - a[aP + j2];
+        x2r = a[aP + j1] - a[aP + j3 + 1];
+        x2i = a[aP + j1 + 1] + a[aP + j3];
+        x3r = a[aP + j1] + a[aP + j3 + 1];
+        x3i = a[aP + j1 + 1] - a[aP + j3];
+        y0r = wn4r * (x2r - x2i);
+        y0i = wn4r * (x2i + x2r);
+        a[aP + 0] = x0r + y0r;
+        a[aP + 1] = x0i + y0i;
+        a[aP + j1] = x0r - y0r;
+        a[aP + j1 + 1] = x0i - y0i;
+        y0r = wn4r * (x3r - x3i);
+        y0i = wn4r * (x3i + x3r);
+        a[aP + j2] = x1r - y0i;
+        a[aP + j2 + 1] = x1i + y0r;
+        a[aP + j3] = x1r + y0i;
+        a[aP + j3 + 1] = x1i - y0r;
+        k = 0;
+        kr = 2 * m;
+        for (j = 2; j < mh; j += 2) {
+            k += 4;
+            wk1r = w[wP + k];
+            wk1i = w[wP + k + 1];
+            wk3r = w[wP + k + 2];
+            wk3i = -w[wP + k + 3];
+            kr -= 4;
+            wd1i = w[wP + kr];
+            wd1r = w[wP + kr + 1];
+            wd3i = w[wP + kr + 2];
+            wd3r = -w[wP + kr + 3];
+            j1 = j + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j] - a[aP + j2 + 1];
+            x0i = a[aP + j + 1] + a[aP + j2];
+            x1r = a[aP + j] + a[aP + j2 + 1];
+            x1i = a[aP + j + 1] - a[aP + j2];
+            x2r = a[aP + j1] - a[aP + j3 + 1];
+            x2i = a[aP + j1 + 1] + a[aP + j3];
+            x3r = a[aP + j1] + a[aP + j3 + 1];
+            x3i = a[aP + j1 + 1] - a[aP + j3];
+            y0r = wk1r * x0r - wk1i * x0i;
+            y0i = wk1r * x0i + wk1i * x0r;
+            y2r = wd1r * x2r - wd1i * x2i;
+            y2i = wd1r * x2i + wd1i * x2r;
+            a[aP + j] = y0r + y2r;
+            a[aP + j + 1] = y0i + y2i;
+            a[aP + j1] = y0r - y2r;
+            a[aP + j1 + 1] = y0i - y2i;
+            y0r = wk3r * x1r + wk3i * x1i;
+            y0i = wk3r * x1i - wk3i * x1r;
+            y2r = wd3r * x3r + wd3i * x3i;
+            y2i = wd3r * x3i - wd3i * x3r;
+            a[aP + j2] = y0r + y2r;
+            a[aP + j2 + 1] = y0i + y2i;
+            a[aP + j3] = y0r - y2r;
+            a[aP + j3 + 1] = y0i - y2i;
+            j0 = m - j;
+            j1 = j0 + m;
+            j2 = j1 + m;
+            j3 = j2 + m;
+            x0r = a[aP + j0] - a[aP + j2 + 1];
+            x0i = a[aP + j0 + 1] + a[aP + j2];
+            x1r = a[aP + j0] + a[aP + j2 + 1];
+            x1i = a[aP + j0 + 1] - a[aP + j2];
+            x2r = a[aP + j1] - a[aP + j3 + 1];
+            x2i = a[aP + j1 + 1] + a[aP + j3];
+            x3r = a[aP + j1] + a[aP + j3 + 1];
+            x3i = a[aP + j1 + 1] - a[aP + j3];
+            y0r = wd1i * x0r - wd1r * x0i;
+            y0i = wd1i * x0i + wd1r * x0r;
+            y2r = wk1i * x2r - wk1r * x2i;
+            y2i = wk1i * x2i + wk1r * x2r;
+            a[aP + j0] = y0r + y2r;
+            a[aP + j0 + 1] = y0i + y2i;
+            a[aP + j1] = y0r - y2r;
+            a[aP + j1 + 1] = y0i - y2i;
+            y0r = wd3i * x1r + wd3r * x1i;
+            y0i = wd3i * x1i - wd3r * x1r;
+            y2r = wk3i * x3r + wk3r * x3i;
+            y2i = wk3i * x3i - wk3r * x3r;
+            a[aP + j2] = y0r + y2r;
+            a[aP + j2 + 1] = y0i + y2i;
+            a[aP + j3] = y0r - y2r;
+            a[aP + j3 + 1] = y0i - y2i;
+        }
+        wk1r = w[wP + m];
+        wk1i = w[wP + m + 1];
+        j0 = mh;
+        j1 = j0 + m;
+        j2 = j1 + m;
+        j3 = j2 + m;
+        x0r = a[aP + j0] - a[aP + j2 + 1];
+        x0i = a[aP + j0 + 1] + a[aP + j2];
+        x1r = a[aP + j0] + a[aP + j2 + 1];
+        x1i = a[aP + j0 + 1] - a[aP + j2];
+        x2r = a[aP + j1] - a[aP + j3 + 1];
+        x2i = a[aP + j1 + 1] + a[aP + j3];
+        x3r = a[aP + j1] + a[aP + j3 + 1];
+        x3i = a[aP + j1 + 1] - a[aP + j3];
+        y0r = wk1r * x0r - wk1i * x0i;
+        y0i = wk1r * x0i + wk1i * x0r;
+        y2r = wk1i * x2r - wk1r * x2i;
+        y2i = wk1i * x2i + wk1r * x2r;
+        a[aP + j0] = y0r + y2r;
+        a[aP + j0 + 1] = y0i + y2i;
+        a[aP + j1] = y0r - y2r;
+        a[aP + j1 + 1] = y0i - y2i;
+        y0r = wk1i * x1r - wk1r * x1i;
+        y0i = wk1i * x1i + wk1r * x1r;
+        y2r = wk1r * x3r - wk1i * x3i;
+        y2i = wk1r * x3i + wk1i * x3r;
+        a[aP + j2] = y0r - y2r;
+        a[aP + j2 + 1] = y0i - y2i;
+        a[aP + j3] = y0r + y2r;
+        a[aP + j3 + 1] = y0i + y2i;
+    }
+
+    /** */
+    private void cftfx41(int n, double[] a, int aP, int nw, double[] w) {
+        if (n == 128) {
+            cftf161(a, aP, w, nw - 8);
+            cftf162(a, aP + 32, w, nw - 32);
+            cftf161(a, aP + 64, w, nw - 8);
+            cftf161(a, aP + 96, w, nw - 8);
+        } else {
+            cftf081(a, aP, w, nw - 16);
+            cftf082(a, aP + 16, w, nw - 16);
+            cftf081(a, aP + 32, w, nw - 16);
+            cftf081(a, aP + 48, w, nw - 16);
+        }
+    }
+
+    /** */
+    private void cftfx42(int n, double[] a, int aP, int nw, double[] w) {
+        if (n == 128) {
+            cftf161(a, aP, w, nw - 8);
+            cftf162(a, aP + 32, w, nw - 32);
+            cftf161(a, aP + 64, w, nw - 8);
+            cftf162(a, aP + 96, w, nw - 32);
+        } else {
+            cftf081(a, aP, w, nw - 16);
+            cftf082(a, aP + 16, w, nw - 16);
+            cftf081(a, aP + 32, w, nw - 16);
+            cftf082(a, aP + 48, w, nw - 16);
+        }
+    }
+
+    /** */
+    private void cftf161(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+        wn4r = w[wP + 1];
+        wk1i = wn4r * w[wP + 2];
+        wk1r = wk1i + w[wP + 2];
+        x0r = a[aP + 0] + a[aP + 16];
+        x0i = a[aP + 1] + a[aP + 17];
+        x1r = a[aP + 0] - a[aP + 16];
+        x1i = a[aP + 1] - a[aP + 17];
+        x2r = a[aP + 8] + a[aP + 24];
+        x2i = a[aP + 9] + a[aP + 25];
+        x3r = a[aP + 8] - a[aP + 24];
+        x3i = a[aP + 9] - a[aP + 25];
+        y0r = x0r + x2r;
+        y0i = x0i + x2i;
+        y4r = x0r - x2r;
+        y4i = x0i - x2i;
+        y8r = x1r - x3i;
+        y8i = x1i + x3r;
+        y12r = x1r + x3i;
+        y12i = x1i - x3r;
+        x0r = a[aP + 2] + a[aP + 18];
+        x0i = a[aP + 3] + a[aP + 19];
+        x1r = a[aP + 2] - a[aP + 18];
+        x1i = a[aP + 3] - a[aP + 19];
+        x2r = a[aP + 10] + a[aP + 26];
+        x2i = a[aP + 11] + a[aP + 27];
+        x3r = a[aP + 10] - a[aP + 26];
+        x3i = a[aP + 11] - a[aP + 27];
+        y1r = x0r + x2r;
+        y1i = x0i + x2i;
+        y5r = x0r - x2r;
+        y5i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y9r = wk1r * x0r - wk1i * x0i;
+        y9i = wk1r * x0i + wk1i * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y13r = wk1i * x0r - wk1r * x0i;
+        y13i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 4] + a[aP + 20];
+        x0i = a[aP + 5] + a[aP + 21];
+        x1r = a[aP + 4] - a[aP + 20];
+        x1i = a[aP + 5] - a[aP + 21];
+        x2r = a[aP + 12] + a[aP + 28];
+        x2i = a[aP + 13] + a[aP + 29];
+        x3r = a[aP + 12] - a[aP + 28];
+        x3i = a[aP + 13] - a[aP + 29];
+        y2r = x0r + x2r;
+        y2i = x0i + x2i;
+        y6r = x0r - x2r;
+        y6i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y10r = wn4r * (x0r - x0i);
+        y10i = wn4r * (x0i + x0r);
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y14r = wn4r * (x0r + x0i);
+        y14i = wn4r * (x0i - x0r);
+        x0r = a[aP + 6] + a[aP + 22];
+        x0i = a[aP + 7] + a[aP + 23];
+        x1r = a[aP + 6] - a[aP + 22];
+        x1i = a[aP + 7] - a[aP + 23];
+        x2r = a[aP + 14] + a[aP + 30];
+        x2i = a[aP + 15] + a[aP + 31];
+        x3r = a[aP + 14] - a[aP + 30];
+        x3i = a[aP + 15] - a[aP + 31];
+        y3r = x0r + x2r;
+        y3i = x0i + x2i;
+        y7r = x0r - x2r;
+        y7i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        y11r = wk1i * x0r - wk1r * x0i;
+        y11i = wk1i * x0i + wk1r * x0r;
+        x0r = x1r + x3i;
+        x0i = x1i - x3r;
+        y15r = wk1r * x0r - wk1i * x0i;
+        y15i = wk1r * x0i + wk1i * x0r;
+        x0r = y12r - y14r;
+        x0i = y12i - y14i;
+        x1r = y12r + y14r;
+        x1i = y12i + y14i;
+        x2r = y13r - y15r;
+        x2i = y13i - y15i;
+        x3r = y13r + y15r;
+        x3i = y13i + y15i;
+        a[aP + 24] = x0r + x2r;
+        a[aP + 25] = x0i + x2i;
+        a[aP + 26] = x0r - x2r;
+        a[aP + 27] = x0i - x2i;
+        a[aP + 28] = x1r - x3i;
+        a[aP + 29] = x1i + x3r;
+        a[aP + 30] = x1r + x3i;
+        a[aP + 31] = x1i - x3r;
+        x0r = y8r + y10r;
+        x0i = y8i + y10i;
+        x1r = y8r - y10r;
+        x1i = y8i - y10i;
+        x2r = y9r + y11r;
+        x2i = y9i + y11i;
+        x3r = y9r - y11r;
+        x3i = y9i - y11i;
+        a[aP + 16] = x0r + x2r;
+        a[aP + 17] = x0i + x2i;
+        a[aP + 18] = x0r - x2r;
+        a[aP + 19] = x0i - x2i;
+        a[aP + 20] = x1r - x3i;
+        a[aP + 21] = x1i + x3r;
+        a[aP + 22] = x1r + x3i;
+        a[aP + 23] = x1i - x3r;
+        x0r = y5r - y7i;
+        x0i = y5i + y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        x0r = y5r + y7i;
+        x0i = y5i - y7r;
+        x3r = wn4r * (x0r - x0i);
+        x3i = wn4r * (x0i + x0r);
+        x0r = y4r - y6i;
+        x0i = y4i + y6r;
+        x1r = y4r + y6i;
+        x1i = y4i - y6r;
+        a[aP + 8] = x0r + x2r;
+        a[aP + 9] = x0i + x2i;
+        a[aP + 10] = x0r - x2r;
+        a[aP + 11] = x0i - x2i;
+        a[aP + 12] = x1r - x3i;
+        a[aP + 13] = x1i + x3r;
+        a[aP + 14] = x1r + x3i;
+        a[aP + 15] = x1i - x3r;
+        x0r = y0r + y2r;
+        x0i = y0i + y2i;
+        x1r = y0r - y2r;
+        x1i = y0i - y2i;
+        x2r = y1r + y3r;
+        x2i = y1i + y3i;
+        x3r = y1r - y3r;
+        x3i = y1i - y3i;
+        a[aP + 0] = x0r + x2r;
+        a[aP + 1] = x0i + x2i;
+        a[aP + 2] = x0r - x2r;
+        a[aP + 3] = x0i - x2i;
+        a[aP + 4] = x1r - x3i;
+        a[aP + 5] = x1i + x3r;
+        a[aP + 6] = x1r + x3i;
+        a[aP + 7] = x1i - x3r;
+    }
+
+    /** */
+    private void cftf162(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, x0r, x0i, x1r, x1i, x2r, x2i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+        wn4r = w[wP + 1];
+        wk1r = w[wP + 4];
+        wk1i = w[wP + 5];
+        wk3r = w[wP + 6];
+        wk3i = w[wP + 7];
+        wk2r = w[wP + 8];
+        wk2i = w[wP + 9];
+        x1r = a[aP + 0] - a[aP + 17];
+        x1i = a[aP + 1] + a[aP + 16];
+        x0r = a[aP + 8] - a[aP + 25];
+        x0i = a[aP + 9] + a[aP + 24];
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        y0r = x1r + x2r;
+        y0i = x1i + x2i;
+        y4r = x1r - x2r;
+        y4i = x1i - x2i;
+        x1r = a[aP + 0] + a[aP + 17];
+        x1i = a[aP + 1] - a[aP + 16];
+        x0r = a[aP + 8] + a[aP + 25];
+        x0i = a[aP + 9] - a[aP + 24];
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        y8r = x1r - x2i;
+        y8i = x1i + x2r;
+        y12r = x1r + x2i;
+        y12i = x1i - x2r;
+        x0r = a[aP + 2] - a[aP + 19];
+        x0i = a[aP + 3] + a[aP + 18];
+        x1r = wk1r * x0r - wk1i * x0i;
+        x1i = wk1r * x0i + wk1i * x0r;
+        x0r = a[aP + 10] - a[aP + 27];
+        x0i = a[aP + 11] + a[aP + 26];
+        x2r = wk3i * x0r - wk3r * x0i;
+        x2i = wk3i * x0i + wk3r * x0r;
+        y1r = x1r + x2r;
+        y1i = x1i + x2i;
+        y5r = x1r - x2r;
+        y5i = x1i - x2i;
+        x0r = a[aP + 2] + a[aP + 19];
+        x0i = a[aP + 3] - a[aP + 18];
+        x1r = wk3r * x0r - wk3i * x0i;
+        x1i = wk3r * x0i + wk3i * x0r;
+        x0r = a[aP + 10] + a[aP + 27];
+        x0i = a[aP + 11] - a[aP + 26];
+        x2r = wk1r * x0r + wk1i * x0i;
+        x2i = wk1r * x0i - wk1i * x0r;
+        y9r = x1r - x2r;
+        y9i = x1i - x2i;
+        y13r = x1r + x2r;
+        y13i = x1i + x2i;
+        x0r = a[aP + 4] - a[aP + 21];
+        x0i = a[aP + 5] + a[aP + 20];
+        x1r = wk2r * x0r - wk2i * x0i;
+        x1i = wk2r * x0i + wk2i * x0r;
+        x0r = a[aP + 12] - a[aP + 29];
+        x0i = a[aP + 13] + a[aP + 28];
+        x2r = wk2i * x0r - wk2r * x0i;
+        x2i = wk2i * x0i + wk2r * x0r;
+        y2r = x1r + x2r;
+        y2i = x1i + x2i;
+        y6r = x1r - x2r;
+        y6i = x1i - x2i;
+        x0r = a[aP + 4] + a[aP + 21];
+        x0i = a[aP + 5] - a[aP + 20];
+        x1r = wk2i * x0r - wk2r * x0i;
+        x1i = wk2i * x0i + wk2r * x0r;
+        x0r = a[aP + 12] + a[aP + 29];
+        x0i = a[aP + 13] - a[aP + 28];
+        x2r = wk2r * x0r - wk2i * x0i;
+        x2i = wk2r * x0i + wk2i * x0r;
+        y10r = x1r - x2r;
+        y10i = x1i - x2i;
+        y14r = x1r + x2r;
+        y14i = x1i + x2i;
+        x0r = a[aP + 6] - a[aP + 23];
+        x0i = a[aP + 7] + a[aP + 22];
+        x1r = wk3r * x0r - wk3i * x0i;
+        x1i = wk3r * x0i + wk3i * x0r;
+        x0r = a[aP + 14] - a[aP + 31];
+        x0i = a[aP + 15] + a[aP + 30];
+        x2r = wk1i * x0r - wk1r * x0i;
+        x2i = wk1i * x0i + wk1r * x0r;
+        y3r = x1r + x2r;
+        y3i = x1i + x2i;
+        y7r = x1r - x2r;
+        y7i = x1i - x2i;
+        x0r = a[aP + 6] + a[aP + 23];
+        x0i = a[aP + 7] - a[aP + 22];
+        x1r = wk1i * x0r + wk1r * x0i;
+        x1i = wk1i * x0i - wk1r * x0r;
+        x0r = a[aP + 14] + a[aP + 31];
+        x0i = a[aP + 15] - a[aP + 30];
+        x2r = wk3i * x0r - wk3r * x0i;
+        x2i = wk3i * x0i + wk3r * x0r;
+        y11r = x1r + x2r;
+        y11i = x1i + x2i;
+        y15r = x1r - x2r;
+        y15i = x1i - x2i;
+        x1r = y0r + y2r;
+        x1i = y0i + y2i;
+        x2r = y1r + y3r;
+        x2i = y1i + y3i;
+        a[aP + 0] = x1r + x2r;
+        a[aP + 1] = x1i + x2i;
+        a[aP + 2] = x1r - x2r;
+        a[aP + 3] = x1i - x2i;
+        x1r = y0r - y2r;
+        x1i = y0i - y2i;
+        x2r = y1r - y3r;
+        x2i = y1i - y3i;
+        a[aP + 4] = x1r - x2i;
+        a[aP + 5] = x1i + x2r;
+        a[aP + 6] = x1r + x2i;
+        a[aP + 7] = x1i - x2r;
+        x1r = y4r - y6i;
+        x1i = y4i + y6r;
+        x0r = y5r - y7i;
+        x0i = y5i + y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 8] = x1r + x2r;
+        a[aP + 9] = x1i + x2i;
+        a[aP + 10] = x1r - x2r;
+        a[aP + 11] = x1i - x2i;
+        x1r = y4r + y6i;
+        x1i = y4i - y6r;
+        x0r = y5r + y7i;
+        x0i = y5i - y7r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 12] = x1r - x2i;
+        a[aP + 13] = x1i + x2r;
+        a[aP + 14] = x1r + x2i;
+        a[aP + 15] = x1i - x2r;
+        x1r = y8r + y10r;
+        x1i = y8i + y10i;
+        x2r = y9r - y11r;
+        x2i = y9i - y11i;
+        a[aP + 16] = x1r + x2r;
+        a[aP + 17] = x1i + x2i;
+        a[aP + 18] = x1r - x2r;
+        a[aP + 19] = x1i - x2i;
+        x1r = y8r - y10r;
+        x1i = y8i - y10i;
+        x2r = y9r + y11r;
+        x2i = y9i + y11i;
+        a[aP + 20] = x1r - x2i;
+        a[aP + 21] = x1i + x2r;
+        a[aP + 22] = x1r + x2i;
+        a[aP + 23] = x1i - x2r;
+        x1r = y12r - y14i;
+        x1i = y12i + y14r;
+        x0r = y13r + y15i;
+        x0i = y13i - y15r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 24] = x1r + x2r;
+        a[aP + 25] = x1i + x2i;
+        a[aP + 26] = x1r - x2r;
+        a[aP + 27] = x1i - x2i;
+        x1r = y12r + y14i;
+        x1i = y12i - y14r;
+        x0r = y13r - y15i;
+        x0i = y13i + y15r;
+        x2r = wn4r * (x0r - x0i);
+        x2i = wn4r * (x0i + x0r);
+        a[aP + 28] = x1r - x2i;
+        a[aP + 29] = x1i + x2r;
+        a[aP + 30] = x1r + x2i;
+        a[aP + 31] = x1i - x2r;
+    }
+
+    /** */
+    private void cftf081(double[] a, int aP, double[] w, int wP) {
+        double wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+        wn4r = w[wP + 1];
+        x0r = a[aP + 0] + a[aP + 8];
+        x0i = a[aP + 1] + a[aP + 9];
+        x1r = a[aP + 0] - a[aP + 8];
+        x1i = a[aP + 1] - a[aP + 9];
+        x2r = a[aP + 4] + a[aP + 12];
+        x2i = a[aP + 5] + a[aP + 13];
+        x3r = a[aP + 4] - a[aP + 12];
+        x3i = a[aP + 5] - a[aP + 13];
+        y0r = x0r + x2r;
+        y0i = x0i + x2i;
+        y2r = x0r - x2r;
+        y2i = x0i - x2i;
+        y1r = x1r - x3i;
+        y1i = x1i + x3r;
+        y3r = x1r + x3i;
+        y3i = x1i - x3r;
+        x0r = a[aP + 2] + a[aP + 10];
+        x0i = a[aP + 3] + a[aP + 11];
+        x1r = a[aP + 2] - a[aP + 10];
+        x1i = a[aP + 3] - a[aP + 11];
+        x2r = a[aP + 6] + a[aP + 14];
+        x2i = a[aP + 7] + a[aP + 15];
+        x3r = a[aP + 6] - a[aP + 14];
+        x3i = a[aP + 7] - a[aP + 15];
+        y4r = x0r + x2r;
+        y4i = x0i + x2i;
+        y6r = x0r - x2r;
+        y6i = x0i - x2i;
+        x0r = x1r - x3i;
+        x0i = x1i + x3r;
+        x2r = x1r + x3i;
+        x2i = x1i - x3r;
+        y5r = wn4r * (x0r - x0i);
+        y5i = wn4r * (x0r + x0i);
+        y7r = wn4r * (x2r - x2i);
+        y7i = wn4r * (x2r + x2i);
+        a[aP + 8] = y1r + y5r;
+        a[aP + 9] = y1i + y5i;
+        a[aP + 10] = y1r - y5r;
+        a[aP + 11] = y1i - y5i;
+        a[aP + 12] = y3r - y7i;
+        a[aP + 13] = y3i + y7r;
+        a[aP + 14] = y3r + y7i;
+        a[aP + 15] = y3i - y7r;
+        a[aP + 0] = y0r + y4r;
+        a[aP + 1] = y0i + y4i;
+        a[aP + 2] = y0r - y4r;
+        a[aP + 3] = y0i - y4i;
+        a[aP + 4] = y2r - y6i;
+        a[aP + 5] = y2i + y6r;
+        a[aP + 6] = y2r + y6i;
+        a[aP + 7] = y2i - y6r;
+    }
+
+    /** */
+    private void cftf082(double[] a, int aP, double[] w, int wP) {
+        double wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+        wn4r = w[wP + 1];
+        wk1r = w[wP + 4];
+        wk1i = w[wP + 5];
+        y0r = a[aP + 0] - a[aP + 9];
+        y0i = a[aP + 1] + a[aP + 8];
+        y1r = a[aP + 0] + a[aP + 9];
+        y1i = a[aP + 1] - a[aP + 8];
+        x0r = a[aP + 4] - a[aP + 13];
+        x0i = a[aP + 5] + a[aP + 12];
+        y2r = wn4r * (x0r - x0i);
+        y2i = wn4r * (x0i + x0r);
+        x0r = a[aP + 4] + a[aP + 13];
+        x0i = a[aP + 5] - a[aP + 12];
+        y3r = wn4r * (x0r - x0i);
+        y3i = wn4r * (x0i + x0r);
+        x0r = a[aP + 2] - a[aP + 11];
+        x0i = a[aP + 3] + a[aP + 10];
+        y4r = wk1r * x0r - wk1i * x0i;
+        y4i = wk1r * x0i + wk1i * x0r;
+        x0r = a[aP + 2] + a[aP + 11];
+        x0i = a[aP + 3] - a[aP + 10];
+        y5r = wk1i * x0r - wk1r * x0i;
+        y5i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 6] - a[aP + 15];
+        x0i = a[aP + 7] + a[aP + 14];
+        y6r = wk1i * x0r - wk1r * x0i;
+        y6i = wk1i * x0i + wk1r * x0r;
+        x0r = a[aP + 6] + a[aP + 15];
+        x0i = a[aP + 7] - a[aP + 14];
+        y7r = wk1r * x0r - wk1i * x0i;
+        y7i = wk1r * x0i + wk1i * x0r;
+        x0r = y0r + y2r;
+        x0i = y0i + y2i;
+        x1r = y4r + y6r;
+        x1i = y4i + y6i;
+        a[aP + 0] = x0r + x1r;
+        a[aP + 1] = x0i + x1i;
+        a[aP + 2] = x0r - x1r;
+        a[aP + 3] = x0i - x1i;
+        x0r = y0r - y2r;
+        x0i = y0i - y2i;
+        x1r = y4r - y6r;
+        x1i = y4i - y6i;
+        a[aP + 4] = x0r - x1i;
+        a[aP + 5] = x0i + x1r;
+        a[aP + 6] = x0r + x1i;
+        a[aP + 7] = x0i - x1r;
+        x0r = y1r - y3i;
+        x0i = y1i + y3r;
+        x1r = y5r - y7r;
+        x1i = y5i - y7i;
+        a[aP + 8] = x0r + x1r;
+        a[aP + 9] = x0i + x1i;
+        a[aP + 10] = x0r - x1r;
+        a[aP + 11] = x0i - x1i;
+        x0r = y1r + y3i;
+        x0i = y1i - y3r;
+        x1r = y5r + y7r;
+        x1i = y5i + y7i;
+        a[aP + 12] = x0r - x1i;
+        a[aP + 13] = x0i + x1r;
+        a[aP + 14] = x0r + x1i;
+        a[aP + 15] = x0i - x1r;
+    }
+
+    /**
+     * 3rd
+     * when n = 8.
+     *
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftf040(double[] a) {
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        x0r = a[0] + a[4];
+        x0i = a[1] + a[5];
+        x1r = a[0] - a[4];
+        x1i = a[1] - a[5];
+        x2r = a[2] + a[6];
+        x2i = a[3] + a[7];
+        x3r = a[2] - a[6];
+        x3i = a[3] - a[7];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[4] = x0r - x2r;
+        a[5] = x0i - x2i;
+        a[2] = x1r - x3i;
+        a[3] = x1i + x3r;
+        a[6] = x1r + x3i;
+        a[7] = x1i - x3r;
+    }
+
+    /**
+     * 3rd
+     * when n = 8.
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     */
+    private void cftb040(double[] a) {
+        double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+        x0r = a[0] + a[4];
+        x0i = a[1] + a[5];
+        x1r = a[0] - a[4];
+        x1i = a[1] - a[5];
+        x2r = a[2] + a[6];
+        x2i = a[3] + a[7];
+        x3r = a[2] - a[6];
+        x3i = a[3] - a[7];
+        a[0] = x0r + x2r;
+        a[1] = x0i + x2i;
+        a[4] = x0r - x2r;
+        a[5] = x0i - x2i;
+        a[2] = x1r + x3i;
+        a[3] = x1i - x3r;
+        a[6] = x1r - x3i;
+        a[7] = x1i + x3r;
+    }
+
+    /**
+     * 3rd
+     * when n = 4.
+     *
+     * @see #cftbsub(int, double[], int[], int, int, double[])
+     * @see #cftfsub(int, double[], int[], int, int, double[])
+     */
+    private void cftx020(double[] a) {
+        double x0r, x0i;
+
+        x0r = a[0] - a[2];
+        x0i = a[1] - a[3];
+        a[0] += a[2];
+        a[1] += a[3];
+        a[2] = x0r;
+        a[3] = x0i;
+    }
+
+    /**
+     * 2nd
+     *
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void rftfsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr, xi, yr, yi;
+
+        m = n >> 1;
+        ks = 2 * nc / m;
+        kk = 0;
+        for (j = 2; j < m; j += 2) {
+            k = n - j;
+            kk += ks;
+            wkr = 0.5 - c[cP + nc - kk];
+            wki = c[cP + kk];
+            xr = a[j] - a[k];
+            xi = a[j + 1] + a[k + 1];
+            yr = wkr * xr - wki * xi;
+            yi = wkr * xi + wki * xr;
+            a[j] -= yr;
+            a[j + 1] -= yi;
+            a[k] += yr;
+            a[k + 1] -= yi;
+        }
+    }
+
+    /**
+     * 2nd
+     *
+     * @see #rdft(int, int, double[], int[], double[])
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #ddst(int, int, double[], int[], double[])
+     */
+    private void rftbsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr, xi, yr, yi;
+
+        m = n >> 1;
+        ks = 2 * nc / m;
+        kk = 0;
+        for (j = 2; j < m; j += 2) {
+            k = n - j;
+            kk += ks;
+            wkr = 0.5 - c[cP + nc - kk];
+            wki = c[cP + kk];
+            xr = a[j] - a[k];
+            xi = a[j + 1] + a[k + 1];
+            yr = wkr * xr + wki * xi;
+            yi = wkr * xi - wki * xr;
+            a[j] -= yr;
+            a[j + 1] -= yi;
+            a[k] += yr;
+            a[k + 1] -= yi;
+        }
+    }
+
+    /**
+     * 2nd
+     *
+     * @see #ddct(int, int, double[], int[], double[])
+     * @see #dfct(int, double[], double[], int[], double[])
+     */
+    private void dctsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr;
+
+        m = n >> 1;
+        ks = nc / n;
+        kk = 0;
+        for (j = 1; j < m; j++) {
+            k = n - j;
+            kk += ks;
+            wkr = c[cP + kk] - c[cP + nc - kk];
+            wki = c[cP + kk] + c[cP + nc - kk];
+            xr = wki * a[j] - wkr * a[k];
+            a[j] = wkr * a[j] + wki * a[k];
+            a[k] = xr;
+        }
+        a[m] *= c[cP + 0];
+    }
+
+    /**
+     * 2nd
+     *
+     * @see #ddst(int, int, double[], int[], double[])
+     * @see #dfst(int, double[], double[], int[], double[])
+     */
+    private void dstsub(int n, double[] a, int nc, double[] c, int cP) {
+        int j, k, kk, ks, m;
+        double wkr, wki, xr;
+
+        m = n >> 1;
+        ks = nc / n;
+        kk = 0;
+        for (j = 1; j < m; j++) {
+            k = n - j;
+            kk += ks;
+            wkr = c[cP + kk] - c[cP + nc - kk];
+            wki = c[cP + kk] + c[cP + nc - kk];
+            xr = wki * a[k] - wkr * a[j];
+            a[k] = wkr * a[k] + wki * a[j];
+            a[j] = xr;
+        }
+        a[m] *= c[cP + 0];
+    }
+}
+
+/* */

+ 3 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/bean/SimpleWord.kt

@@ -0,0 +1,3 @@
+package cn.i2edu.dubbing_lib.bean
+
+data class SimpleWord (val content: String, val score: Float)

+ 5 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/bean/VideoData.kt

@@ -0,0 +1,5 @@
+package cn.i2edu.dubbing_lib.bean
+
+data class VideoData (val videoId: String, val bgmUrl: String, val endTimeList: List<Long>, val durationList: List<Long>, val videoPath: String,
+                      val audioDecodePaths: List<String>, var bgmPath: String?=null, var decodeBgmPath: String?=null, var decodeAsyncBgmPath: String?=null,
+                      var encodeAudioWithBgmPath: String?=null, var mixVideoPath: String?=null)

+ 7 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/callback/MixinHandlerCallback.kt

@@ -0,0 +1,7 @@
+package cn.i2edu.dubbing_lib.callback
+
+import android.os.Message
+
+interface MixinHandlerCallback {
+     fun onHandleMessage(msg: Message)
+}

+ 11 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/listener/DownloadListener.kt

@@ -0,0 +1,11 @@
+package com.i2edu.dubbing_lib.listener
+
+interface DownloadListener {
+    fun onStart()
+
+    fun onProgress(progress: Int)
+
+    fun onFinish(localPath: String)
+
+    fun onFailure();
+}

+ 111 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/util/AudioEvaluatorUtil.kt

@@ -0,0 +1,111 @@
+package com.i2edu.dubbing_lib.util
+
+import android.content.Context
+import android.os.Bundle
+import android.util.Log
+import cn.i2edu.dubbing_lib.audioUtils.audiotransfer.DecodeEngine
+import cn.i2edu.dubbing_lib.audioUtils.audiotransfer.DecodeOperateInterface
+import com.iflytek.cloud.*
+import java.io.File
+import java.io.FileInputStream
+
+interface EvaluatorCallBack {
+    fun onResult(result: EvaluatorResult)
+    fun onError(error: SpeechError)
+}
+
+class AudioEvaluatorUtil private constructor(val context: Context){
+
+    private var evaluatorCallBack: EvaluatorCallBack? = null
+    private var speechEvaluator: SpeechEvaluator? = null
+    private val Tag: String = "AudioEvaluatorUtil"
+
+    companion object {
+        @Volatile private var instance: AudioEvaluatorUtil? = null
+        fun getInstance(context: Context) = instance
+                ?: synchronized(this) {
+            instance
+                    ?: AudioEvaluatorUtil(context)
+                    .also {
+                        it.speechEvaluator = SpeechEvaluator.createEvaluator(context, null)
+                    }
+                    .also { instance = it }
+        }
+    }
+
+    fun startEvaluator(en: String, filePath: String, decodedFilePath: String) {
+        speechEvaluator?.setParameter(SpeechConstant.LANGUAGE, "en_us")
+        speechEvaluator?.setParameter(SpeechConstant.ISE_CATEGORY, "read_sentence")
+        speechEvaluator?.setParameter(SpeechConstant.AUDIO_SOURCE,"-1")
+
+        if (!File(decodedFilePath).exists()) {
+            File(decodedFilePath).createNewFile()
+        }
+        Thread{
+            DecodeEngine.getInstance(context).beginDecodeMusicFile(filePath, decodedFilePath, 0, 16000, 1, 2, object : DecodeOperateInterface {
+                override fun updateDecodeProgress(decodeProgress: Int) {
+                }
+
+                override fun decodeSuccess() {
+                    try {
+                        val file = File(decodedFilePath)
+                        val fileInputStream = FileInputStream(file)
+                        val bytes = ByteArray(1024)
+                        speechEvaluator?.startEvaluating(en, null, object : EvaluatorListener {
+                            override fun onVolumeChanged(p0: Int, p1: ByteArray?) {
+                            }
+
+                            override fun onResult(p0: EvaluatorResult, p1: Boolean) {
+                                Log.d(Tag, p0.toString())
+                                evaluatorCallBack?.onResult(p0)
+                                file.delete()
+                                onDestory()
+                            }
+
+                            override fun onBeginOfSpeech() {
+                            }
+
+                            override fun onEvent(p0: Int, p1: Int, p2: Int, p3: Bundle?) {
+                            }
+
+                            override fun onEndOfSpeech() {
+                            }
+
+                            override fun onError(p0: SpeechError) {
+                                Log.d(Tag, p0.toString())
+                                evaluatorCallBack?.onError(p0)
+                                file.delete()
+                                onDestory()
+                            }
+                        })
+                        var byteCount = fileInputStream.read(bytes)
+                        while (byteCount > 0) {
+                            speechEvaluator?.writeAudio(bytes, 0, byteCount)
+                            Thread.sleep(10)
+                            byteCount = fileInputStream.read(bytes)
+                        }
+                        speechEvaluator?.stopEvaluating()
+                        fileInputStream.close()
+                    } catch (e: Exception) {
+                        e.printStackTrace()
+                    }
+
+                }
+
+                override fun decodeFail() {
+                    val speechError = SpeechError(1, "评测失败,请稍后再试!")
+                    evaluatorCallBack?.onError(speechError)
+                }
+            })
+        }.start()
+    }
+
+    fun setEvaluatorCallBack(callBack: EvaluatorCallBack): AudioEvaluatorUtil?{
+        this.evaluatorCallBack = callBack
+        return instance
+    }
+
+    fun onDestory() {
+        speechEvaluator?.destroy()
+    }
+}

+ 141 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/util/DownloadUtil.kt

@@ -0,0 +1,141 @@
+package com.i2edu.dubbing_lib.util
+
+import android.os.Environment
+import android.util.Log
+import cn.i2edu.dubbing_lib.api.DownloadHelper
+import okhttp3.ResponseBody
+import retrofit2.Call
+import cn.i2edu.dubbing_lib.api.DownloadService
+import com.i2edu.dubbing_lib.listener.DownloadListener
+import retrofit2.Callback
+import retrofit2.Response
+import java.io.*
+
+
+class DownloadUtil {
+
+    companion object {
+        private val Tag: String = "DownloadUtil"
+        val PATH_VIDEO = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_video/"
+        val PATH_BGM = "${Environment.getExternalStorageDirectory().absoluteFile}/i2_file/i2_bgm/"
+    }
+
+    protected lateinit var mApi: DownloadService
+    private var mCall: Call<ResponseBody>? = null
+    private var mThread: Thread? = null
+
+    constructor() {
+        if (!this::mApi.isInitialized) {
+            mApi = DownloadHelper.instance.buildRetrofit(" http://cdn.i2nexted.com/upload/video/file/")
+                    .createRetrofit(DownloadService::class.java)
+        }
+    }
+
+    fun downloadFile(url: String, downloadListener: DownloadListener) {
+        downloadListener.onStart()
+        val file = File(PATH_VIDEO)
+        if (!file.exists()) {
+            file.mkdirs()
+        }
+        var name = url
+        val i = url.lastIndexOf('/')
+        if (i == -1) {
+            return
+        }
+        name = PATH_VIDEO + name.substring(i)
+        val mFile = File(name)
+        if (!mFile.exists()) {
+            if (!mFile.createNewFile()) {
+                return
+            }
+        }
+        // 下载
+        mCall = mApi.downFile(url)
+        mCall?.enqueue(object : Callback<ResponseBody>{
+            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
+                downloadListener.onFailure()
+            }
+
+            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
+                mThread = Thread {
+                    saveToFile(response, mFile, downloadListener)
+                }
+                mThread?.start()
+            }
+        })
+    }
+
+    /**
+     * download BGM file with [url] and  [downloadListener]
+     */
+    fun downloadBGMFile(url: String, downloadListener: DownloadListener) {
+        downloadListener.onStart()
+        val file = File(PATH_BGM)
+        if (!file.exists()) {
+            file.mkdirs()
+        }
+        var name = url
+        val i = url.lastIndexOf('/')
+        if (i == -1) {
+            return
+        }
+        name = PATH_BGM + name.substring(i)
+        val mFile = File(name)
+        if (!mFile.exists()) {
+            if (!mFile.createNewFile()) {
+                return
+            }
+        }
+        // 下载
+        mCall = mApi.downFile(url)
+        mCall?.enqueue(object : Callback<ResponseBody>{
+            override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
+                downloadListener.onFailure()
+            }
+
+            override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
+                mThread = Thread {
+                    saveToFile(response, mFile, downloadListener)
+                }
+                mThread?.start()
+            }
+        })
+    }
+
+    private fun saveToFile(response: Response<ResponseBody>, mFile: File, downloadListener: DownloadListener) {
+        var currentLength: Long = 0
+        var os: OutputStream? = null
+
+        val iStream = response.body()?.byteStream()
+        val totalLength = response.body()?.contentLength()
+
+        try {
+            os = FileOutputStream(mFile)
+            val buff = ByteArray(1024)
+            do{
+                val len = iStream!!.read(buff)
+                if (len == -1) {
+                    break
+                }
+                os.write(buff, 0, len)
+                currentLength += len
+                Log.i(Tag, "当前进度: ${(100 * currentLength / totalLength!!).toInt()}")
+                //计算当前下载百分比,并经由回调传出
+                downloadListener.onProgress((100 * currentLength / totalLength!!).toInt())
+                //当百分比为100时下载结束,调用结束回调,并传出下载后的本地路径
+                if ((100 * currentLength / totalLength).toInt() == 100) {
+                    downloadListener.onFinish(mFile.absolutePath)//下载完成
+                }
+            } while (true)
+        } catch (e: FileNotFoundException) {
+            e.printStackTrace()
+        } catch (e: IOException) {
+            e.printStackTrace()
+        } finally {
+            os?.close()
+            iStream?.close()
+        }
+
+    }
+
+}

+ 54 - 0
android/src/main/kotlin/cn/i2edu/dubbing_lib/util/PausableThreadPool.java

@@ -0,0 +1,54 @@
+package cn.i2edu.dubbing_lib.util;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class PausableThreadPool extends ThreadPoolExecutor {
+    private volatile boolean isPaused;
+    private final ReentrantLock pauseLock = new ReentrantLock();
+    private final Condition unpaused = pauseLock.newCondition();
+
+    public PausableThreadPool(int nThreads) {
+        super(nThreads, nThreads,
+                0L, TimeUnit.MILLISECONDS,
+                new LinkedBlockingQueue<Runnable>(), new DiscardOldestPolicy());
+    }
+
+    protected void beforeExecute(Thread t, Runnable r) {
+        super.beforeExecute(t, r);
+        pauseLock.lock();
+        try {
+            while (isPaused) unpaused.await();
+        } catch (InterruptedException ie) {
+            t.interrupt();
+        } finally {
+            pauseLock.unlock();
+        }
+    }
+
+    public void pause() {
+        pauseLock.lock();
+        try {
+            isPaused = true;
+        } finally {
+            pauseLock.unlock();
+        }
+    }
+
+    public void resume() {
+        pauseLock.lock();
+        try {
+            isPaused = false;
+            unpaused.signalAll();
+        } finally {
+            pauseLock.unlock();
+        }
+    }
+
+    public boolean isPaused() {
+        return isPaused;
+    }
+}  

+ 19 - 0
dubbing_lib.iml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
+      <excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
+      <excludeFolder url="file://$MODULE_DIR$/.idea" />
+      <excludeFolder url="file://$MODULE_DIR$/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+      <excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
+      <excludeFolder url="file://$MODULE_DIR$/example/.pub" />
+      <excludeFolder url="file://$MODULE_DIR$/example/build" />
+    </content>
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Dart SDK" level="project" />
+    <orderEntry type="library" name="Flutter Plugins" level="project" />
+  </component>
+</module>

+ 73 - 0
example/.gitignore

@@ -0,0 +1,73 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Android related
+**/android/**/gradle-wrapper.jar
+**/android/.gradle
+**/android/captures/
+**/android/gradlew
+**/android/gradlew.bat
+**/android/local.properties
+**/android/**/GeneratedPluginRegistrant.java
+
+# iOS/XCode related
+**/ios/**/*.mode1v3
+**/ios/**/*.mode2v3
+**/ios/**/*.moved-aside
+**/ios/**/*.pbxuser
+**/ios/**/*.perspectivev3
+**/ios/**/*sync/
+**/ios/**/.sconsign.dblite
+**/ios/**/.tags*
+**/ios/**/.vagrant/
+**/ios/**/DerivedData/
+**/ios/**/Icon?
+**/ios/**/Pods/
+**/ios/**/.symlinks/
+**/ios/**/profile
+**/ios/**/xcuserdata
+**/ios/.generated/
+**/ios/Flutter/App.framework
+**/ios/Flutter/Flutter.framework
+**/ios/Flutter/Generated.xcconfig
+**/ios/Flutter/app.flx
+**/ios/Flutter/app.zip
+**/ios/Flutter/flutter_assets/
+**/ios/Flutter/flutter_export_environment.sh
+**/ios/ServiceDefinitions.json
+**/ios/Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!**/ios/**/default.mode1v3
+!**/ios/**/default.mode2v3
+!**/ios/**/default.pbxuser
+!**/ios/**/default.perspectivev3
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

+ 10 - 0
example/.metadata

@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
+  channel: stable
+
+project_type: app

+ 16 - 0
example/README.md

@@ -0,0 +1,16 @@
+# dubbing_lib_example
+
+Demonstrates how to use the dubbing_lib plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.

+ 67 - 0
example/android/app/build.gradle

@@ -0,0 +1,67 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+    localPropertiesFile.withReader('UTF-8') { reader ->
+        localProperties.load(reader)
+    }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+    throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+    flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+    flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+    compileSdkVersion 28
+
+    sourceSets {
+        main.java.srcDirs += 'src/main/kotlin'
+    }
+
+    lintOptions {
+        disable 'InvalidPackage'
+    }
+
+    defaultConfig {
+        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+        applicationId "cn.i2edu.dubbing_lib_example"
+        minSdkVersion 16
+        targetSdkVersion 28
+        versionCode flutterVersionCode.toInteger()
+        versionName flutterVersionName
+        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            // TODO: Add your own signing config for the release build.
+            // Signing with the debug keys for now, so `flutter run --release` works.
+            signingConfig signingConfigs.debug
+        }
+    }
+}
+
+flutter {
+    source '../..'
+}
+
+dependencies {
+    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+    testImplementation 'junit:junit:4.12'
+    androidTestImplementation 'com.android.support.test:runner:1.0.2'
+    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+}

+ 7 - 0
example/android/app/src/debug/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="cn.i2edu.dubbing_lib_example">
+    <!-- Flutter needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

+ 33 - 0
example/android/app/src/main/AndroidManifest.xml

@@ -0,0 +1,33 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="cn.i2edu.dubbing_lib_example">
+
+    <!-- io.flutter.app.FlutterApplication is an android.app.Application that
+         calls FlutterMain.startInitialization(this); in its onCreate method.
+         In most cases you can leave this as-is, but you if you want to provide
+         additional functionality it is fine to subclass or reimplement
+         FlutterApplication and put your custom class here. -->
+    <application
+        android:name="io.flutter.app.FlutterApplication"
+        android:label="dubbing_lib_example"
+        android:icon="@mipmap/ic_launcher">
+        <activity
+            android:name=".MainActivity"
+            android:launchMode="singleTop"
+            android:theme="@style/LaunchTheme"
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <!-- This keeps the window background of the activity showing
+                 until Flutter renders its first frame. It can be removed if
+                 there is no splash screen (such as the default splash screen
+                 defined in @style/LaunchTheme). -->
+            <meta-data
+                android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
+                android:value="true" />
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>

+ 13 - 0
example/android/app/src/main/kotlin/cn/i2edu/dubbing_lib_example/MainActivity.kt

@@ -0,0 +1,13 @@
+package cn.i2edu.dubbing_lib_example
+
+import android.os.Bundle
+
+import io.flutter.app.FlutterActivity
+import io.flutter.plugins.GeneratedPluginRegistrant
+
+class MainActivity: FlutterActivity() {
+  override fun onCreate(savedInstanceState: Bundle?) {
+    super.onCreate(savedInstanceState)
+    GeneratedPluginRegistrant.registerWith(this)
+  }
+}

+ 12 - 0
example/android/app/src/main/res/drawable/launch_background.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:drawable="@android:color/white" />
+
+    <!-- You can insert your own image assets here -->
+    <!-- <item>
+        <bitmap
+            android:gravity="center"
+            android:src="@mipmap/launch_image" />
+    </item> -->
+</layer-list>

BIN
example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 8 - 0
example/android/app/src/main/res/values/styles.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+        <!-- Show a splash screen on the activity. Automatically removed when
+             Flutter draws its first frame -->
+        <item name="android:windowBackground">@drawable/launch_background</item>
+    </style>
+</resources>

+ 7 - 0
example/android/app/src/profile/AndroidManifest.xml

@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="cn.i2edu.dubbing_lib_example">
+    <!-- Flutter needs it to communicate with the running application
+         to allow setting breakpoints, to provide hot reload, etc.
+    -->
+    <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>

+ 31 - 0
example/android/build.gradle

@@ -0,0 +1,31 @@
+buildscript {
+    ext.kotlin_version = '1.2.71'
+    repositories {
+        google()
+        jcenter()
+    }
+
+    dependencies {
+        classpath 'com.android.tools.build:gradle:3.2.1'
+        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+    project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+    project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

+ 2 - 0
example/android/gradle.properties

@@ -0,0 +1,2 @@
+org.gradle.jvmargs=-Xmx1536M
+

+ 6 - 0
example/android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip

+ 15 - 0
example/android/settings.gradle

@@ -0,0 +1,15 @@
+include ':app'
+
+def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
+
+def plugins = new Properties()
+def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
+if (pluginsFile.exists()) {
+    pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
+}
+
+plugins.each { name, path ->
+    def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
+    include ":$name"
+    project(":$name").projectDir = pluginDirectory
+}

+ 26 - 0
example/ios/Flutter/AppFrameworkInfo.plist

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>$(DEVELOPMENT_LANGUAGE)</string>
+  <key>CFBundleExecutable</key>
+  <string>App</string>
+  <key>CFBundleIdentifier</key>
+  <string>io.flutter.flutter.app</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>App</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+  <key>MinimumOSVersion</key>
+  <string>8.0</string>
+</dict>
+</plist>

+ 1 - 0
example/ios/Flutter/Debug.xcconfig

@@ -0,0 +1 @@
+#include "Generated.xcconfig"

+ 1 - 0
example/ios/Flutter/Release.xcconfig

@@ -0,0 +1 @@
+#include "Generated.xcconfig"

+ 511 - 0
example/ios/Runner.xcodeproj/project.pbxproj

@@ -0,0 +1,511 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
+		3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
+		9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
+		978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
+		97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
+		97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+		97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
+				9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
+				3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				3B80C3931E831B6300D905FE /* App.framework */,
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEBA1CF902C7004384FC /* Flutter.framework */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "<group>";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				97C146EF1CF9000F007C117D /* Products */,
+				CF3B75C9A7D2FA2A4C99F110 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,
+				7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FD1CF9000F007C117D /* Assets.xcassets */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				97C146F11CF9000F007C117D /* Supporting Files */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		97C146F11CF9000F007C117D /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				97C146F21CF9000F007C117D /* main.m */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1020;
+				ORGANIZATIONNAME = "The Chromium Authors";
+				TargetAttributes = {
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
+				97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+				97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,
+				97C146F31CF9000F007C117D /* main.m in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		249021D3217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Profile;
+		};
+		249021D4217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = cn.i2edu.dubbingLibExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Profile;
+		};
+		97C147031CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		97C147041CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		97C147061CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = cn.i2edu.dubbingLibExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Debug;
+		};
+		97C147071CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = cn.i2edu.dubbingLibExample;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug */,
+				97C147041CF9000F007C117D /* Release */,
+				249021D3217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug */,
+				97C147071CF9000F007C117D /* Release */,
+				249021D4217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}

+ 7 - 0
example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+</Workspace>

Some files were not shown because too many files changed in this diff