Initial commit
This commit is contained in:
23
.clang-format
Normal file
23
.clang-format
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
BasedOnStyle: LLVM,
|
||||
UseTab: ForContinuationAndIndentation,
|
||||
IndentWidth: 4,
|
||||
TabWidth: 4,
|
||||
BreakBeforeBraces: Attach,
|
||||
IndentCaseLabels: false,
|
||||
ColumnLimit: 0,
|
||||
AccessModifierOffset: -4,
|
||||
NamespaceIndentation: None,
|
||||
SpaceBeforeParens: ControlStatements,
|
||||
PointerAlignment: Right,
|
||||
SpaceAfterCStyleCast: false,
|
||||
SpaceAfterTemplateKeyword: false,
|
||||
SpaceBeforeAssignmentOperators: true,
|
||||
SpaceBeforeCtorInitializerColon: true,
|
||||
SpaceBeforeInheritanceColon: true,
|
||||
SpaceInEmptyParentheses: false,
|
||||
SpacesInAngles: false,
|
||||
SpacesInParentheses: false,
|
||||
SpacesInSquareBrackets: false,
|
||||
Standard: c++11,
|
||||
}
|
||||
8
.clang-tidy
Normal file
8
.clang-tidy
Normal file
@@ -0,0 +1,8 @@
|
||||
---
|
||||
Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,portability-*,bugprone-*'
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.MethodCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.ParameterCase
|
||||
value: camelBack
|
||||
...
|
||||
13
.editorconfig
Normal file
13
.editorconfig
Normal file
@@ -0,0 +1,13 @@
|
||||
[*]
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
vc_generate_documentation_comments = doxygen_slash_star
|
||||
end_of_line = lf
|
||||
|
||||
[*.bat]
|
||||
end_of_line = crlf
|
||||
|
||||
[*.lingo]
|
||||
charset = macroman
|
||||
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
/po/*.po encoding=utf-8
|
||||
*.lingo encoding=MacRoman
|
||||
/engines.awk eol=lf
|
||||
*.bat text eol=crlf
|
||||
*.cpp text eol=lf
|
||||
*.h text eol=lf
|
||||
*.hpp text eol=lf
|
||||
*.sh text eol=lf
|
||||
/config* text eol=lf
|
||||
configure.engine text eol=lf
|
||||
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
custom: "https://www.scummvm.org/donate-with-paypal"
|
||||
34
.github/pull_request_template.md
vendored
Normal file
34
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
<!---
|
||||
Thank you for contributing to ScummVM. Please read the following carefully before submitting your Pull Request.
|
||||
|
||||
Make sure your individual commits follow the guidelines found in the ScummVM Wiki: https://wiki.scummvm.org/index.php?title=Commit_Guidelines. If they're not please edit them before submitting the Pull Request.
|
||||
|
||||
Proper documentation must also be included for common code and changes impacting user facing elements.
|
||||
|
||||
Commits and Pull Requests should use the following template:
|
||||
|
||||
```
|
||||
SUBSYSTEM: Short (50 chars or less) summary of changes
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
two together.
|
||||
|
||||
Write your commit message in the present tense: "Fix bug" and not "Fixed
|
||||
bug." This convention matches up with commit messages generated by
|
||||
commands like git merge and git revert.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, preceded by a
|
||||
single space, with blank lines in between, but conventions vary here
|
||||
|
||||
- Use a hanging indent
|
||||
```
|
||||
--->
|
||||
146
.github/vcpkg-ports/libmpeg2/0001-Add-naive-MSVC-support-to-sources.patch
vendored
Normal file
146
.github/vcpkg-ports/libmpeg2/0001-Add-naive-MSVC-support-to-sources.patch
vendored
Normal file
@@ -0,0 +1,146 @@
|
||||
From ed3b6e4bca1fe5211e3d7ca06bbbf9b161c8bc19 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Janiszewski <janisozaur@gmail.com>
|
||||
Date: Sat, 2 Nov 2019 14:50:53 -0700
|
||||
Subject: [PATCH] Add naive MSVC support to sources
|
||||
|
||||
---
|
||||
libmpeg2/convert/rgb.c | 2 +-
|
||||
libmpeg2/cpu_accel.c | 4 ++--
|
||||
libmpeg2/cpu_state.c | 4 ++--
|
||||
libmpeg2/idct.c | 2 +-
|
||||
libmpeg2/motion_comp.c | 2 +-
|
||||
libvo/video_out_dx.c | 6 +++---
|
||||
vc++/config.h | 2 ++
|
||||
7 files changed, 12 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/libmpeg2/convert/rgb.c b/libmpeg2/convert/rgb.c
|
||||
index 8863b0b..db6f4e3 100644
|
||||
--- a/libmpeg2/convert/rgb.c
|
||||
+++ b/libmpeg2/convert/rgb.c
|
||||
@@ -499,7 +499,7 @@ static int rgb_internal (mpeg2convert_rgb_order_t order, unsigned int bpp,
|
||||
int convert420 = 0;
|
||||
int rgb_stride_min = ((bpp + 7) >> 3) * seq->width;
|
||||
|
||||
-#ifdef ARCH_X86
|
||||
+#if !defined(_MSC_VER) && defined(ARCH_X86)
|
||||
if (!copy && (accel & MPEG2_ACCEL_X86_MMXEXT)) {
|
||||
convert420 = 0;
|
||||
copy = mpeg2convert_rgb_mmxext (order, bpp, seq);
|
||||
diff --git a/libmpeg2/cpu_accel.c b/libmpeg2/cpu_accel.c
|
||||
index 9b24610..a922df1 100644
|
||||
--- a/libmpeg2/cpu_accel.c
|
||||
+++ b/libmpeg2/cpu_accel.c
|
||||
@@ -29,7 +29,7 @@
|
||||
#include "attributes.h"
|
||||
#include "mpeg2_internal.h"
|
||||
|
||||
-#if defined(ARCH_X86) || defined(ARCH_X86_64)
|
||||
+#if !defined(_MSC_VER) && (defined(ARCH_X86) || defined(ARCH_X86_64))
|
||||
static inline uint32_t arch_accel (uint32_t accel)
|
||||
{
|
||||
if (accel & (MPEG2_ACCEL_X86_3DNOW | MPEG2_ACCEL_X86_MMXEXT))
|
||||
@@ -253,7 +253,7 @@ static inline uint32_t arch_accel (uint32_t accel)
|
||||
|
||||
uint32_t mpeg2_detect_accel (uint32_t accel)
|
||||
{
|
||||
-#if defined (ARCH_X86) || defined (ARCH_X86_64) || defined (ARCH_PPC) || defined (ARCH_ALPHA) || defined (ARCH_SPARC)
|
||||
+#if !defined(_MSC_VER) && (defined (ARCH_X86) || defined (ARCH_X86_64) || defined (ARCH_PPC) || defined (ARCH_ALPHA) || defined (ARCH_SPARC))
|
||||
accel = arch_accel (accel);
|
||||
#endif
|
||||
return accel;
|
||||
diff --git a/libmpeg2/cpu_state.c b/libmpeg2/cpu_state.c
|
||||
index 2f2f64a..f4966c1 100644
|
||||
--- a/libmpeg2/cpu_state.c
|
||||
+++ b/libmpeg2/cpu_state.c
|
||||
@@ -36,7 +36,7 @@
|
||||
void (* mpeg2_cpu_state_save) (cpu_state_t * state) = NULL;
|
||||
void (* mpeg2_cpu_state_restore) (cpu_state_t * state) = NULL;
|
||||
|
||||
-#if defined(ARCH_X86) || defined(ARCH_X86_64)
|
||||
+#if !defined(_MSC_VER) && (defined(ARCH_X86) || defined(ARCH_X86_64))
|
||||
static void state_restore_mmx (cpu_state_t * state)
|
||||
{
|
||||
emms ();
|
||||
@@ -115,7 +115,7 @@ static void state_restore_altivec (cpu_state_t * state)
|
||||
|
||||
void mpeg2_cpu_state_init (uint32_t accel)
|
||||
{
|
||||
-#if defined(ARCH_X86) || defined(ARCH_X86_64)
|
||||
+#if !defined(_MSC_VER) && (defined(ARCH_X86) || defined(ARCH_X86_64))
|
||||
if (accel & MPEG2_ACCEL_X86_MMX) {
|
||||
mpeg2_cpu_state_restore = state_restore_mmx;
|
||||
}
|
||||
diff --git a/libmpeg2/idct.c b/libmpeg2/idct.c
|
||||
index 81c57e0..a057bf7 100644
|
||||
--- a/libmpeg2/idct.c
|
||||
+++ b/libmpeg2/idct.c
|
||||
@@ -235,7 +235,7 @@ static void mpeg2_idct_add_c (const int last, int16_t * block,
|
||||
|
||||
void mpeg2_idct_init (uint32_t accel)
|
||||
{
|
||||
-#ifdef ARCH_X86
|
||||
+#if !defined(_MSC_VER) && defined(ARCH_X86)
|
||||
if (accel & MPEG2_ACCEL_X86_SSE2) {
|
||||
mpeg2_idct_copy = mpeg2_idct_copy_sse2;
|
||||
mpeg2_idct_add = mpeg2_idct_add_sse2;
|
||||
diff --git a/libmpeg2/motion_comp.c b/libmpeg2/motion_comp.c
|
||||
index 7aed113..b00a32d 100644
|
||||
--- a/libmpeg2/motion_comp.c
|
||||
+++ b/libmpeg2/motion_comp.c
|
||||
@@ -33,7 +33,7 @@ mpeg2_mc_t mpeg2_mc;
|
||||
|
||||
void mpeg2_mc_init (uint32_t accel)
|
||||
{
|
||||
-#ifdef ARCH_X86
|
||||
+#if !defined(_MSC_VER) && defined(ARCH_X86)
|
||||
if (accel & MPEG2_ACCEL_X86_MMXEXT)
|
||||
mpeg2_mc = mpeg2_mc_mmxext;
|
||||
else if (accel & MPEG2_ACCEL_X86_3DNOW)
|
||||
diff --git a/libvo/video_out_dx.c b/libvo/video_out_dx.c
|
||||
index 36de68a..0797cdc 100644
|
||||
--- a/libvo/video_out_dx.c
|
||||
+++ b/libvo/video_out_dx.c
|
||||
@@ -82,7 +82,7 @@ static void update_overlay (dx_instance_t * instance)
|
||||
dwFlags, &ddofx);
|
||||
}
|
||||
|
||||
-static long FAR PASCAL event_procedure (HWND hwnd, UINT message,
|
||||
+static LRESULT FAR PASCAL event_procedure (HWND hwnd, UINT message,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
RECT rect_window;
|
||||
@@ -92,7 +92,7 @@ static long FAR PASCAL event_procedure (HWND hwnd, UINT message,
|
||||
switch (message) {
|
||||
|
||||
case WM_WINDOWPOSCHANGED:
|
||||
- instance = (dx_instance_t *) GetWindowLong (hwnd, GWL_USERDATA);
|
||||
+ instance = (dx_instance_t *) GetWindowLongPtr (hwnd, GWLP_USERDATA);
|
||||
|
||||
/* update the window position and size */
|
||||
point_window.x = 0;
|
||||
@@ -173,7 +173,7 @@ static int create_window (dx_instance_t * instance)
|
||||
/* store a directx_instance pointer into the window local storage
|
||||
* (for later use in event_handler).
|
||||
* We need to use SetWindowLongPtr when it is available in mingw */
|
||||
- SetWindowLong (instance->window, GWL_USERDATA, (LONG) instance);
|
||||
+ SetWindowLongPtr (instance->window, GWLP_USERDATA, (LONG_PTR) instance);
|
||||
|
||||
ShowWindow (instance->window, SW_SHOW);
|
||||
|
||||
diff --git a/vc++/config.h b/vc++/config.h
|
||||
index 93719f0..a03cce6 100644
|
||||
--- a/vc++/config.h
|
||||
+++ b/vc++/config.h
|
||||
@@ -16,7 +16,9 @@
|
||||
/* #undef ARCH_SPARC */
|
||||
|
||||
/* x86 architecture */
|
||||
+#if defined(_M_AMD64) || defined(_M_IX86)
|
||||
#define ARCH_X86
|
||||
+#endif
|
||||
|
||||
/* maximum supported data alignment */
|
||||
/* #undef ATTRIBUTE_ALIGNED_MAX */
|
||||
--
|
||||
2.25.0
|
||||
|
||||
108
.github/vcpkg-ports/libmpeg2/CMakeLists.txt
vendored
Normal file
108
.github/vcpkg-ports/libmpeg2/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libmpeg2)
|
||||
|
||||
option(TOOLS "Build libmpeg2 tools" OFF)
|
||||
|
||||
set(MPEG2_SOURCE_FILES
|
||||
libmpeg2/alloc.c
|
||||
libmpeg2/cpu_accel.c
|
||||
libmpeg2/cpu_state.c
|
||||
libmpeg2/decode.c
|
||||
libmpeg2/header.c
|
||||
libmpeg2/idct.c
|
||||
libmpeg2/idct_alpha.c
|
||||
libmpeg2/idct_altivec.c
|
||||
#libmpeg2/idct_mmx.c
|
||||
libmpeg2/motion_comp.c
|
||||
libmpeg2/motion_comp_alpha.c
|
||||
libmpeg2/motion_comp_altivec.c
|
||||
libmpeg2/motion_comp_arm.c
|
||||
#libmpeg2/motion_comp_mmx.c
|
||||
libmpeg2/motion_comp_vis.c
|
||||
libmpeg2/slice.c
|
||||
)
|
||||
set(VO_SOURCE_FILES
|
||||
libvo/video_out.c
|
||||
libvo/video_out_dx.c
|
||||
libvo/video_out_null.c
|
||||
libvo/video_out_pgm.c
|
||||
libvo/video_out_sdl.c
|
||||
libvo/video_out_x11.c
|
||||
)
|
||||
set(MPEG2_CONVERT_SOURCES
|
||||
libmpeg2/convert/rgb.c
|
||||
#libmpeg2/convert/rgb_mmx.c
|
||||
libmpeg2/convert/rgb_vis.c
|
||||
libmpeg2/convert/uyvy.c
|
||||
)
|
||||
set(GETOPT_FILES
|
||||
src/getopt.c
|
||||
)
|
||||
set(HEADERS
|
||||
include/mpeg2.h
|
||||
include/mpeg2convert.h
|
||||
)
|
||||
|
||||
add_library(mpeg2 ${MPEG2_SOURCE_FILES})
|
||||
add_library(mpeg2convert ${MPEG2_CONVERT_SOURCES})
|
||||
add_library(getopt STATIC ${GETOPT_FILES})
|
||||
add_library(vo STATIC ${VO_SOURCE_FILES})
|
||||
|
||||
target_include_directories(mpeg2convert PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
target_include_directories(getopt PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
target_include_directories(vo PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
target_include_directories(mpeg2 PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
target_include_directories(mpeg2 INTERFACE
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
)
|
||||
|
||||
target_compile_definitions(getopt PUBLIC HAVE_CONFIG_H)
|
||||
target_link_libraries(vo mpeg2convert)
|
||||
|
||||
if (TOOLS)
|
||||
add_executable(mpeg2dec src/mpeg2dec.c src/dump_state.c src/gettimeofday.c)
|
||||
add_executable(extract_mpeg2 src/extract_mpeg2.c)
|
||||
add_executable(corrupt_mpeg2 src/corrupt_mpeg2.c)
|
||||
|
||||
target_compile_definitions(extract_mpeg2 PUBLIC HAVE_CONFIG_H)
|
||||
target_compile_definitions(corrupt_mpeg2 PUBLIC HAVE_CONFIG_H)
|
||||
|
||||
target_link_libraries(mpeg2dec PRIVATE getopt vo mpeg2convert mpeg2 gdi32)
|
||||
target_link_libraries(extract_mpeg2 PRIVATE getopt)
|
||||
target_link_libraries(corrupt_mpeg2 PRIVATE getopt)
|
||||
|
||||
target_include_directories(mpeg2dec PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
)
|
||||
target_include_directories(extract_mpeg2 PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
)
|
||||
target_include_directories(corrupt_mpeg2 PUBLIC
|
||||
"${CMAKE_SOURCE_DIR}/vc++"
|
||||
"${CMAKE_SOURCE_DIR}/include"
|
||||
"${CMAKE_SOURCE_DIR}/src"
|
||||
)
|
||||
endif (TOOLS)
|
||||
|
||||
install(TARGETS mpeg2
|
||||
EXPORT libmpeg2
|
||||
LIBRARY DESTINATION lib
|
||||
)
|
||||
|
||||
install(FILES ${HEADERS} DESTINATION "include/mpeg2dec")
|
||||
31
.github/vcpkg-ports/libmpeg2/portfile.cmake
vendored
Normal file
31
.github/vcpkg-ports/libmpeg2/portfile.cmake
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
vcpkg_check_linkage(ONLY_STATIC_LIBRARY)
|
||||
|
||||
# There is archived version of releases available at https://github.com/janisozaur/libmpeg2
|
||||
vcpkg_from_git(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
URL https://code.videolan.org/videolan/libmpeg2.git
|
||||
REF 946bf4b518aacc224f845e73708f99e394744499 # Use a pinned commit hash
|
||||
PATCHES
|
||||
0001-Add-naive-MSVC-support-to-sources.patch
|
||||
)
|
||||
|
||||
file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${SOURCE_PATH}")
|
||||
|
||||
vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS
|
||||
FEATURES
|
||||
tools TOOLS
|
||||
)
|
||||
|
||||
vcpkg_cmake_configure(
|
||||
SOURCE_PATH "${SOURCE_PATH}"
|
||||
OPTIONS ${FEATURE_OPTIONS}
|
||||
)
|
||||
|
||||
vcpkg_cmake_install()
|
||||
|
||||
# # Handle copyright
|
||||
file(INSTALL "${SOURCE_PATH}/COPYING" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}" RENAME copyright)
|
||||
|
||||
file(REMOVE_RECURSE "${CURRENT_PACKAGES_DIR}/debug/include")
|
||||
|
||||
vcpkg_copy_pdbs()
|
||||
19
.github/vcpkg-ports/libmpeg2/vcpkg.json
vendored
Normal file
19
.github/vcpkg-ports/libmpeg2/vcpkg.json
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "libmpeg2",
|
||||
"version": "0.5.1",
|
||||
"port-version": 3,
|
||||
"description": "a free MPEG-2 video stream decoder",
|
||||
"homepage": "http://libmpeg2.sourceforge.net/",
|
||||
"supports": "!(linux | osx | uwp)",
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "vcpkg-cmake",
|
||||
"host": true
|
||||
}
|
||||
],
|
||||
"features": {
|
||||
"tools": {
|
||||
"description": "Build tools provided with libmpeg2"
|
||||
}
|
||||
}
|
||||
}
|
||||
259
.github/workflows/ci.yml
vendored
Normal file
259
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,259 @@
|
||||
name: CI
|
||||
on: [push, pull_request]
|
||||
# schedule:
|
||||
# - cron: '0 0-23/4 * * *'
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
emscripten:
|
||||
name: Emscripten
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: emscripten
|
||||
configFlags: --enable-gif --enable-jpeg --enable-ogg --enable-png --enable-vorbis --enable-zlib --enable-freetype2
|
||||
- name: emscripten (extra libs)
|
||||
configFlags: --enable-gif --enable-jpeg --enable-ogg --enable-png --enable-vorbis --enable-zlib --enable-freetype2 --enable-a52 --enable-faad --enable-fluidlite --enable-fribidi --enable-mad --enable-mpcdec --enable-mpeg2 --enable-mpeg2 --enable-mikmod --enable-retrowave --enable-theoradec --enable-vpx
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build cache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.platform }}
|
||||
max-size: 1G
|
||||
create-symlink: true
|
||||
- name: Restore libs cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
key: ${{ matrix.configFlags }}
|
||||
path: |
|
||||
dists/emscripten/libs/
|
||||
- name: Call configure
|
||||
run: |
|
||||
CXX='ccache emcc' dists/emscripten/build.sh configure --enable-all-engines ${{ matrix.configFlags }}
|
||||
- name: Save libs cache
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
key: ${{ matrix.configFlags }}
|
||||
path: |
|
||||
dists/emscripten/libs/
|
||||
- name: Build scummvm
|
||||
run: |
|
||||
dists/emscripten/build.sh make
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: win32
|
||||
triplet: x86-windows
|
||||
arch: x86
|
||||
configFlags: --enable-discord --enable-faad --enable-gif --enable-mikmod --enable-mpeg2 --enable-vpx
|
||||
useNasm: 'true'
|
||||
- platform: x64
|
||||
arch: x64
|
||||
triplet: x64-windows
|
||||
configFlags: --enable-discord --enable-faad --enable-gif --enable-mikmod --enable-mpeg2 --enable-vpx
|
||||
- platform: arm64
|
||||
arch: arm64
|
||||
triplet: arm64-windows
|
||||
configFlags: --enable-discord --enable-faad --enable-gif --enable-mikmod --enable-mpeg2 --enable-vpx
|
||||
env:
|
||||
CONFIGURATION: Debug
|
||||
PLATFORM: ${{ matrix.platform }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VCPKG_FEATURE_FLAGS: dependencygraph
|
||||
VCPKG_DEFAULT_TRIPLET: ${{ matrix.triplet }}
|
||||
VCPKG_INSTALLED_DIR: ${{ github.workspace }}/vcpkg_installed
|
||||
VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/vcpkg_cache,readwrite
|
||||
VCPKG_OVERLAY_PORTS: ${{ github.workspace }}/.github/vcpkg-ports
|
||||
GIT_VCPKG_COMMIT: ef7dbf94b9198bc58f45951adcf1f041fcbc5ea0
|
||||
permissions:
|
||||
contents: write # For dependencygraph
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Setup nasm
|
||||
uses: ilammy/setup-nasm@v1
|
||||
if: matrix.useNasm == 'true'
|
||||
- name: Install vcpkg
|
||||
uses: lukka/run-vcpkg@v11
|
||||
id: runvcpkg
|
||||
with:
|
||||
vcpkgGitCommitId: ${{ env.GIT_VCPKG_COMMIT }}
|
||||
- name: Integrate vcpkg
|
||||
run: |
|
||||
${{ steps.runvcpkg.outputs.RUNVCPKG_VCPKG_ROOT_OUT }}/vcpkg integrate install
|
||||
- name: Restore vcpkg cache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/vcpkg_cache
|
||||
key: vcpkg-${{ env.GIT_VCPKG_COMMIT }}-${{ matrix.triplet }}-${{ hashFiles('vcpkg.json', 'vcpkg_installed/compiler-file-hash-cache.json', 'vcpkg_installed/status') }}
|
||||
restore-keys: vcpkg-${{ env.GIT_VCPKG_COMMIT }}-${{ matrix.triplet }}-
|
||||
- name: Build create_project
|
||||
run: |
|
||||
cd devtools/create_project/cmake
|
||||
cmake .
|
||||
cmake --build . -j 2
|
||||
ls
|
||||
cd ../../../
|
||||
- name: Call create_project
|
||||
run: |
|
||||
mkdir build-scummvm
|
||||
cd build-scummvm
|
||||
../devtools/create_project/cmake/Debug/create_project.exe .. --msvc --vcpkg --enable-all-engines ${{ matrix.configFlags }}
|
||||
ls
|
||||
- name: Add msbuild to PATH
|
||||
uses: microsoft/setup-msbuild@v2
|
||||
- name: Install vcpkg packages
|
||||
run: |
|
||||
vcpkg install
|
||||
- name: Save vcpkg cache
|
||||
uses: actions/cache/save@v4
|
||||
with:
|
||||
path: ${{ github.workspace }}/vcpkg_cache
|
||||
key: vcpkg-${{ env.GIT_VCPKG_COMMIT }}-${{ matrix.triplet }}-${{ hashFiles('vcpkg.json', 'vcpkg_installed/compiler-file-hash-cache.json', 'vcpkg_installed/status') }}
|
||||
- name: Build scummvm
|
||||
run: |
|
||||
cd build-scummvm
|
||||
ls
|
||||
msbuild scummvm.sln /m /p:VcpkgEnableManifest=true /p:BuildInParallel=true /p:Configuration=${{ env.CONFIGURATION }} /p:PreferredToolArchitecture=x64 /p:Platform=${{ matrix.platform }} /v:minimal
|
||||
- name: Upload scummvm
|
||||
uses: actions/upload-artifact@v4
|
||||
if: matrix.buildArtifacts == 'true'
|
||||
with:
|
||||
name: scummvm-${{ matrix.arch }}
|
||||
path: build-scummvm/${{ env.CONFIGURATION }}${{ matrix.arch }}/*.exe
|
||||
- name: Upload scummvm libs
|
||||
uses: actions/upload-artifact@v4
|
||||
if: matrix.buildArtifacts == 'true'
|
||||
with:
|
||||
name: libs-${{ matrix.arch }}
|
||||
path: ${{ env.VCPKG_INSTALLED_DIR }}\\${{ matrix.triplet }}\\bin\\*.dll
|
||||
- name: Upload scummvm symbols
|
||||
uses: actions/upload-artifact@v4
|
||||
if: matrix.buildArtifacts == 'true' && env.CONFIGURATION == 'Debug'
|
||||
with:
|
||||
name: symbols-${{ matrix.arch }}
|
||||
path: build-scummvm/${{ env.CONFIGURATION }}${{ matrix.arch }}/*.pdb
|
||||
- name: Upload scummvm libs symbols
|
||||
uses: actions/upload-artifact@v4
|
||||
if: matrix.buildArtifacts == 'true' && env.CONFIGURATION == 'Debug'
|
||||
with:
|
||||
name: lib-symbols-${{ matrix.arch }}
|
||||
path: ${{ env.VCPKG_INSTALLED_DIR }}\\${{ matrix.triplet }}\\bin\\*.pdb
|
||||
xcode:
|
||||
name: Xcode
|
||||
runs-on: macos-15-intel
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: macosx
|
||||
buildFlags: -scheme ScummVM-macOS -destination 'platform=macOS,arch=x86_64'
|
||||
configFlags: --enable-faad --enable-gif --enable-mikmod --enable-mpeg2 --enable-vpx
|
||||
brewPackages: a52dec faad2 flac fluid-synth freetype fribidi giflib jpeg mad libmikmod libmpeg2 libogg libpng libvorbis libvpx sdl2 sdl2_net theora
|
||||
- platform: ios7
|
||||
buildFlags: -scheme ScummVM-iOS CODE_SIGN_IDENTITY="" CODE_SIGNING_ALLOWED=NO
|
||||
configFlags: --ios --use-xcframework --enable-faad --enable-gif --enable-mikmod --enable-vpx --enable-mpc --enable-a52 --disable-taskbar --disable-tts
|
||||
packagesUrl: https://downloads.scummvm.org/frs/build/scummvm-ios7-libs-v4.zip
|
||||
defaults:
|
||||
run:
|
||||
# Must be explicit for proper pipefail support
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Install packages
|
||||
if: ${{ matrix.brewPackages }}
|
||||
run: brew install ${{ matrix.brewPackages }}
|
||||
- name: Download libraries
|
||||
if: ${{ matrix.packagesUrl }}
|
||||
run: |
|
||||
curl -L -o libs.zip ${{ matrix.packagesUrl }}
|
||||
unzip libs.zip
|
||||
ls
|
||||
- name: Build create_project
|
||||
run: |
|
||||
cd devtools/create_project/xcode
|
||||
xcodebuild
|
||||
ls
|
||||
cd ../../../
|
||||
- name: Call create_project
|
||||
run: |
|
||||
./devtools/create_project/xcode/build/Release/create_project . --xcode --enable-all-engines ${{ matrix.configFlags }}
|
||||
ls
|
||||
- name: Build cache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.platform }}
|
||||
max-size: 1G
|
||||
create-symlink: true
|
||||
- name: Build scummvm
|
||||
run: |
|
||||
xcodebuild CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ build -project scummvm.xcodeproj ${{ matrix.buildFlags }} | awk '$1 !~ /^(export|cd|clang++)/'
|
||||
ls
|
||||
ubuntu:
|
||||
name: Ubuntu
|
||||
runs-on: ${{ matrix.platform }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- platform: ubuntu-latest
|
||||
sdlConfig: sdl2-config
|
||||
cxx: ccache g++
|
||||
aptPackages: 'liba52-dev libcurl4-openssl-dev libfaad-dev libflac-dev libfluidsynth-dev libfreetype6-dev libfribidi-dev libgif-dev libgtk-3-dev libjpeg-turbo8-dev libmad0-dev libmikmod-dev libmpeg2-4-dev libogg-dev libpng-dev libsdl2-dev libsdl2-net-dev libsndio-dev libspeechd-dev libtheora-dev libunity-dev libvorbis-dev libvpx-dev zlib1g-dev'
|
||||
configFlags: --enable-discord --with-discord-prefix=/usr/local
|
||||
- platform: ubuntu-22.04
|
||||
sdlConfig: sdl-config
|
||||
cxx: ccache g++-4.8
|
||||
aptPackages: 'g++-4.8 liba52-dev libcurl4-openssl-dev libfaad-dev libflac-dev libfluidsynth-dev libfreetype6-dev libfribidi-dev libgif-dev libgtk-3-dev libjpeg-turbo8-dev libmad0-dev libmikmod-dev libmpeg2-4-dev libogg-dev libpng-dev libsdl-net1.2-dev libsdl1.2-dev libsndio-dev libspeechd-dev libtheora-dev libunity-dev libvorbis-dev libvpx-dev zlib1g-dev'
|
||||
configFlags: --enable-discord --with-discord-prefix=/usr/local
|
||||
env:
|
||||
SDL_CONFIG: ${{ matrix.sdlConfig }}
|
||||
defaults:
|
||||
run:
|
||||
# Must be explicit for proper pipefail support
|
||||
shell: bash
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: Add Ubuntu Xenial package sources
|
||||
if: matrix.platform == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 40976EAF437D05B5
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-key 3B4FE6ACC0B21F32
|
||||
sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ xenial main'
|
||||
sudo apt-add-repository 'deb http://azure.archive.ubuntu.com/ubuntu/ xenial universe'
|
||||
- name: Install packages
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install ${{ matrix.aptPackages }}
|
||||
- name: Download and install Discord RPC libraries
|
||||
run: |
|
||||
curl -L -o discord-rpc-linux.zip https://github.com/discord/discord-rpc/releases/download/v3.4.0/discord-rpc-linux.zip
|
||||
echo 'dac1f5dc6bedaeab1cc3c2c7fd4261e00838c81619c3ee325f3723c3d55ee03a discord-rpc-linux.zip' | sha256sum --check && unzip discord-rpc-linux.zip
|
||||
sudo cp -v -pR discord-rpc/linux-dynamic/include/*.* /usr/local/include/
|
||||
sudo cp -v -pR discord-rpc/linux-dynamic/lib/*.* /usr/local/lib/
|
||||
- name: ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
key: ${{ matrix.platform }}
|
||||
max-size: 1G
|
||||
- name: Call configure
|
||||
run: |
|
||||
CXX='${{ matrix.cxx }}' ./configure --enable-all-engines ${{ matrix.configFlags }}
|
||||
- name: Build scummvm
|
||||
run: |
|
||||
make -j2
|
||||
- name: Build tests
|
||||
run: |
|
||||
make test
|
||||
- name: Build devtools
|
||||
run: |
|
||||
make devtools
|
||||
307
.gitignore
vendored
Normal file
307
.gitignore
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
|
||||
*~
|
||||
.*.swp
|
||||
.*.swo
|
||||
*.o
|
||||
*.o.tmp
|
||||
*.dwo
|
||||
*.dwp
|
||||
lib*.a
|
||||
.deps
|
||||
*_pch.cpp
|
||||
|
||||
/config.log
|
||||
/scummvm
|
||||
/libscummvm.so
|
||||
/scummvm-static
|
||||
/ScummVMDockTilePlugin*
|
||||
/config.h
|
||||
/config.mk
|
||||
/configure.stamp
|
||||
/.gdb_history
|
||||
/dumps
|
||||
/Credits.rtf
|
||||
/*.mshark
|
||||
/*.dSYM
|
||||
/MT32_CONTROL.ROM
|
||||
/MT32_PCM.ROM
|
||||
/ScummVM.app
|
||||
/scummvm.docktileplugin
|
||||
/\!ScummVM*
|
||||
/*,e1f
|
||||
/scummvm-ps3.pkg
|
||||
/*.ipk
|
||||
/map.txt
|
||||
*.elf
|
||||
*.nds
|
||||
/*.3dsx
|
||||
/*.cia
|
||||
/*.smdh
|
||||
/*.bnr
|
||||
/romfs
|
||||
/dist_3ds
|
||||
/dist-generic
|
||||
/.project
|
||||
/.cproject
|
||||
/.settings
|
||||
/.autotools
|
||||
/Icon.*
|
||||
/scummvm-conf.cpp
|
||||
/tmp_*.cpp
|
||||
/*.apk
|
||||
/CMakeLists.txt
|
||||
|
||||
/README
|
||||
/README.html*
|
||||
/NEWS
|
||||
/NEWS.html
|
||||
/CONTRIBUTING
|
||||
/CONTRIBUTING.html
|
||||
/doc/de/NEUES
|
||||
/doc/de/NEUES.html
|
||||
/doc/docportal/_build
|
||||
/ScummVM Manual*.pdf
|
||||
|
||||
/build*
|
||||
/staging
|
||||
/portdist
|
||||
|
||||
/backends/platform/3ds/shader.shbin
|
||||
/backends/platform/3ds/shader_shbin.h
|
||||
|
||||
/backends/platform/dc/gui
|
||||
/backends/platform/dc/graphics
|
||||
/backends/platform/dc/sound
|
||||
/backends/platform/dc/common
|
||||
/backends/platform/dc/base
|
||||
/backends/platform/dc/backends
|
||||
/backends/platform/dc/tools
|
||||
/backends/platform/dc/plugins
|
||||
/backends/platform/dc/engines
|
||||
/backends/platform/dc/scummvm.elf
|
||||
/backends/platform/dc/scummvm.bin
|
||||
/backends/platform/dc/SCUMMVM.BIN
|
||||
/backends/platform/dc/*.PLG
|
||||
|
||||
/backends/platform/ds/gfx/*.s
|
||||
/backends/platform/ds/gfx/*.h
|
||||
|
||||
/backends/platform/maemo/scummvm
|
||||
|
||||
/dists/rpl.exe
|
||||
|
||||
/dists/codeblocks/*.cbp
|
||||
/dists/codeblocks/*.depend
|
||||
/dists/codeblocks/*.layout
|
||||
/dists/codeblocks/engines/
|
||||
/dists/codeblocks/scummvm*
|
||||
|
||||
/doc/doxygen/html
|
||||
/doc/doxygen/doxygen_warnings.txt
|
||||
|
||||
#Ignore XCode user data and build files
|
||||
xcuserdata
|
||||
project.xcworkspace
|
||||
/dists/macosx/build
|
||||
/dists/macosx/scummvm.xcodeproj
|
||||
/dists/macosx/create_project
|
||||
/dists/ios7/create_project
|
||||
/devtools/create_project/xcode/build
|
||||
|
||||
/dists/msvc/[Dd]ebug*/
|
||||
/dists/msvc/[Rr]elease*/
|
||||
/dists/msvc/[Aa]nalysis*/
|
||||
/dists/msvc/*.lib
|
||||
/dists/msvc/*.SAV
|
||||
/dists/msvc/*.dat
|
||||
/dists/msvc/*.dll
|
||||
/dists/msvc/test_runner.cpp
|
||||
|
||||
/dists/engine-data/testbed-audiocd-files/testbed.config
|
||||
/dists/engine-data/testbed-audiocd-files/testbed.out
|
||||
/dists/engine-data/playground3d
|
||||
|
||||
/dists/scummvm_rc_*.rh
|
||||
|
||||
|
||||
/doc/*.aux
|
||||
/doc/*.dvi
|
||||
/doc/*.log
|
||||
/doc/*.out
|
||||
/doc/*.pdf
|
||||
/doc/*.ps
|
||||
/doc/*.toc
|
||||
|
||||
/plugins
|
||||
|
||||
/engines/detection_table.h
|
||||
/engines/plugins_table.h
|
||||
/engines/engines.mk
|
||||
|
||||
/test/runner
|
||||
/test/runner.cpp
|
||||
/test/*.dSYM
|
||||
|
||||
/devtools/convbdf
|
||||
/devtools/convbdf.dSYM
|
||||
/devtools/md5table
|
||||
/devtools/md5table.dSYM
|
||||
/devtools/make-scumm-fontdata
|
||||
/devtools/make-scumm-fontdata.dSYM
|
||||
/devtools/create_access/create_access
|
||||
/devtools/create_engine/create_engine
|
||||
/devtools/create_cryo/create_cryo_dat
|
||||
/devtools/create_cryomni3d/create_cryomni3d_dat
|
||||
/devtools/create_drascula/create_drascula
|
||||
/devtools/create_glk/create_glk
|
||||
/devtools/create_hugo/create_hugo
|
||||
/devtools/create_kyradat/create_kyradat
|
||||
/devtools/create_lure/create_lure
|
||||
/devtools/create_mm/create_xeen/create_mm
|
||||
/devtools/create_mm/create_xeen/create_xeen
|
||||
/devtools/create_mortdat/create_mortdat
|
||||
/devtools/create_myst3/create_myst3
|
||||
/devtools/create_nancy/create_nancy
|
||||
/devtools/create_neverhood/create_neverhood
|
||||
/devtools/create_project/create_project
|
||||
/devtools/create_supernova/create_supernova
|
||||
/devtools/create_supernova/create_image/create_image
|
||||
/devtools/create_teenagent/create_teenagent
|
||||
/devtools/create_titanic/create_titanic
|
||||
/devtools/create_tony/create_tony
|
||||
/devtools/create_toon/create_toon
|
||||
/devtools/create_translations/create_translations
|
||||
/devtools/create_ultima/create_ultima
|
||||
/devtools/extract_mort/extract_mort
|
||||
/devtools/qtable/qtable
|
||||
/devtools/skycpt/skycpt
|
||||
|
||||
/snapshots
|
||||
|
||||
#Ignore thumbnails created by Windows
|
||||
Thumbs.db
|
||||
#Ignore files build by Visual Studio
|
||||
*.obj
|
||||
*.exe
|
||||
*.pdb
|
||||
*.user
|
||||
*.aps
|
||||
*.pch
|
||||
*.vspscc
|
||||
*.ncb
|
||||
*.suo
|
||||
*.tlb
|
||||
*.tlh
|
||||
*.bak
|
||||
*.cache
|
||||
*.ilk
|
||||
*.log
|
||||
*.sbr
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.opendb
|
||||
obj/
|
||||
_ReSharper*/
|
||||
ipch/
|
||||
[Tt]est[Rr]esult*
|
||||
*.vcproj
|
||||
*.sln
|
||||
*.vsprops
|
||||
*.props
|
||||
*.vcxproj*
|
||||
*.bat
|
||||
*.tss
|
||||
*.VC.db
|
||||
.vs/
|
||||
UpgradeLog.htm
|
||||
|
||||
#Ignore default Visual Studio build folders
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
[Dd]ebug32/
|
||||
[Rr]elease32/
|
||||
[Dd]ebug64/
|
||||
[Rr]elease64/
|
||||
LLVM32/
|
||||
LLVM64/
|
||||
|
||||
#Ignore files created by the Win32 distribution target
|
||||
win32dist-mingw/
|
||||
|
||||
#Ignore files generated by Visual Studio Code
|
||||
.vscode/
|
||||
compile_commands.json
|
||||
|
||||
#Ignore gettext generated files
|
||||
/messages.mo
|
||||
|
||||
#Ignore Qt Creator project files
|
||||
ScummVM.config
|
||||
ScummVM.creator
|
||||
ScummVM.files
|
||||
ScummVM.includes
|
||||
ScummVM.cflags
|
||||
ScummVM.cxxflags
|
||||
|
||||
#Ignore Komodo IDE/Edit project files
|
||||
*.komodoproject
|
||||
|
||||
#Ignore Mac DS_Store files
|
||||
.DS_Store
|
||||
|
||||
#Ignore MS Visual C++ temporary files/subdirectories (except create_project.bat)
|
||||
dists/msvc/**
|
||||
!dists/msvc/create_project.bat
|
||||
|
||||
#Ignore bison debug output
|
||||
*.output
|
||||
|
||||
#Ignore CMake build files
|
||||
CMakeFiles
|
||||
CMakeCache.txt
|
||||
cmake_install.cmake
|
||||
.ninja_*
|
||||
*.ninja
|
||||
|
||||
#Ignore Xcode output/project files
|
||||
out/
|
||||
/*.xcodeproj
|
||||
|
||||
#Ignore PSP2 files
|
||||
psp2pkg/
|
||||
*.velf
|
||||
*.vpk
|
||||
|
||||
#Ignore Switch files
|
||||
switch_release/
|
||||
scummvm.elf
|
||||
scummvm.nro
|
||||
scummvm_switch.zip
|
||||
|
||||
#Ignore gmon.out created by gprof
|
||||
gmon.out
|
||||
/scummvm_libs_2015
|
||||
|
||||
#Ignore Eclipse related files (such as a Java plugin for Visual Studio Code)
|
||||
.settings
|
||||
.project
|
||||
|
||||
#Ignore Android Studio files
|
||||
.idea
|
||||
|
||||
#Ignore temporary Android project folder
|
||||
android_project
|
||||
|
||||
#Ignore snapcraft build artifacts
|
||||
.snap
|
||||
|
||||
#Ignore emscripten build artifacts
|
||||
dists/emscripten/libs/
|
||||
dists/emscripten/games/
|
||||
dists/emscripten/emsdk-*
|
||||
|
||||
#Ignore Atari/FreeMiNT files
|
||||
scummvm.prg
|
||||
|
||||
#Ignore Python generated files
|
||||
__pycache__/
|
||||
4
.gitlab-ci.yml
Normal file
4
.gitlab-ci.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
|
||||
|
||||
include:
|
||||
- 'backends/platform/libretro/.gitlab-ci.yml'
|
||||
18
.pre-commit-config.yaml
Normal file
18
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v6.0.0
|
||||
hooks:
|
||||
- id: check-yaml
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v21.1.2
|
||||
hooks:
|
||||
- id: clang-format
|
||||
- repo: local
|
||||
hooks:
|
||||
- id: check-commit-msg
|
||||
name: check-commit-msg
|
||||
language: python
|
||||
entry: devtools/check-commit-msg.py
|
||||
stages: [ commit-msg ]
|
||||
16
.readthedocs.yaml
Normal file
16
.readthedocs.yaml
Normal file
@@ -0,0 +1,16 @@
|
||||
version: 2
|
||||
|
||||
build:
|
||||
os: ubuntu-22.04
|
||||
tools:
|
||||
python: "3"
|
||||
|
||||
sphinx:
|
||||
configuration: doc/docportal/conf.py
|
||||
|
||||
python:
|
||||
install:
|
||||
- requirements: doc/docportal/requirements.txt
|
||||
|
||||
formats:
|
||||
- pdf
|
||||
12
CONTRIBUTING.md
Normal file
12
CONTRIBUTING.md
Normal file
@@ -0,0 +1,12 @@
|
||||
Thank you for considering contributing to ScummVM.
|
||||
|
||||
Please make sure to read our guidelines for contributions on our
|
||||
[wiki](https://wiki.scummvm.org/index.php/Developer_Central). In particular:
|
||||
|
||||
* [Coding style](https://wiki.scummvm.org/index.php/Code_Formatting_Conventions)
|
||||
* [Portability](https://wiki.scummvm.org/index.php/Coding_Conventions)
|
||||
* [Commit message style](https://wiki.scummvm.org/index.php/Commit_Guidelines)
|
||||
* License: GPLv3+
|
||||
|
||||
If you have any questions about code, style, procedure, or anything else, feel
|
||||
free to contact us on our mailing list at scummvm-devel@lists.scummvm.org.
|
||||
674
COPYING
Normal file
674
COPYING
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
||||
966
COPYRIGHT
Normal file
966
COPYRIGHT
Normal file
@@ -0,0 +1,966 @@
|
||||
ScummVM
|
||||
Copyright (C) 2001-2026 by the following:
|
||||
|
||||
If you have contributed to this project then you deserve to be on this
|
||||
list. Contact us (see: AUTHORS) and we'll add you.
|
||||
|
||||
Daniel Albano
|
||||
Manuel Alfayate
|
||||
Thomas Allen
|
||||
Tore Anderson
|
||||
Tor Andersson
|
||||
Torbjörn Andersson
|
||||
Thanasis Antoniou
|
||||
Chris Apers
|
||||
Joseph Applegate
|
||||
Francesco Ariis
|
||||
Adrian Astley
|
||||
Bertrand Augereau
|
||||
Ori Avtalion
|
||||
Nicolas Bacca
|
||||
Scott Baker
|
||||
Daniel Balsom
|
||||
Matan Bareket
|
||||
Yotam Barnoy
|
||||
Fabio Battaglia
|
||||
Chris Benshoof
|
||||
Alex Bevilacqua
|
||||
Gunnar Birke
|
||||
Robert Biro
|
||||
Arthur Blot
|
||||
Laurent Blume
|
||||
Andrea Boscarino
|
||||
Bastien Bouclet
|
||||
Arnaud Boutonné
|
||||
Peter Bozsó
|
||||
Jurgen Braam
|
||||
Ralph Brorsen
|
||||
James Brown
|
||||
Patrick Burke
|
||||
Henry Bush
|
||||
Vincent Bénony
|
||||
Giulio Camuffo
|
||||
David Cardwell
|
||||
Ray Carro
|
||||
Cameron Cawley
|
||||
Kaloyan Chehlarski
|
||||
Jamieson Christian
|
||||
Fabien Coeurjoly
|
||||
Stefano Collavini
|
||||
Marcus Comstedt
|
||||
Andrea Corna
|
||||
David Corrales-Lopez
|
||||
Alan Cox
|
||||
Thierry Crozat
|
||||
Patrik Dahlström
|
||||
Sunit Das
|
||||
Joseph Davies
|
||||
Bartosz Dudziak
|
||||
Frantisek Dufka
|
||||
Matthew Duggan
|
||||
Sylvain Dupont
|
||||
Jonathan E. Wright
|
||||
Joachim Eberhard
|
||||
Ángel Eduardo García Hernández
|
||||
Thomas Edvalson
|
||||
Oystein Eftevaag
|
||||
David Eriksson
|
||||
Thomas Fach-Pedersen
|
||||
Michael Fink
|
||||
David Fioramonti
|
||||
Jerome Fisher
|
||||
Alex Fontoura
|
||||
Alexandre Fontoura
|
||||
Hans-Jörg Frieden
|
||||
Greg Frieger
|
||||
Eric Fry
|
||||
Matthew Gambrell
|
||||
Nipun Garg
|
||||
Nathanael Gentry
|
||||
Stuart George
|
||||
Martin Gerhardy
|
||||
Janet Gilbert
|
||||
Paul Gilbert
|
||||
David Goldsmith
|
||||
Chris Gray
|
||||
Jonathan Gray
|
||||
Gustavo Grieco
|
||||
Yuri Guimaraes
|
||||
Tobias Gunkel
|
||||
Robert Göffringmann
|
||||
Azamat H. Hackimov
|
||||
Benjamin Haisch
|
||||
Vincent Hamm
|
||||
Rüdiger Hanke
|
||||
Tobias Hansen
|
||||
Dries Harnie
|
||||
Toël Hartmann
|
||||
Andre Heider
|
||||
Sven Hesse
|
||||
Steven Hoefel
|
||||
Jochen Hoenicke
|
||||
Matthew Hoops
|
||||
Erich Hoover
|
||||
Max Horn
|
||||
Travis Howell
|
||||
Cadi Howley
|
||||
Marius Ioan Orban
|
||||
Dmitry Iskrich
|
||||
Michael J. Roberts
|
||||
Felix Jakschitsch
|
||||
Willem Jan Palenstijn
|
||||
Joseph Jezak
|
||||
Matthew Jimenez
|
||||
Einar Johan T. Sømåen
|
||||
Stefan Jokisch
|
||||
Chris Jones
|
||||
Guillem Jover
|
||||
Florian Kagerer
|
||||
Filippos Karapetis
|
||||
Denis Kasak
|
||||
Robert Kelsen
|
||||
Ismail Khatib
|
||||
Oliver Kiehl
|
||||
Martin Kiewitz
|
||||
Peter Kohaut
|
||||
Paweł Kołodziejski
|
||||
Mutwin Kraus
|
||||
Christian Krause
|
||||
Andrew Kurushin
|
||||
Christian Kündig
|
||||
Kyuubu
|
||||
Eric Lasota
|
||||
Joe Lee
|
||||
Angus Lees
|
||||
Max Lingua
|
||||
Lubomyr Lisen
|
||||
Borja Lorente
|
||||
Pieter Luteijn
|
||||
Awad Mackie
|
||||
George Macon
|
||||
Hubert Maier
|
||||
Malignant Manor
|
||||
Vicent Marti
|
||||
Josh Matthews
|
||||
Sam Matthews
|
||||
Avijeet Maurya
|
||||
Steve McCrea
|
||||
Robert Megone
|
||||
Vladimir Menshakov
|
||||
Matthieu Milan
|
||||
Alyssa Milburn
|
||||
Neil Millstone
|
||||
Ivan Mogilko
|
||||
Gregory Montoir
|
||||
Cristian Morales Vega
|
||||
Peter Moraliyski
|
||||
Stefano Musumeci
|
||||
Bendegúz Nagy
|
||||
Kostas Nakos
|
||||
Christian Neumair
|
||||
Jeremy Newman
|
||||
Juha Niemimäki
|
||||
Markus Niemistö
|
||||
Ryan Nunn
|
||||
Ryan O'Connor
|
||||
Per Olav Flaten
|
||||
Chris Page
|
||||
Alexander Panov
|
||||
Stefan Parviainen
|
||||
Vincent Pelletier
|
||||
Benjamin Penney
|
||||
Scott Percival
|
||||
Lars Persson
|
||||
Joost Peters
|
||||
Benoit Pierre
|
||||
Andrew Plotkin
|
||||
Steven Poulton
|
||||
Andrei Prykhodko
|
||||
Raina
|
||||
Coen Rampen
|
||||
Dominik Reichardt
|
||||
Alexander Reim
|
||||
Klaus Reimer
|
||||
Miroslav Remák
|
||||
Ľubomír Remák
|
||||
Retro-Junk
|
||||
Michael Rittenhouse
|
||||
Bernhard Rosenkraenzer
|
||||
Edward Rudd
|
||||
Kari Salminen
|
||||
Felipe Sanches
|
||||
Eugene Sandulenko
|
||||
Daniel Schepler
|
||||
Dominik Scherer
|
||||
Johannes Schickel
|
||||
Jochen Schleu
|
||||
Luc Schrijvers
|
||||
Keith Scroggins
|
||||
Martin Sedlak
|
||||
Vladimir Serbinenko/Google
|
||||
Lothar Serra Mari
|
||||
Deborah Servilla
|
||||
Tzach Shabtay
|
||||
Gilad Shaham
|
||||
Lars Skovlund
|
||||
Paul Smedley
|
||||
Dmitry Smirnov
|
||||
Colin Snover
|
||||
Tarek Soliman
|
||||
Nick Sonneveld
|
||||
Robert Špalek
|
||||
Won Star
|
||||
John Steele Scott
|
||||
Shane Stevens
|
||||
Matthew Stewart
|
||||
Markus Strangl
|
||||
Ludvig Strigeus
|
||||
Fedor Strizhniou
|
||||
David Symonds
|
||||
Yaron Tausky
|
||||
Matt Taylor
|
||||
Joel Teichroeb
|
||||
Julien Templier
|
||||
Nikita Tereshin
|
||||
Tobia Tesan
|
||||
Ferdinand Thiessen
|
||||
Scott Thomas
|
||||
Will Thomson
|
||||
Brian Tietz
|
||||
Pino Toscano
|
||||
Trembyle
|
||||
David Turner
|
||||
Lionel Ulmer
|
||||
Philippe Valembois
|
||||
Alan Van Drake
|
||||
Tom Vandepoele
|
||||
Sebástien Viannay
|
||||
Erico Vieira Porto
|
||||
Jordi Vilalta Prat
|
||||
Joni Vähämäki
|
||||
Shawn R. Walker
|
||||
Chris Warren-Smith
|
||||
Robin Watts
|
||||
David Weinehall
|
||||
Fredrik Wendel
|
||||
Piotr Wieczorek
|
||||
Paul Wilkinson
|
||||
Morgan Willcock
|
||||
Berian Williams
|
||||
John Willis
|
||||
Joseph-Eugene Winzer
|
||||
Jaromír Wysoglad
|
||||
Łukasz Wątka
|
||||
Crane Yang
|
||||
Anton Yarcev
|
||||
Simei Yin
|
||||
Kamil Zbróg
|
||||
Liu Zhaosong
|
||||
Daniel c. Würl
|
||||
Daniël ter Laan
|
||||
Hein-Pieter van Braam-Stewart
|
||||
Roland van Laar
|
||||
Ingo van Lil
|
||||
Walter van Niftrik
|
||||
|
||||
|
||||
Patches contributed by:
|
||||
|
||||
Henrik "Henke37" Andersson
|
||||
Anton (Zidane)
|
||||
Alex ASP
|
||||
Laura Abbott "sageofminerva"
|
||||
Gordon Acocella
|
||||
Walter Agazzi
|
||||
Vikram Aggarwal "youngelf"
|
||||
Divyam Ahuja
|
||||
Daniel Albano
|
||||
Manuel Alfayate
|
||||
Fred Almeida
|
||||
Henrik Andersson
|
||||
Tor Andersson
|
||||
Fedor Antokhin
|
||||
Athanasios Antoniou
|
||||
Joseph Applegate
|
||||
Francesco Ariis
|
||||
Ettore Atalan
|
||||
Antoniou Athanasios
|
||||
Ivan Avdeev
|
||||
Logan B
|
||||
Niv Baehr
|
||||
Norbert Bajkó
|
||||
Giovanni Bajo
|
||||
Scott Baker
|
||||
Michael Ball
|
||||
Daniel Balsom
|
||||
Luca Barbato
|
||||
Matan Bareket
|
||||
Dieter Baron "dillo"
|
||||
Rouven Bauer
|
||||
Harsh Bawari
|
||||
Kevin Becker
|
||||
Alban Bedel "albeu"
|
||||
Bodo Bellut "bellut"
|
||||
Nagy Bendeguz
|
||||
Chris Benshoof
|
||||
Jonny Bergström
|
||||
Vincent Bernat
|
||||
C.W. Betts
|
||||
Vincent Bénony
|
||||
Andreas Bierfert "awjb"
|
||||
Kaustav Biswas
|
||||
Elio Blanca
|
||||
Elio Blanca "eblanca76"
|
||||
Dmitry Blau
|
||||
Laurent Blume
|
||||
Martin Bohm
|
||||
Paolo Bossi
|
||||
Peter Bozsó
|
||||
Jurgen Braam
|
||||
Carlo Bramini
|
||||
Christophe Branchereau
|
||||
David Breakey "dbreakey"
|
||||
Michael Brown
|
||||
Robert Buchholz "prendi"
|
||||
Lars Buitinck
|
||||
Patrick Burke
|
||||
Tim Burke
|
||||
Sander Buskens
|
||||
Vincent Bénony
|
||||
Martin Böhm
|
||||
Rafał Będźkowski
|
||||
Giulio Camuffo
|
||||
Ralph Caraveo III
|
||||
Sergio Carmona
|
||||
Kevin Carnes
|
||||
Mathieu Carot "yokna"
|
||||
Ray Carro
|
||||
Milo Casagrande
|
||||
Ben Castricum
|
||||
Little Cat
|
||||
Stefano Ceccherini "jackburton"
|
||||
Eduard Chaika
|
||||
Pragyansh Chaturvedi (r41k0u)
|
||||
Hyunseo Cho
|
||||
Dan Church
|
||||
Josh Coalson "jcoalson"
|
||||
Curt Coder
|
||||
Fabien Coeurjoly
|
||||
Stefano Collavini
|
||||
Thomas Combeleran "hibernatus"
|
||||
Patrick Combet
|
||||
Kees Cook "keescook"
|
||||
Carlos Corbacho "cathectic"
|
||||
David Cordero
|
||||
Andrea Corna
|
||||
Gabriel Corona
|
||||
David Corrales
|
||||
David Corrales-Lopez
|
||||
Roberto Costa "fiix76"
|
||||
Alan Cox
|
||||
Robert Crossfield
|
||||
Eric Culp
|
||||
Michael D
|
||||
Patrik Dahlström
|
||||
Sunit Das
|
||||
Misty De Meo
|
||||
Alexander Dergunov
|
||||
Alexandre Detiste
|
||||
John Doe
|
||||
Roman Donchenko
|
||||
Ian Douglas Scott
|
||||
Heather Douglass
|
||||
Michael Drüing "doc_wagon"
|
||||
Bartosz Dudziak
|
||||
Matthew Duggan "stauff1"
|
||||
Barry Duncan
|
||||
Olivier Duverne "richiefs"
|
||||
Andrei Dziahel "develop7"
|
||||
Tomasz Długosz
|
||||
Jonathan E. Wright
|
||||
John Eckerdal "johneck"
|
||||
Annick Ecuyer
|
||||
Erich Edgar Hoover
|
||||
Ángel Eduardo García Hernández
|
||||
Abdeselam El-Haman
|
||||
Chatziargyriou Eleftheria
|
||||
Henrik Engqvist
|
||||
Björn Esser
|
||||
Michael Fink
|
||||
Alexandre Folle de Menezes
|
||||
Alex Fontoura
|
||||
Ignaz Forster
|
||||
Hans-Jörg Frieden
|
||||
Mike Frysinger "vapier"
|
||||
Adrian Frühwirth
|
||||
Andrea G
|
||||
Markus G
|
||||
D G Turner
|
||||
Santiago G. Sanz
|
||||
Matthew Gambrell
|
||||
Edu Garcia
|
||||
Matthew Garrett
|
||||
Bence Gazder
|
||||
Chris Gelatt "kreeblah"
|
||||
Lafazar Gendibal
|
||||
Bartosz Gentkowski
|
||||
Jens Georg "phako"
|
||||
Nicolas George "cigaes"
|
||||
Martin Gerhardy
|
||||
Jonathan Gevaryahu "lord_nightmare"
|
||||
Anubhab Ghosh
|
||||
Janet Gilbert
|
||||
Dmitry Gladkov
|
||||
Boris Gnezdilov
|
||||
David Goldsmith
|
||||
Chris Gray
|
||||
Evgeny Grechnikov
|
||||
Gustavo Grieco
|
||||
Tobias Gruetzmacher "tobig"
|
||||
Damien Guard "damienguard"
|
||||
Yuri Guimaraes
|
||||
Krzysztof Głodo
|
||||
Matti Hamalainen "ccrtnsp"
|
||||
Ruediger Hanke
|
||||
Rüdiger Hanke
|
||||
Tobias Hansen
|
||||
Zvika Haramaty
|
||||
Matt Hargett
|
||||
Lauri Harsila
|
||||
Toël Hartmann
|
||||
Stefan Haubenthal "polluks"
|
||||
Gavin Hayler
|
||||
Peter Helbing
|
||||
Steven Hoefel
|
||||
Alexander Holler "holler"
|
||||
Erich Hoover
|
||||
Enrico Horn
|
||||
Cadi Howley
|
||||
Falk Hueffner "mellum"
|
||||
Casey Hutchinson "nnooiissee"
|
||||
Lauri Härsilä
|
||||
Marius Ioan Orban
|
||||
Mikel Iturbe Urretxa
|
||||
Marchukov Ivan
|
||||
Michael J. Roberts
|
||||
Tomas Jakobsson
|
||||
Alex Jakovleff
|
||||
Felix Jakschitsch
|
||||
Christian Jamieson
|
||||
Michał Janiszewski
|
||||
Gregor Jasny "gjasny"
|
||||
Joachim Jautz
|
||||
Francisco Javier Diéguez Tirado
|
||||
Francisco Javier Trujillo Mata
|
||||
Peter Johansson
|
||||
Matt Johnson "mattjon"
|
||||
Stefan Jokisch
|
||||
Nicolas Joly "njoly"
|
||||
Chris Jones
|
||||
Kastuś K
|
||||
Keith Kaisershot
|
||||
Yauheni Kaliuta
|
||||
Daniel Kamil Kozar
|
||||
Yusuke Kamiyamane
|
||||
Jonas Karlsson
|
||||
Felix Kehrer
|
||||
Robert Kelsen
|
||||
Martin Kennedy
|
||||
Stephen Kennedy
|
||||
Sam Kenny "sam_k"
|
||||
Kalle Kietavainen
|
||||
Arthur Kiyanovski
|
||||
Ruud Klaver
|
||||
Kai Knoblich
|
||||
Sven Kochmann
|
||||
Koen Kooi "koenkooi"
|
||||
George Kormendi
|
||||
Christoph Korn
|
||||
Maxim Kovalenko
|
||||
Petr Kratina
|
||||
Christian Krause
|
||||
Till Kresslein
|
||||
Henrik Kretzschmar
|
||||
Stefan Kristiansson
|
||||
Daniel Krol
|
||||
Zygmunt Krynicki "zygoon"
|
||||
Sebastian Krzyszkowiak
|
||||
Janne Kujanpaa "jukuja"
|
||||
Neeraj Kumar
|
||||
Pavel Kungurtsev
|
||||
Oleksiy Kurochko
|
||||
Bernhard Kölbl
|
||||
Jay Lanagan "r0ni"
|
||||
Norbert Lange "nolange"
|
||||
Manuel Lauss "mlau2"
|
||||
Gael Le Migno "kilobug"
|
||||
Joe Lee
|
||||
Joohan Lee
|
||||
Jozef Legény
|
||||
Rolf Leggewie "leggewie"
|
||||
Jim Leiterman
|
||||
Matt Lewandowsky
|
||||
Chenbo Li
|
||||
Christian Lindemann
|
||||
Lyubomyr Lisen
|
||||
Douglas Liu
|
||||
Rob Loach
|
||||
Duncan Lock "dflock"
|
||||
Mark Lodato "itsr0y"
|
||||
Fridvin Logi "phillip_j_fry"
|
||||
Michael Lojkovic
|
||||
Borja Lorente Escobar
|
||||
Schrijvers Luc
|
||||
Georg Lukas "ge0rg"
|
||||
Artem Lukoyanov
|
||||
Ivan Lukyanov
|
||||
Pieter Luteijn
|
||||
Duane Mach
|
||||
Michael Madsen "pidgeot"
|
||||
Matthias Mailander
|
||||
Narek Mailian
|
||||
Matthias Mailänder
|
||||
Christoph Mallon
|
||||
Engin Manap
|
||||
Malignant Manor
|
||||
Dmitry Marakasov "amdmi3"
|
||||
Dominik Marks
|
||||
Andrew Martin
|
||||
Víctor Martínez Pajares
|
||||
Alejandro Marzini "vgvgf"
|
||||
Claudio Matsuoka
|
||||
Sam Matthews
|
||||
Avijeet Maurya
|
||||
Christian Mayer
|
||||
Steve McCrea
|
||||
Travis McKay
|
||||
Connor McLeod "mcleod2032"
|
||||
Jennifer McMurray
|
||||
Mickey McMurray "metafox"
|
||||
Robert Megone
|
||||
Sven Meier
|
||||
Laurent Merckx
|
||||
Adam Metcalf "gamblore"
|
||||
Nicola Mettifogo
|
||||
Nicola Mettifogo
|
||||
Frank Meyering "frank_m24"
|
||||
Timo Mikkolainen
|
||||
Alexander Miller
|
||||
Evan Miller
|
||||
Etienne Millon
|
||||
Ivan Mogilko
|
||||
Andy Molloy "maloi"
|
||||
Joan Montané
|
||||
Omer Mor
|
||||
Cristian Morales Vega
|
||||
Armin Mueller "arm_in"
|
||||
Sean Murrau "lightcast"
|
||||
Andrea Musuruane "musuruan"
|
||||
KO Myung-Hun "lvzuufx"
|
||||
Markus Napp "meist3r"
|
||||
Peter Naulls "pnaulls"
|
||||
Christian Neumair "mannythegnome"
|
||||
Hannes Niederhausen
|
||||
Juha Niemimäki
|
||||
Markus Niemistö "niemisto"
|
||||
Nicolas Noble
|
||||
Bastien Nocera
|
||||
Jody Northup
|
||||
Ryan Nunn
|
||||
Steffen Nyeland
|
||||
Ano Nymous
|
||||
Ryan O'Connor
|
||||
Per Olav Flaten
|
||||
Pere Orga
|
||||
Julian Ospald
|
||||
Christopher Page
|
||||
Oliver Pahl
|
||||
Alexander Panov
|
||||
Chris Paras "paras_rasmatazz"
|
||||
Mathias Parnaudeau
|
||||
Stefan Parviainen
|
||||
Aubin Paul "outlyer"
|
||||
Dennis Payne
|
||||
Michael Pearce
|
||||
Vincent Pelletier "subdino"
|
||||
Benjamin Penney
|
||||
Tobias Pfaff
|
||||
Jonathan Phénix
|
||||
Jussi Pitkanen
|
||||
Daniel Plakhotich
|
||||
Andrew Plotkin
|
||||
Carsten Pohl "carstenpohl"
|
||||
Steven Poulton
|
||||
Tony Puccinelli
|
||||
Markus Pyykko "mankeli"
|
||||
Kamil Páral
|
||||
Sergi Pérez Labernia
|
||||
Shawn R. Walker
|
||||
Jelle Raaijmakers
|
||||
Max Raskin
|
||||
Aryan Rawlani
|
||||
Rodrigo Rebello
|
||||
Dominik Reichardt
|
||||
Alexander Reim
|
||||
Nick Renieris
|
||||
Frank Richter
|
||||
Thomas Richter "thorfdbg"
|
||||
Felix Riemann "kirschsaft"
|
||||
Michael Rittenhouse
|
||||
Morgan Roberts
|
||||
Stephen Robinson
|
||||
Timo Roehling "t1m0"
|
||||
Jonathan Rogers "jonner"
|
||||
Enrico Rolfi
|
||||
Jean-Christophe Rona
|
||||
Doron Rosenberg
|
||||
Bernhard Rosenkraenzer
|
||||
Marek Roth "logicdeluxe"
|
||||
Edward Rudd
|
||||
David Russo
|
||||
Uwe Ryssel "uweryssel"
|
||||
Rafał Rzepecki
|
||||
Travis S Coady "theealien"
|
||||
Felipe Sanches
|
||||
Santiago Sanchez
|
||||
Vitor Santos
|
||||
Fernando Sarmento
|
||||
Simon Sawatzki
|
||||
Dario Scarpa
|
||||
Dominik Scherer
|
||||
Jochen Schleu
|
||||
Florian Schmitt "fatpenguin"
|
||||
Martin Schoenmakers
|
||||
Mark Schreiber "mark7"
|
||||
Luc Schrijvers
|
||||
Stian Schultz
|
||||
Zbyněk Schwarz
|
||||
Keith Scroggins
|
||||
Martin Sedlak
|
||||
Deborah Servilla
|
||||
Tzach Shabtay
|
||||
Ben Shadwick "benshadwick"
|
||||
Gilad Shaham
|
||||
Orgad Shaneh
|
||||
Rodrigo Silva
|
||||
Jean-Yves Simon "lethalwp"
|
||||
Catarina Simões
|
||||
Andrej Sinicyn "andrej4000"
|
||||
Petter Sjölund
|
||||
Paul Smedley
|
||||
Dmitry Smirnov
|
||||
Nick Sonneveld
|
||||
Marcel Souza Lemes
|
||||
Thomas Sowell
|
||||
Christian Speckner
|
||||
Jan Sperling
|
||||
Steve Stavropoulos "isnothere"
|
||||
John Steele Scott
|
||||
Daniel Steinberger "amorphousshape"
|
||||
Shane Stevens
|
||||
Markus Strangl
|
||||
Fedor Strizhniou
|
||||
Sven Strothoff "dataslayer"
|
||||
Andrea Suatoni "mrhandler"
|
||||
Yoshi Sugawara
|
||||
Purple T
|
||||
Martin T. H. Sandsmark
|
||||
Max Tabachenko
|
||||
Krzysztof Targoński
|
||||
Yaron Tausky
|
||||
Matt Taylor
|
||||
DOSBox Team
|
||||
Sarien Team
|
||||
Jonathan Teh
|
||||
Joel Teichroeb
|
||||
Denis Telyukh
|
||||
Nikita Tereshin
|
||||
Jimmi Thogersen
|
||||
Will Thomson
|
||||
Brian Tietz
|
||||
Yegor Timoshenko
|
||||
Pino Toscano
|
||||
Luigi Toscano "ltosky"
|
||||
Xavier Trochu "xtrochu"
|
||||
Vasyl Tsvirkunov
|
||||
Michal Tulacek "tutchek"
|
||||
Michael Udaltsov "cccp99"
|
||||
Joni Vahamaki
|
||||
Aashwin Vaish
|
||||
Alfred Vallés Tortosa
|
||||
Alan Van Drake
|
||||
Tom Vandepoele
|
||||
Kristof Vansant "lupusbe"
|
||||
Aaryaman Vasishta
|
||||
Rodrigo Vegas Sánchez-Ferrero
|
||||
Sebástien Viannay
|
||||
Erico Vieira Porto
|
||||
Aapo Vienamo
|
||||
Marc Vinyals
|
||||
Artem Vorobiev
|
||||
Josef Väcklén
|
||||
Joni Vähämäki
|
||||
Linus Väinämö Virtanen
|
||||
Benjamin W. Zale "junior_aepi"
|
||||
Jakob Wagner
|
||||
Rebecca Wallander
|
||||
Tim Walters "realmz"
|
||||
Donovan Watteau
|
||||
David Weinehall
|
||||
Eric A. Welsh "eweish42"
|
||||
Fredrik Wendel
|
||||
Lars Wendler
|
||||
Yudhi Widyatama "yudhi97"
|
||||
Piotr Wieczorek
|
||||
Jakub Wilk
|
||||
Kieron Wilkinson
|
||||
Paul Wilkinson
|
||||
Stefan Will
|
||||
Morgan Willcock
|
||||
Berian Williams
|
||||
David-John Willis
|
||||
Robert Wohlrab "moshroum"
|
||||
Daniel Wolf
|
||||
James Woodcock
|
||||
Jaromír Wysoglad
|
||||
Łukasz Wątka
|
||||
Crane Yang
|
||||
Anton Yarcev
|
||||
James Ye
|
||||
Grant Yeager "glo_kidd"
|
||||
Zhiqi Yin
|
||||
Kamil Zbrog
|
||||
Liu Zhaosong
|
||||
Xiao Zheng
|
||||
Michael Zinn
|
||||
Michał Ziąbkowski
|
||||
Erik Zubiria
|
||||
Daniel c. Würl
|
||||
Michael du Breuil "WickedShell"
|
||||
dc france "erwan2004"
|
||||
the rara avis "theraraavis"
|
||||
Daniël ter Laan
|
||||
Hein-Pieter van Braam
|
||||
Hein-Pieter van Braam-Stewart
|
||||
Max von Werner
|
||||
Crane yang
|
||||
Роман Донченко
|
||||
Денис Телюх
|
||||
AReim1982
|
||||
AndywinXp
|
||||
Arius
|
||||
AsciiWolf
|
||||
Asirome
|
||||
AspireONE-zz
|
||||
Avijeet
|
||||
BLooperZ
|
||||
BeWorld
|
||||
Bendegúz
|
||||
Bernhard
|
||||
Bluddy
|
||||
Bramvandijk "bramvandijk"
|
||||
Canadacow
|
||||
Carlos
|
||||
CeRiAl
|
||||
countingpine
|
||||
Cpasjuste
|
||||
CrazyMax
|
||||
Damien
|
||||
Damien
|
||||
dewt "mncl"
|
||||
DinoWattz
|
||||
DivyamAhuja
|
||||
DouglasLiuGamer
|
||||
DreadnoughtPT
|
||||
dubsdj
|
||||
Faalagorn
|
||||
Federico
|
||||
Fenyx4
|
||||
Florent "flobo"
|
||||
Florob "florob"
|
||||
Gazben
|
||||
Gilles
|
||||
Gucek
|
||||
GunnarBirke
|
||||
IlDucci
|
||||
Iskrich
|
||||
j0tt
|
||||
Jellby "jellby"
|
||||
JenniBee
|
||||
Joerg "macdrega"
|
||||
Jonas
|
||||
Kawa
|
||||
Kawa-oneechan
|
||||
Kyuubu
|
||||
LKramer
|
||||
LMerckx
|
||||
Lenny
|
||||
Lostech "lostech"
|
||||
Luis
|
||||
Manuel
|
||||
Marisa-Chan
|
||||
MaximRussia
|
||||
MestreLion
|
||||
MrHuu
|
||||
NMIError
|
||||
Nicos "anarxia"
|
||||
Nitrus
|
||||
ole
|
||||
phi1
|
||||
Pix2 "pix2"
|
||||
Quote58
|
||||
Raina
|
||||
Richard "trinity78"
|
||||
RichieSams
|
||||
Rossano
|
||||
Ryper_Zsolt
|
||||
Scarlatti "escarlate"
|
||||
SecularSteve
|
||||
ShaharAriel
|
||||
Snejp
|
||||
Strangerke
|
||||
SupSuper
|
||||
TehGelly
|
||||
Thunderforge
|
||||
Tim "tipabu"
|
||||
Timofonic
|
||||
TomFrost
|
||||
TomasM
|
||||
Trembyle
|
||||
Twan
|
||||
VAN-Gluon
|
||||
vandalo
|
||||
Vanfanel
|
||||
VelocityRa
|
||||
Vhati
|
||||
Wammus
|
||||
WinterGrascph
|
||||
Xanathar "xanathar"
|
||||
Xaviu
|
||||
YYxsCnnPP
|
||||
Zerophase
|
||||
Zibri
|
||||
Zvika
|
||||
|
||||
|
||||
FreeSCI contributors
|
||||
|
||||
Francois-R Boyer
|
||||
Rainer Canavan
|
||||
Xiaojun Chen
|
||||
Paul David Doherty
|
||||
Vyacheslav Dikonov
|
||||
Ruediger Hanke
|
||||
Matt Hargett
|
||||
Max Horn
|
||||
Ravi I.
|
||||
Emmanuel Jeandel
|
||||
Dmitry Jemerov
|
||||
Chris Kehler
|
||||
Christopher T. Lansdown
|
||||
Sergey Lapin
|
||||
Rickard Lind
|
||||
Hubert Maier
|
||||
Johannes Manhave
|
||||
Claudio Matsuoka
|
||||
Dark Minister
|
||||
Carl Muckenhoupt
|
||||
Anders Baden Nielsen
|
||||
Walter van Niftrik
|
||||
Rune Orsval
|
||||
Solomon Peachy
|
||||
Robey Pointer
|
||||
Magnus Reftel
|
||||
Christoph Reichenbach
|
||||
George Reid
|
||||
Lars Skovlund
|
||||
Rink Springer
|
||||
Rainer De Temple
|
||||
Sean Terrell
|
||||
Hugues Valois
|
||||
Jordi Vilalta
|
||||
Petr Vyhnak
|
||||
Bas Zoetekouw
|
||||
|
||||
|
||||
ResidualVM
|
||||
Copyright (C) 2003-2021 by the following:
|
||||
|
||||
Some of the code in this project was originally LGPL v2.1 (or later)
|
||||
but is used as GPL v2 (or later) starting from 19 Dec 2012.
|
||||
|
||||
If you have contributed to this project then you deserve to be on this
|
||||
list. Contact us (see: AUTHORS) and we'll add you.
|
||||
|
||||
Thomas Allen
|
||||
Torbjorn Andersson
|
||||
Ori Avtalion
|
||||
Gunnar Birke
|
||||
Robert Biro
|
||||
Elio Blanca
|
||||
Bastien Bouclet
|
||||
James Brown
|
||||
Giulio Camuffo
|
||||
David Cardwell
|
||||
Cameron Cawley
|
||||
Marcus Comstedt
|
||||
Andrea Corna
|
||||
Bartosz Dudziak
|
||||
David Fioramonti
|
||||
Jonathan Gray
|
||||
Tobias Gunkel
|
||||
Azamat H. Hackimov
|
||||
Vincent Hamm
|
||||
Dries Harnie
|
||||
Sven Hesse
|
||||
Matthew Hoops
|
||||
Erich Hoover
|
||||
Max Horn
|
||||
Travis Howell
|
||||
Joseph Jezak
|
||||
Guillem Jover
|
||||
Filippos Karapetis
|
||||
Pawel Kolodziejski
|
||||
Christian Krause
|
||||
Ingo van Lil
|
||||
Douglas Liu
|
||||
Joost Peters
|
||||
Jordi Vilalta Prat
|
||||
Awad Mackie
|
||||
George Macon
|
||||
Hubert Maier
|
||||
Josh Matthews
|
||||
Matthieu Milan
|
||||
Gregory Montoir
|
||||
Stefano Musumeci
|
||||
Christian Neumair
|
||||
Marius Ioan Orban
|
||||
Vincent Pelletier
|
||||
Aryan Rawlani
|
||||
Daniel Schepler
|
||||
Orgad Shaneh
|
||||
Dmitry Smirnov
|
||||
Colin Snover
|
||||
Einar Johan T. Somaaen
|
||||
Yaron Tausky
|
||||
Joel Teichroeb
|
||||
Julien Templier
|
||||
Scott Thomas
|
||||
Will Thomson
|
||||
Pino Toscano
|
||||
Lionel Ulmer
|
||||
Joni Vahamaki
|
||||
Liu Zhaosong
|
||||
ArtemVorobiev
|
||||
aviloria
|
||||
cmayer0087
|
||||
Faalagorn
|
||||
federicorosso1993
|
||||
JenniBee
|
||||
karjonas
|
||||
Kaede
|
||||
LMerckx
|
||||
mparnaudeau
|
||||
orangeforest11
|
||||
PoulpiFr
|
||||
sietschie
|
||||
207
LICENSES/COPYING.Apache
Normal file
207
LICENSES/COPYING.Apache
Normal file
@@ -0,0 +1,207 @@
|
||||
|
||||
NOTE: This license file only applies to the following files distributed
|
||||
along with our theme files:
|
||||
|
||||
* MaterialSymbolsSharp.ttf
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
156
LICENSES/COPYING.BSD
Normal file
156
LICENSES/COPYING.BSD
Normal file
@@ -0,0 +1,156 @@
|
||||
NOTE: Only certain parts of the ScummVM project are under the BSD license.
|
||||
The majority of the files are under the GNU GPL. See the headers of the
|
||||
individual files to find out the exact license.
|
||||
|
||||
The term "BSD license" refers to any BSD-like license, as they are sometimes
|
||||
hard to tell apart. No slight against other licenses is intended.
|
||||
|
||||
|
||||
Parts of the Nintendo DS port use the following license:
|
||||
|
||||
Copyright (c) 2006 Michael "Chishm" Chisholm
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
||||
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Parts of the MPEG decoder use the following license:
|
||||
|
||||
Copyright (c) 1995 The Regents of the University of California.
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without written agreement is
|
||||
hereby granted, provided that the above copyright notice and the following
|
||||
two paragraphs appear in all copies of this software.
|
||||
|
||||
IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||
OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
|
||||
CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
|
||||
ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
|
||||
PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
Copyright (c) 1995 Erik Corry
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without written agreement is
|
||||
hereby granted, provided that the above copyright notice and the following
|
||||
two paragraphs appear in all copies of this software.
|
||||
|
||||
IN NO EVENT SHALL ERIK CORRY BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
|
||||
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF
|
||||
THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF ERIK CORRY HAS BEEN ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
ERIK CORRY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
|
||||
BASIS, AND ERIK CORRY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
|
||||
UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
Portions of this software Copyright (c) 1995 Brown University.
|
||||
All rights reserved.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software and its
|
||||
documentation for any purpose, without fee, and without written agreement
|
||||
is hereby granted, provided that the above copyright notice and the
|
||||
following two paragraphs appear in all copies of this software.
|
||||
|
||||
IN NO EVENT SHALL BROWN UNIVERSITY BE LIABLE TO ANY PARTY FOR
|
||||
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
|
||||
OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF BROWN
|
||||
UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
BROWN UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
|
||||
BASIS, AND BROWN UNIVERSITY HAS NO OBLIGATION TO PROVIDE MAINTENANCE,
|
||||
SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
||||
|
||||
|
||||
Parts of the MT-32 emulator use the following license:
|
||||
|
||||
Copyright (c) 2011, Micael Hildenborg
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of Micael Hildenborg nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY Micael Hildenborg ''AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL Micael Hildenborg BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
ScummVM uses OpenFontIcons published under the terms of the following license
|
||||
|
||||
Copyright (c) 2016 Bigelow & Holmes Inc.. All rights reserved.
|
||||
|
||||
Distribution of this font is governed by the following license. If you do not
|
||||
agree to this license, including the disclaimer, do not distribute or modify
|
||||
this font.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Google Inc. nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
DISCLAIMER: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
27
LICENSES/COPYING.BSL
Normal file
27
LICENSES/COPYING.BSL
Normal file
@@ -0,0 +1,27 @@
|
||||
NOTE: This license file only applies to the following source files
|
||||
in ScummVM:
|
||||
|
||||
* engines/twp/clipper/clipper.cpp
|
||||
* engines/twp/clipper/clipper.h
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
69
LICENSES/COPYING.GLAD
Normal file
69
LICENSES/COPYING.GLAD
Normal file
@@ -0,0 +1,69 @@
|
||||
NOTE: This license file only applies to the following source files
|
||||
in ScummVM:
|
||||
|
||||
* graphics/opengl/glad.h
|
||||
|
||||
|
||||
The glad source code:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2021 David Herberth
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
The Khronos Specifications:
|
||||
|
||||
Copyright (c) 2013-2020 The Khronos Group Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
|
||||
The EGL Specification and various headers:
|
||||
|
||||
Copyright (c) 2007-2016 The Khronos Group Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and/or associated documentation files (the
|
||||
"Materials"), to deal in the Materials without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Materials, and to
|
||||
permit persons to whom the Materials are furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Materials.
|
||||
|
||||
THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
|
||||
19
LICENSES/COPYING.ISC
Normal file
19
LICENSES/COPYING.ISC
Normal file
@@ -0,0 +1,19 @@
|
||||
NOTE: All .patchr files in ScummVM are under the ISC license:
|
||||
|
||||
ScummVM - Graphic Adventure Engine
|
||||
|
||||
ScummVM is the legal property of its developers, whose names
|
||||
are too numerous to list here. Please refer to the COPYRIGHT
|
||||
file distributed with this source distribution.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
508
LICENSES/COPYING.LGPL
Normal file
508
LICENSES/COPYING.LGPL
Normal file
@@ -0,0 +1,508 @@
|
||||
NOTE: Only certain parts of the ScummVM project are under the GNU LGPL.
|
||||
The majority of the files are under the GNU GPL. See the headers of the
|
||||
individual files to find out the exact license.
|
||||
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
38
LICENSES/COPYING.LUA
Normal file
38
LICENSES/COPYING.LUA
Normal file
@@ -0,0 +1,38 @@
|
||||
NOTE: ScummVM uses code from Lua, which is covered by the following license.
|
||||
This implementation of Lua contains additional code and modifications added
|
||||
only for ScummVM project.
|
||||
Look into the source code file "engines/grim/lua/Changelog" for more info.
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (c) 1994-1998 TeCGraf, PUC-Rio. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, without written agreement and without license
|
||||
* or royalty fees, to use, copy, modify, and distribute this software and its
|
||||
* documentation for any purpose, including commercial applications, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* - The above copyright notice and this permission notice shall appear in all
|
||||
* copies or substantial portions of this software.
|
||||
*
|
||||
* - The origin of this software must not be misrepresented; you must not
|
||||
* claim that you wrote the original software. If you use this software in a
|
||||
* product, an acknowledgment in the product documentation would be greatly
|
||||
* appreciated (but it is not required).
|
||||
*
|
||||
* - Altered source versions must be plainly marked as such, and must not be
|
||||
* misrepresented as being the original software.
|
||||
*
|
||||
* The authors specifically disclaim any warranties, including, but not limited
|
||||
* to, the implied warranties of merchantability and fitness for a particular
|
||||
* purpose. The software provided hereunder is on an "as is" basis, and the
|
||||
* authors have no obligation to provide maintenance, support, updates,
|
||||
* enhancements, or modifications. In no event shall TeCGraf, PUC-Rio, or the
|
||||
* authors be held liable to any party for direct, indirect, special,
|
||||
* incidental, or consequential damages arising out of the use of this software
|
||||
* and its documentation.
|
||||
*
|
||||
* The Lua language and this implementation have been entirely designed and
|
||||
* written by Waldemar Celes Filho, Roberto Ierusalimschy and
|
||||
* Luiz Henrique de Figueiredo at TeCGraf, PUC-Rio.
|
||||
*
|
||||
******************************************************************************/
|
||||
56
LICENSES/COPYING.MIT
Normal file
56
LICENSES/COPYING.MIT
Normal file
@@ -0,0 +1,56 @@
|
||||
NOTE: Only certain parts of the ScummVM project are under the MIT license.
|
||||
ScummVM uses matrix manipulation code from the glm library. Look into
|
||||
source code files "engines/grim/gfx_base.cpp",
|
||||
"engines/grim/gfx_opengl_shaders.cpp" and
|
||||
"engines/myst3/gfx_opengl_shaders.cpp" for more info. The majority of the
|
||||
files are under the GNU GPL. See the headers of the individual files to
|
||||
find out the exact license.
|
||||
|
||||
|
||||
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2005 - 2012 G-Truc Creation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
|
||||
ScummVM uses NanoJPEG -- KeyJ's Tiny Baseline JPEG Decoder published under
|
||||
the terms of the MIT license.
|
||||
|
||||
Copyright (c) 2009-2016 Martin J. Fiedler <martin.fiedler@gmx.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to
|
||||
deal in the Software without restriction, including without limitation the
|
||||
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
34
LICENSES/COPYING.MKV
Normal file
34
LICENSES/COPYING.MKV
Normal file
@@ -0,0 +1,34 @@
|
||||
NOTE: This license file only applies to the following files and
|
||||
directories in ScummVM:
|
||||
|
||||
* video/mkv/*
|
||||
|
||||
Copyright (c) 2010, Google Inc. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
* Neither the name of Google nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
379
LICENSES/COPYING.MPL
Normal file
379
LICENSES/COPYING.MPL
Normal file
@@ -0,0 +1,379 @@
|
||||
NOTE: This license file only applies to the following files and
|
||||
directories:
|
||||
|
||||
* engines/director/lingo/lingodec/
|
||||
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
115
LICENSES/COPYING.OFL
Normal file
115
LICENSES/COPYING.OFL
Normal file
@@ -0,0 +1,115 @@
|
||||
NOTE: This license file only applies to the fillowing files distributed
|
||||
along with our theme files:
|
||||
|
||||
* LiberationMono-Bold.ttf
|
||||
* LiberationMono-Regular.ttf
|
||||
* LiberationSans-Bold.ttf
|
||||
* LiberationSans-Regular.ttf
|
||||
* LiberationSerif-Bold.ttf
|
||||
* LiberationSerif-Regular.ttf
|
||||
* NotoNaskhArabic-Bold.ttf
|
||||
* NotoNaskhArabic-Regular.ttf
|
||||
* NotoSerif-Bold-Italic.ttf
|
||||
* NotoSerif-Bold.ttf
|
||||
* NotoSerif-Italic.ttf
|
||||
* NotoSerif-Regular.ttf
|
||||
* NotoSans-Bold.ttf
|
||||
* NotoSans-Regular.ttf
|
||||
* NotoSansRunic-Regular.ttf
|
||||
* SourceCodeVariable-Roman.ttf
|
||||
|
||||
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
|
||||
with Reserved Font Name 'Source'. All Rights Reserved. Source is a
|
||||
trademark of Adobe Systems Incorporated in the United States and/or other
|
||||
countries.
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
24
LICENSES/COPYING.TINYGL
Normal file
24
LICENSES/COPYING.TINYGL
Normal file
@@ -0,0 +1,24 @@
|
||||
NOTE: ScummVM uses code from TinyGL, which is covered by the following license.
|
||||
This implementation of TinyGL contains additional code and modifications
|
||||
added only for ScummVM project.
|
||||
Look into the source code file "graphics/tinygl/Changelog" for more info.
|
||||
|
||||
Copyright (c) 1997-2022 Fabrice Bellard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
128
LICENSES/CatharonLicense.txt
Normal file
128
LICENSES/CatharonLicense.txt
Normal file
@@ -0,0 +1,128 @@
|
||||
NOTE: The only part of ScummVM under the "Catharon License" is the
|
||||
automatic hinting code in the AGS engine
|
||||
(/engines/ags/lib/freetype-2.1.3/autohint)
|
||||
|
||||
|
||||
The Catharon Open Source LICENSE
|
||||
----------------------------
|
||||
|
||||
2000-Jul-04
|
||||
|
||||
Copyright (C) 2000 by Catharon Productions, Inc.
|
||||
|
||||
|
||||
|
||||
Introduction
|
||||
============
|
||||
|
||||
This license applies to source files distributed by Catharon
|
||||
Productions, Inc. in several archive packages. This license
|
||||
applies to all files found in such packages which do not fall
|
||||
under their own explicit license.
|
||||
|
||||
This license was inspired by the BSD, Artistic, and IJG
|
||||
(Independent JPEG Group) licenses, which all encourage inclusion
|
||||
and use of free software in commercial and freeware products
|
||||
alike. As a consequence, its main points are that:
|
||||
|
||||
o We don't promise that this software works. However, we are
|
||||
interested in any kind of bug reports. (`as is' distribution)
|
||||
|
||||
o You can use this software for whatever you want, in parts or
|
||||
full form, without having to pay us. (`royalty-free' usage)
|
||||
|
||||
o You may not pretend that you wrote this software. If you use
|
||||
it, or only parts of it, in a program, you must acknowledge
|
||||
somewhere in your documentation that you have used the
|
||||
Catharon Code. (`credits')
|
||||
|
||||
We specifically permit and encourage the inclusion of this
|
||||
software, with or without modifications, in commercial products.
|
||||
We disclaim all warranties covering the packages distributed by
|
||||
Catharon Productions, Inc. and assume no liability related to
|
||||
their use.
|
||||
|
||||
|
||||
Legal Terms
|
||||
===========
|
||||
|
||||
0. Definitions
|
||||
--------------
|
||||
|
||||
Throughout this license, the terms `Catharon Package', `package',
|
||||
and `Catharon Code' refer to the set of files originally
|
||||
distributed by Catharon Productions, Inc.
|
||||
|
||||
`You' refers to the licensee, or person using the project, where
|
||||
`using' is a generic term including compiling the project's source
|
||||
code as well as linking it to form a `program' or `executable'.
|
||||
This program is referred to as `a program using one of the
|
||||
Catharon Packages'.
|
||||
|
||||
This license applies to all files distributed in the original
|
||||
Catharon Package(s), including all source code, binaries and
|
||||
documentation, unless otherwise stated in the file in its
|
||||
original, unmodified form as distributed in the original archive.
|
||||
If you are unsure whether or not a particular file is covered by
|
||||
this license, you must contact us to verify this.
|
||||
|
||||
The Catharon Packages are copyright (C) 2000 by Catharon
|
||||
Productions, Inc. All rights reserved except as specified below.
|
||||
|
||||
1. No Warranty
|
||||
--------------
|
||||
|
||||
THE CATHARON PACKAGES ARE PROVIDED `AS IS' WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. IN NO EVENT WILL ANY OF THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY DAMAGES CAUSED BY THE USE OF OR THE INABILITY TO
|
||||
USE THE CATHARON PACKAGE.
|
||||
|
||||
2. Redistribution
|
||||
-----------------
|
||||
|
||||
This license grants a worldwide, royalty-free, perpetual and
|
||||
irrevocable right and license to use, execute, perform, compile,
|
||||
display, copy, create derivative works of, distribute and
|
||||
sublicense the Catharon Packages (in both source and object code
|
||||
forms) and derivative works thereof for any purpose; and to
|
||||
authorize others to exercise some or all of the rights granted
|
||||
herein, subject to the following conditions:
|
||||
|
||||
o Redistribution of source code must retain this license file
|
||||
(`license.txt') unaltered; any additions, deletions or changes
|
||||
to the original files must be clearly indicated in
|
||||
accompanying documentation. The copyright notices of the
|
||||
unaltered, original files must be preserved in all copies of
|
||||
source files.
|
||||
|
||||
o Redistribution in binary form must provide a disclaimer that
|
||||
states that the software is based in part on the work of
|
||||
Catharon Productions, Inc. in the distribution documentation.
|
||||
|
||||
These conditions apply to any software derived from or based on
|
||||
the Catharon Packages, not just the unmodified files. If you use
|
||||
our work, you must acknowledge us. However, no fee need be paid
|
||||
to us.
|
||||
|
||||
3. Advertising
|
||||
--------------
|
||||
|
||||
Neither Catharon Productions, Inc. and contributors nor you shall
|
||||
use the name of the other for commercial, advertising, or
|
||||
promotional purposes without specific prior written permission.
|
||||
|
||||
We suggest, but do not require, that you use the following phrase
|
||||
to refer to this software in your documentation: 'this software is
|
||||
based in part on the Catharon Typography Project'.
|
||||
|
||||
As you have not signed this license, you are not required to
|
||||
accept it. However, as the Catharon Packages are copyrighted
|
||||
material, only this license, or another one contracted with the
|
||||
authors, grants you the right to use, distribute, and modify it.
|
||||
Therefore, by using, distributing, or modifying the Catharon
|
||||
Packages, you indicate that you understand and accept all the
|
||||
terms of this license.
|
||||
|
||||
--- end of license.txt ---
|
||||
160
Makefile
Normal file
160
Makefile
Normal file
@@ -0,0 +1,160 @@
|
||||
# GNU Make 3.80 and older have bugs that cause parsing issues.
|
||||
# Make sure we have at least version 3.81.
|
||||
ifndef .FEATURES
|
||||
$(error GNU Make 3.81 or higher is required)
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
# Default compilation parameters. Normally don't edit these #
|
||||
#######################################################################
|
||||
|
||||
srcdir ?= .
|
||||
|
||||
DEFINES := -DHAVE_CONFIG_H
|
||||
LDFLAGS :=
|
||||
INCLUDES := -I. -I$(srcdir) -I$(srcdir)/engines
|
||||
LIBS :=
|
||||
OBJS :=
|
||||
DEPDIR := .deps
|
||||
|
||||
MODULES :=
|
||||
MODULE_DIRS :=
|
||||
|
||||
# All game detection-related object files for engines
|
||||
DETECT_OBJS :=
|
||||
LOAD_RULES_MK := 1
|
||||
|
||||
# Load the make rules generated by configure
|
||||
-include config.mk
|
||||
|
||||
ifeq "$(HAVE_GCC)" "1"
|
||||
CXXFLAGS:= -Wall $(CXXFLAGS)
|
||||
# Turn off some annoying and not-so-useful warnings
|
||||
CXXFLAGS+= -Wno-long-long -Wno-multichar -Wno-unknown-pragmas -Wno-reorder
|
||||
# Enable even more warnings...
|
||||
CXXFLAGS+= -Wpointer-arith -Wcast-qual
|
||||
CXXFLAGS+= -Wnon-virtual-dtor -Wwrite-strings
|
||||
|
||||
# Currently we disable this gcc flag, since it will also warn in cases,
|
||||
# where using GCC_PRINTF (means: __attribute__((format(printf, x, y))))
|
||||
# is not possible, thus it would fail compilation with -Werror without
|
||||
# being helpful.
|
||||
#CXXFLAGS+= -Wmissing-format-attribute
|
||||
|
||||
# Disable exceptions.
|
||||
CXXFLAGS+= -fno-exceptions
|
||||
|
||||
ifneq "$(HAVE_CLANG)" "1"
|
||||
# enable checking of pointers returned by "new", but only when we do not
|
||||
# build with clang
|
||||
CXXFLAGS+= -fcheck-new
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq "$(HAVE_CLANG)" "1"
|
||||
CXXFLAGS+= -Wno-conversion -Wno-shorten-64-to-32 -Wno-sign-compare -Wno-four-char-constants
|
||||
# We use a anonymous nested type declaration in an anonymous union in
|
||||
# common/str.h. This is no standard construct and clang warns about it.
|
||||
# It works for all our target systems though, thus we simply disable that
|
||||
# warning.
|
||||
CXXFLAGS+= -Wno-nested-anon-types
|
||||
endif
|
||||
|
||||
ifeq "$(HAVE_ICC)" "1"
|
||||
# Disable some warnings:
|
||||
# 161: unrecognized #pragma
|
||||
# 1899: multicharacter character literal (potential portability problem)
|
||||
CXXFLAGS+= -diag-disable 161,1899
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
# Default commands - put the necessary replacements in config.mk #
|
||||
#######################################################################
|
||||
|
||||
CAT ?= cat
|
||||
CP ?= cp
|
||||
ECHO ?= printf
|
||||
INSTALL ?= install
|
||||
MKDIR ?= mkdir -p
|
||||
RM ?= rm -f
|
||||
RM_REC ?= $(RM) -r
|
||||
ZIP ?= zip -q
|
||||
|
||||
ifeq ($(VERBOSE_BUILD),1)
|
||||
LS := ls -l
|
||||
else
|
||||
LS := true
|
||||
endif
|
||||
|
||||
#######################################################################
|
||||
# Misc stuff - you should never have to edit this #
|
||||
#######################################################################
|
||||
|
||||
EXECUTABLE := $(EXEPRE)scummvm$(EXEEXT)
|
||||
|
||||
include $(srcdir)/Makefile.common
|
||||
|
||||
ENGINE_SUBDIRS_CONFIGURE := $(wildcard $(srcdir)/engines/*/configure.engine)
|
||||
|
||||
config.h:
|
||||
SAVED_ENV_VARS = AR AS ASFLAGS CPPFLAGS CXX CXXFILT CXXFLAGS LD LDFLAGS NM RANLIB SDL_CONFIG STRIP WINDRES WINDRESFLAGS
|
||||
|
||||
# The environment variable PKG_CONFIG_LIBDIR has a different meaning
|
||||
# for pkg-config when it is empty and when it is not defined.
|
||||
# When PKG_CONFIG_LIBDIR is defined but empty, the .pc files cannot
|
||||
# be found because the search path is empty.
|
||||
# Here we make sure not to define PKG_CONFIG_LIBDIR when automatically
|
||||
# running configure and it was not set for the previous run
|
||||
# so pkg-config uses the system default search path for the .pc files.
|
||||
ifneq ($(SAVED_PKG_CONFIG_LIBDIR),unset)
|
||||
SAVED_ENV_VARS += PKG_CONFIG_LIBDIR
|
||||
endif
|
||||
|
||||
# check if configure has been run or has been changed since last run
|
||||
configure.stamp: $(srcdir)/configure $(srcdir)/engines.awk $(ENGINE_SUBDIRS_CONFIGURE)
|
||||
ifeq "$(findstring config.mk,$(MAKEFILE_LIST))" "config.mk"
|
||||
@echo "Running $(srcdir)/configure with the last specified parameters"
|
||||
@sleep 2
|
||||
|
||||
$(foreach VAR,$(SAVED_ENV_VARS),$(VAR)="$(SAVED_$(VAR))") \
|
||||
$(srcdir)/configure $(SAVED_CONFIGFLAGS)
|
||||
else
|
||||
$(error You need to run $(srcdir)/configure before you can run make. Check $(srcdir)/configure --help for a list of parameters)
|
||||
endif
|
||||
|
||||
config.h config.mk engines/plugins_table.h engines/detection_table.h engines/engines.mk: configure.stamp
|
||||
@if ! test -f $@; then \
|
||||
rm -f configure.stamp; \
|
||||
$(MAKE) configure.stamp; \
|
||||
fi
|
||||
|
||||
ifneq ($(origin port_mk), undefined)
|
||||
include $(srcdir)/$(port_mk)
|
||||
endif
|
||||
|
||||
.PHONY: print-dists print-executables print-version print-distversion
|
||||
print-dists:
|
||||
@echo $(DIST_FILES_DOCS) $(DIST_FILES_THEMES) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_ENGINEDATA_BIG) $(DIST_FILES_SOUNDFONTS) $(DIST_FILES_PLATFORM) $(srcdir)/doc
|
||||
|
||||
print-executables:
|
||||
@echo $(if $(DIST_EXECUTABLES),$(DIST_EXECUTABLES),$(EXECUTABLE) $(PLUGINS))
|
||||
|
||||
print-version:
|
||||
@echo $(VERSION)
|
||||
|
||||
print-distversion:
|
||||
@echo $(DISTVERSION)
|
||||
|
||||
devtools/create_project/cmake/build/create_project:
|
||||
cmake -Hdevtools/create_project/cmake -Bdevtools/create_project/cmake/build/
|
||||
cmake --build devtools/create_project/cmake/build/
|
||||
|
||||
CMakeLists.txt: devtools/create_project/cmake/build/create_project config.mk
|
||||
./devtools/create_project/cmake/build/create_project . --cmake $(SAVED_CONFIGFLAGS)
|
||||
|
||||
cmake: CMakeLists.txt
|
||||
cmake -H. -Bbuild
|
||||
cmake --build build
|
||||
|
||||
test-games: $(EXECUTABLE)
|
||||
devtools/run_event_recorder_tests.py --xunit-output=$(EXECUTABLE).test-results.xml
|
||||
471
Makefile.common
Normal file
471
Makefile.common
Normal file
@@ -0,0 +1,471 @@
|
||||
# This file is used by Makefile and declares common build rules,
|
||||
# a list of common object files etc.
|
||||
|
||||
######################################################################
|
||||
# The default build target: just build the scummvm executable
|
||||
######################################################################
|
||||
|
||||
all: $(EXECUTABLE) plugins
|
||||
|
||||
ifdef SPLIT_DWARF
|
||||
all: $(EXECUTABLE).dwp
|
||||
endif
|
||||
|
||||
ifdef USE_PANDOC
|
||||
all: PANDOC_CONVERT
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# Module settings
|
||||
######################################################################
|
||||
|
||||
PLUGINS :=
|
||||
MODULES := test devtools base $(MODULES)
|
||||
|
||||
-include engines/engines.mk
|
||||
|
||||
# After the game specific modules follow the shared modules
|
||||
MODULES += \
|
||||
engines \
|
||||
gui \
|
||||
backends \
|
||||
video \
|
||||
image \
|
||||
graphics \
|
||||
audio \
|
||||
math \
|
||||
common \
|
||||
common/compression \
|
||||
common/formats \
|
||||
po \
|
||||
doc
|
||||
|
||||
ifdef USE_LUA
|
||||
MODULES += common/lua
|
||||
endif
|
||||
|
||||
ifdef USE_MT32EMU
|
||||
MODULES += audio/softsynth/mt32
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# The build rules follow - normally you should have no need to
|
||||
# touch whatever comes after here.
|
||||
######################################################################
|
||||
|
||||
# Concat DEFINES and INCLUDES to form the CPPFLAGS
|
||||
CPPFLAGS := $(DEFINES) $(INCLUDES)
|
||||
|
||||
# Include the build instructions for all modules
|
||||
-include $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES)))
|
||||
|
||||
# Store original info
|
||||
MODULES_ORIG:= $(MODULES)
|
||||
MODULE_DIRS_ORIG := $(MODULE_DIRS)
|
||||
KYRARPG_COMMON_OBJ_ORIG := $(KYRARPG_COMMON_OBJ)
|
||||
|
||||
# Skip rules for these files, by resetting the LOAD_RULES_MK
|
||||
LOAD_RULES_MK :=
|
||||
|
||||
ifdef DETECTION_FULL
|
||||
# Reset detection objects, which uptill now are filled with only
|
||||
# enabled engines.
|
||||
DETECT_OBJS :=
|
||||
|
||||
# Include all engine's module files, which populate DETECT_OBJS
|
||||
-include $(srcdir)/engines/*/module.mk
|
||||
endif
|
||||
|
||||
# Reset stuff
|
||||
MODULES := $(MODULES_ORIG)
|
||||
MODULE :=
|
||||
MODULE_OBJS :=
|
||||
MODULE_DIRS := $(MODULE_DIRS_ORIG)
|
||||
PLUGIN :=
|
||||
KYRARPG_COMMON_OBJ := $(KYRARPG_COMMON_OBJ_ORIG)
|
||||
|
||||
# Enable-rules again
|
||||
LOAD_RULES_MK := 1
|
||||
|
||||
ifneq ($(DETECTION_STATIC), 1)
|
||||
-include $(srcdir)/base/detection/module.mk
|
||||
else
|
||||
MODULE_DIRS += $(sort $(dir $(DETECT_OBJS)))
|
||||
endif
|
||||
|
||||
# Depdir information
|
||||
DEPDIRS = $(addsuffix $(DEPDIR),$(MODULE_DIRS)) dists/$(DEPDIR)
|
||||
DEPFILES =
|
||||
|
||||
# Make base/version.o depend on all other object files. This way if anything is
|
||||
# changed, it causes version.cpp to be recompiled. This in turn ensures that
|
||||
# the build date in gScummVMBuildDate is correct.
|
||||
base/version.o: $(filter-out base/libbase.a,$(OBJS))
|
||||
|
||||
ifdef USE_ELF_LOADER
|
||||
backends/plugins/elf/version.o: $(filter-out base/libbase.a,$(filter-out backends/libbackends.a,$(OBJS)))
|
||||
endif
|
||||
|
||||
# Replace regular output with quiet messages
|
||||
ifneq ($(findstring $(MAKEFLAGS),s),s)
|
||||
ifneq ($(VERBOSE_BUILD),1)
|
||||
ifneq ($(VERBOSE_BUILD),yes)
|
||||
QUIET_CC = @echo ' ' C ' ' $@;
|
||||
QUIET_CXX = @echo ' ' C++ ' ' $@;
|
||||
QUIET_AS = @echo ' ' AS ' ' $@;
|
||||
QUIET_NASM = @echo ' ' NASM ' ' $@;
|
||||
QUIET_PANDOC = @echo ' ' PANDOC ' ' $@;
|
||||
QUIET_AR = @echo ' ' AR ' ' $@;
|
||||
QUIET_RANLIB = @echo ' ' RANLIB ' ' $@;
|
||||
QUIET_PLUGIN = @echo ' ' PLUGIN ' ' $@;
|
||||
QUIET_LINK = @echo ' ' LINK ' ' $@;
|
||||
QUIET_DWP = @echo ' ' DWP ' ' $@;
|
||||
QUIET_WINDRES = @echo ' ' WINDRES '' $@;
|
||||
QUIET_CURL = @echo ' ' CURL ' ' $@;
|
||||
QUIET = @
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
# The build rule for the ScummVM executable
|
||||
$(EXECUTABLE): $(DETECT_OBJS) $(OBJS)
|
||||
+$(QUIET_LINK)$(LD) $(LDFLAGS) $(PRE_OBJS_FLAGS) $+ $(POST_OBJS_FLAGS) $(LIBS) -o $@
|
||||
+$(QUIET)$(LS) $@
|
||||
|
||||
ifdef SPLIT_DWARF
|
||||
%.dwp: %
|
||||
$(QUIET_DWP)$(DWP) -e $< -o $@
|
||||
endif
|
||||
|
||||
# Grab the ScummVM Manual from Read the Docs
|
||||
ifdef USE_CURL
|
||||
DIST_FILES_MANUAL := ScummVM\ Manual\ $(MANUALVERSION).pdf
|
||||
manual:
|
||||
$(QUIET_CURL)$(CURL) -s https://docs.scummvm.org/_/downloads/en/$(MANUALVERSION)/pdf/ --output $(DIST_FILES_MANUAL)
|
||||
else
|
||||
manual:
|
||||
endif
|
||||
|
||||
distclean: clean clean-devtools clean-test
|
||||
$(RM) config.h config.mk config.log configure.stamp engines/engines.mk engines/detection_table.h engines/plugins_table.h "ScummVM Manual"*.pdf
|
||||
|
||||
clean: clean-toplevel
|
||||
clean-toplevel:
|
||||
$(RM_REC) $(DEPDIRS)
|
||||
$(RM) $(OBJS) $(DETECT_OBJS) $(EXECUTABLE) $(PLUGIN_OBJ_FILES)
|
||||
ifdef SPLIT_DWARF
|
||||
$(RM) $(OBJS:.o=.dwo) $(DETECT_OBJS:.o=.dwo)
|
||||
$(RM) $(EXECUTABLE).dwp
|
||||
endif
|
||||
|
||||
|
||||
#
|
||||
# The build rules for object files.
|
||||
#
|
||||
|
||||
base/plugins.o: config.mk
|
||||
|
||||
ifdef CXX_UPDATE_DEP_FLAG
|
||||
|
||||
# Build rule for C++ files. Makes use of CXX_UPDATE_DEP_FLAG for advanced
|
||||
# dependency tracking.
|
||||
%.o: %.c
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CC)$(CC) $(CXX_UPDATE_DEP_FLAG) $(CFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
%.o: %.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
|
||||
|
||||
# Build rules for Objective-C and Objective-C++ files. Strictly speaking, this is for macOS only.
|
||||
%.o: %.mm
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
|
||||
%.o: %.m
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CPPFLAGS) $(OBJCFLAGS) -c $(<) -o $@
|
||||
|
||||
# Build rule for assembler files with preprocessing
|
||||
%.o: %.S
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_AS)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(ASFLAGS) -c $(<) -o $@
|
||||
|
||||
base/version.o: base/version.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET_CXX)$(CXX) $(CXX_UPDATE_DEP_FLAG) $(CXXFLAGS) $(VERFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
|
||||
else
|
||||
|
||||
# Dumb compile rule, for C++ compilers that don't allow dependency tracking or
|
||||
# where it is broken
|
||||
%.o: %.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_CXX)$(CXX) $(CXXFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
|
||||
# Build rule for assembler files with preprocessing
|
||||
%.o: %.S
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_AS)$(CXX) $(ASFLAGS) -c $(<) -o $@
|
||||
|
||||
base/version.o: base/version.cpp
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_CXX)$(CXX) $(CXXFLAGS) $(VERFLAGS) $(CPPFLAGS) -c $(<) -o $@
|
||||
endif
|
||||
|
||||
# Build rule for assembler files
|
||||
%.o: %.s
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_AS)$(AS) $(ASFLAGS) $(<) -o $@
|
||||
|
||||
# Build rule for Windows resource files
|
||||
# This has multiple passes to generate the deps file:
|
||||
# First is to include config.h and config.mk
|
||||
# Second is to process all #include "*.h" form includes into source-tree deps
|
||||
# Third is to process all #include "*.rh" form includes into build-tree deps
|
||||
# Fourth is to process all FILE, ICON, RT_MANIFEST, and DATA lines into source-tree deps
|
||||
# The regexes are portable forms of:
|
||||
# ^#include\s+"([^"]*).h".*$
|
||||
# ^#include\s+"([^"]*).rh".*$
|
||||
# ^(.*\s(FILE|ICON|RT_MANIFEST|DATA))\s+"([^"]*)".*$
|
||||
# The first sed removes winresrc.h (system include) and config.h (not in srcdir, printed
|
||||
# by the echo before sed), the last strips the trailing backslash on the last line.
|
||||
%.o: %.rc
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET)$(MKDIR) $(*D)/$(DEPDIR)
|
||||
$(QUIET)echo "$@: $< config.h config.mk \\" > $(*D)/$(DEPDIR)/scummvm.d
|
||||
$(QUIET)sed -n 's:^\#include[[:space:]][[:space:]]*"\([^"]*\.h\)".*$$: $(srcdir)/\1 \\:p; ' $(<) | \
|
||||
sed '/winresrc\.h\|config\.h/d' >> $(*D)/$(DEPDIR)/scummvm.d
|
||||
$(QUIET)sed -n 's:^\#include[[:space:]][[:space:]]*"\([^"]*\.rh\)".*$$: \1 \\:p; ' $(<) \
|
||||
>> $(*D)/$(DEPDIR)/scummvm.d
|
||||
$(QUIET)sed -n 's:^\(.*[[:space:]]\(FILE\|ICON\|RT_MANIFEST\|DATA\)\)[[:space:]][[:space:]]*"\([^"]*\)".*$$: $(srcdir)/\3 \\:p; ' $(<) | \
|
||||
sed '$$ s/ \\//' >> $(*D)/$(DEPDIR)/scummvm.d
|
||||
$(QUIET_WINDRES)$(WINDRES) $(WINDRESFLAGS) $(CPPFLAGS) $(<) -o $@
|
||||
|
||||
ifdef USE_NASM
|
||||
# Build rule for NASM assembler files
|
||||
%.o: %.asm
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_NASM)$(NASM) $(NASMFLAGS) -o $@ $(<)
|
||||
endif
|
||||
|
||||
# Include the dependency tracking files.
|
||||
-include $(wildcard $(addsuffix /*.d,$(DEPDIRS)))
|
||||
|
||||
# Mark *.d files and most *.mk files as PHONY. This stops make from trying to
|
||||
# recreate them (which it can't), and in particular from looking for potential
|
||||
# source files. This can save quite a bit of disk access time.
|
||||
.PHONY: $(wildcard $(addsuffix /*.d,$(DEPDIRS))) $(addprefix $(srcdir)/, $(addsuffix /module.mk,$(MODULES))) \
|
||||
$(srcdir)/$(port_mk) $(srcdir)/rules.mk
|
||||
|
||||
######################################################################
|
||||
# Get the current version information
|
||||
######################################################################
|
||||
|
||||
# AmigaOS grep command has a problem with "./" in pathnames, so use cat piped into grep instead.
|
||||
VERSION = $(shell cat "${srcdir}/base/internal_version.h" | grep SCUMMVM_VERSION | cut -d\" -f2)
|
||||
VER_MAJOR = $(shell echo $(VERSION) | cut -d. -f 1)
|
||||
VER_MINOR = $(shell echo $(VERSION) | cut -d. -f 2)
|
||||
VER_PATCH = $(shell echo $(VERSION) | cut -d. -f 3 | cut -c1)
|
||||
VER_EXTRA = $(shell echo $(VERSION) | cut -d. -f 3 | cut -c2-)
|
||||
|
||||
ifdef AMIGAOS
|
||||
# AmigaOS needs the date in a specific format for the version cookie.
|
||||
AMIGA_DATE = $(shell gdate '+%d.%m.%Y')
|
||||
VERFLAGS += -DAMIGA_DATE=\"$(AMIGA_DATE)\"
|
||||
endif
|
||||
ifdef MORPHOS
|
||||
# MorphOS needs the date in a specific format for the version cookie.
|
||||
AMIGA_DATE = $(shell date +"%-d.%-m.%Y")
|
||||
VERFLAGS += -DAMIGA_DATE=\"$(AMIGA_DATE)\"
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# Get git's working copy information
|
||||
######################################################################
|
||||
|
||||
ifneq ($(shell cd $(srcdir); git rev-parse --verify HEAD 1>/dev/null 2>&1 || echo "error"),error)
|
||||
GITROOT := $(srcdir)
|
||||
ifeq ($(origin VER_REV), undefined)
|
||||
GIT_VER_MAJOR = $(shell git --version | sed 's/^git version //' | cut -d. -f 1)
|
||||
# Are there uncommitted changes? (describe --dirty is only available since 1.6.6)
|
||||
ifeq ($(GIT_VER_MAJOR),1)
|
||||
VER_DIRTY := $(shell cd $(srcdir); git update-index --refresh --unmerged 1>/dev/null 2>&1; git diff-index --quiet HEAD || echo "-dirty")
|
||||
else
|
||||
GIT_DIRTY_FLAG = --dirty
|
||||
endif
|
||||
# Get the working copy base revision
|
||||
VER_REV := $(shell cd $(srcdir); git describe $(GIT_DIRTY_FLAG) --always --long --match desc/\* | cut -d '-' -f 2-)$(VER_DIRTY)
|
||||
endif
|
||||
else
|
||||
GITROOT := git://github.com/scummvm/scummvm.git
|
||||
endif
|
||||
|
||||
# Define the Subversion revision if available, either autodetected or
|
||||
# specified by the user, but only for base/version.cpp.
|
||||
ifneq ($(origin VER_REV), undefined)
|
||||
VERFLAGS += -DSCUMMVM_REVISION=\"$(VER_REV)\"
|
||||
endif
|
||||
|
||||
######################################################################
|
||||
# Distribution settings
|
||||
######################################################################
|
||||
|
||||
ifeq ($(VER_EXTRA),git)
|
||||
ifeq ($(origin VER_REV), undefined)
|
||||
DISTVERSION = $(shell date '+%Y-%m-%d')
|
||||
else
|
||||
DISTVERSION = git$(VER_REV)
|
||||
endif
|
||||
else
|
||||
DISTVERSION = $(VERSION)
|
||||
# Set the manual version to the release tag, unless a manual value was provided
|
||||
ifeq ($(MANUALVERSION),latest)
|
||||
MANUALVERSION = v$(VERSION)
|
||||
endif
|
||||
endif
|
||||
|
||||
DISTNAME := scummvm-$(DISTVERSION)
|
||||
DISTDIR := dist
|
||||
VERFILE := $(DISTDIR)/$(DISTNAME)/base/internal_version.h
|
||||
|
||||
ifdef USE_PANDOC
|
||||
# Convert README.md and NEWS.md to plain text for any platform that might require it
|
||||
PANDOC_CONVERT: README$(PANDOCEXT) CONTRIBUTING$(PANDOCEXT) NEWS$(PANDOCEXT) doc/de/NEUES$(PANDOCEXT)
|
||||
@sed -i'' -e "s/NEWS.md/NEWS$(PANDOCEXT)/g" README$(PANDOCEXT)
|
||||
@sed -i'' -e "s/CONTRIBUTING.md/CONTRIBUTING$(PANDOCEXT)/g" README$(PANDOCEXT)
|
||||
|
||||
%$(PANDOCEXT): %.md
|
||||
$(QUIET)$(MKDIR) $(*D)
|
||||
$(QUIET_PANDOC)$(PANDOC) -f gfm -t $(PANDOCFORMAT) --metadata pagetitle=$(basename $(notdir $@)) -s -o $@ $<
|
||||
endif
|
||||
|
||||
# TODO git via $(GITROOT)
|
||||
$(VERFILE): $(srcdir)/base/internal_version.h
|
||||
@$(RM_REC) $(DISTDIR)
|
||||
@$(MKDIR) $(DISTDIR)
|
||||
svn export $(SVNROOT) $(DISTDIR)/$(DISTNAME)
|
||||
ifneq ($(origin VER_REV), undefined)
|
||||
@# Use the current SVN revision as a default for the snapshot sources
|
||||
@svn cat $(SVNROOT)/base/internal_version.h | sed -e \
|
||||
"s/^#define SCUMMVM_REVISION$$/#define SCUMMVM_REVISION \"$(VER_REV)\"/g" \
|
||||
> $(VERFILE)
|
||||
endif
|
||||
|
||||
$(DISTDIR)/$(DISTNAME).tar.gz: $(VERFILE)
|
||||
cd $(DISTDIR); tar zcf $(DISTNAME).tar.gz $(DISTNAME)
|
||||
|
||||
$(DISTDIR)/$(DISTNAME).tar.bz2: $(VERFILE)
|
||||
cd $(DISTDIR); tar jcf $(DISTNAME).tar.bz2 $(DISTNAME)
|
||||
|
||||
$(DISTDIR)/$(DISTNAME).zip: $(VERFILE)
|
||||
cd $(DISTDIR); zip -qr9 $(DISTNAME).zip $(DISTNAME)
|
||||
|
||||
dist-src: \
|
||||
$(DISTDIR)/$(DISTNAME).tar.gz \
|
||||
$(DISTDIR)/$(DISTNAME).tar.bz2 \
|
||||
$(DISTDIR)/$(DISTNAME).zip
|
||||
@#RPM-src?
|
||||
@#DEB-src?
|
||||
|
||||
# Common files
|
||||
DIST_FILES_DOCS:=AUTHORS COPYING COPYRIGHT NEWS.md README.md CONTRIBUTING.md
|
||||
DIST_FILES_DOCS+=LICENSES/COPYING.Apache LICENSES/COPYING.BSD LICENSES/COPYING.BSL
|
||||
DIST_FILES_DOCS+=LICENSES/COPYING.GLAD LICENSES/COPYING.ISC LICENSES/COPYING.LGPL LICENSES/COPYING.LUA
|
||||
DIST_FILES_DOCS+=LICENSES/COPYING.MIT LICENSES/COPYING.MKV LICENSES/COPYING.MPL LICENSES/COPYING.OFL LICENSES/COPYING.TINYGL
|
||||
DIST_FILES_DOCS+=LICENSES/CatharonLicense.txt
|
||||
DIST_FILES_DOCS:=$(addprefix $(srcdir)/,$(DIST_FILES_DOCS))
|
||||
ifdef USE_PANDOC
|
||||
DIST_FILES_DOCS+=README$(PANDOCEXT) NEWS$(PANDOCEXT) CONTRIBUTING$(PANDOCEXT)
|
||||
endif
|
||||
ifdef DIST_FILES_MANUAL
|
||||
ifneq ("$(wildcard $(DIST_FILES_MANUAL))","")
|
||||
DIST_FILES_DOCS+=$(DIST_FILES_MANUAL)
|
||||
endif
|
||||
endif
|
||||
|
||||
DIST_FILES_DOCS_languages=cz da de es fr it no-nb se
|
||||
DIST_FILES_DOCS_cz:=$(addprefix $(srcdir)/doc/cz/,PrectiMe)
|
||||
DIST_FILES_DOCS_da:=$(addprefix $(srcdir)/doc/da/,HurtigStart)
|
||||
DIST_FILES_DOCS_de:=$(addprefix $(srcdir)/doc/de/,LIESMICH NEUES.md Schnellstart)
|
||||
ifdef USE_PANDOC
|
||||
DIST_FILES_DOCS_de+=doc/de/NEUES$(PANDOCEXT)
|
||||
endif
|
||||
DIST_FILES_DOCS_es:=$(addprefix $(srcdir)/doc/es/,InicioRapido)
|
||||
DIST_FILES_DOCS_fr:=$(addprefix $(srcdir)/doc/fr/,DemarrageRapide)
|
||||
DIST_FILES_DOCS_it:=$(addprefix $(srcdir)/doc/it/,GuidaRapida)
|
||||
DIST_FILES_DOCS_no-nb:=$(addprefix $(srcdir)/doc/no-nb/,HurtigStart)
|
||||
DIST_FILES_DOCS_se:=$(addprefix $(srcdir)/doc/sv/,LasMig Snabbstart)
|
||||
|
||||
# Themes files
|
||||
DIST_FILES_THEMES=scummmodern.zip scummclassic.zip scummremastered.zip residualvm.zip gui-icons.dat shaders.dat
|
||||
ifdef USE_TRANSLATION
|
||||
DIST_FILES_THEMES+=translations.dat
|
||||
endif
|
||||
DIST_FILES_THEMES:=$(addprefix $(srcdir)/gui/themes/,$(DIST_FILES_THEMES))
|
||||
|
||||
# Networking files
|
||||
DIST_FILES_NETWORKING=
|
||||
ifdef USE_SDL_NET
|
||||
DIST_FILES_NETWORKING:=$(addprefix $(srcdir)/dists/networking/,wwwroot.zip)
|
||||
endif
|
||||
|
||||
# Virtual keyboard files
|
||||
DIST_FILES_VKEYBD=
|
||||
ifdef ENABLE_VKEYBD
|
||||
DIST_FILES_VKEYBD:=$(addprefix $(srcdir)/backends/vkeybd/packs/,vkeybd_default.zip vkeybd_small.zip)
|
||||
endif
|
||||
|
||||
# Engine data files
|
||||
DIST_FILES_ENGINEDATA_BASE_CORE_SOURCE:=$(srcdir)/dists/engine-data/engine_data_core.mk
|
||||
DIST_FILES_LIST=
|
||||
-include $(DIST_FILES_ENGINEDATA_BASE_CORE_SOURCE)
|
||||
DIST_FILES_ENGINEDATA_BASE_CORE:=$(DIST_FILES_LIST)
|
||||
|
||||
DIST_FILES_ENGINEDATA_BASE_SOURCE:=$(srcdir)/dists/engine-data/engine_data.mk
|
||||
DIST_FILES_LIST=
|
||||
-include $(DIST_FILES_ENGINEDATA_BASE_SOURCE)
|
||||
DIST_FILES_ENGINEDATA_BASE:=$(DIST_FILES_LIST)
|
||||
|
||||
DIST_FILES_ENGINEDATA_BASE_BIG_SOURCE:=$(srcdir)/dists/engine-data/engine_data_big.mk
|
||||
DIST_FILES_LIST=
|
||||
-include $(DIST_FILES_ENGINEDATA_BASE_BIG_SOURCE)
|
||||
DIST_FILES_ENGINEDATA_BASE_BIG:=$(DIST_FILES_LIST)
|
||||
|
||||
DIST_FILES_ENGINEDATA:=$(addprefix $(srcdir)/,$(DIST_FILES_ENGINEDATA_BASE_CORE) $(DIST_FILES_ENGINEDATA_BASE))
|
||||
DIST_FILES_ENGINEDATA_BIG:=$(addprefix $(srcdir)/,$(DIST_FILES_ENGINEDATA_BASE_BIG))
|
||||
|
||||
# Shaders: install if USE_OPENGL_SHADERS is defined
|
||||
DIST_FILES_SHADERS=
|
||||
ifneq ($(USE_OPENGL_SHADERS),)
|
||||
ifdef ENABLE_GRIM
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/grim/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_MYST3
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/myst3/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_STARK
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/stark/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_WINTERMUTE
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/wintermute/base/gfx/opengl/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_PLAYGROUND3D
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/playground3d/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_HPL1
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/hpl1/engine/impl/shaders/*)
|
||||
endif
|
||||
ifdef ENABLE_FREESCAPE
|
||||
DIST_FILES_SHADERS+=$(wildcard $(srcdir)/engines/freescape/shaders/*)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Soundfonts
|
||||
DIST_FILES_SOUNDFONTS=
|
||||
ifneq "$(or $(USE_FLUIDSYNTH),$(USE_FLUIDLITE))" ""
|
||||
DIST_FILES_SOUNDFONTS:=$(addprefix $(srcdir)/dists/soundfonts/,Roland_SC-55.sf2)
|
||||
DIST_FILES_DOCS+=$(addprefix $(srcdir)/dists/soundfonts/,COPYRIGHT.Roland_SC-55)
|
||||
endif
|
||||
|
||||
.PHONY: all clean distclean plugins dist-src clean-toplevel manual
|
||||
80
README.md
Normal file
80
README.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# [ScummVM README](https://www.scummvm.org/) · [](https://github.com/scummvm/scummvm/actions/workflows/ci.yml) [](https://translations.scummvm.org/engage/scummvm/?utm_source=widget) [](CONTRIBUTING.md#pull-requests) [](https://www.codacy.com/gh/scummvm/scummvm/dashboard?utm_source=github.com&utm_medium=referral&utm_content=scummvm/scummvm&utm_campaign=Badge_Grade)
|
||||
|
||||
## About ScummVM
|
||||
|
||||
ScummVM allows you to play classic graphic point-and-click adventure games, text adventure games, and RPGs, as long as you already have the game data files. ScummVM replaces the executable files shipped with the games, which means you can now play your favorite games on all your favorite devices.
|
||||
|
||||
So how did ScummVM get its name? Many of the famous LucasArts adventure games, such as Maniac Mansion and the Monkey Island series, were created using a utility called SCUMM (Script Creation Utility for Maniac Mansion). The ‘VM’ in ScummVM stands for Virtual Machine.
|
||||
|
||||
While ScummVM was originally designed to run LucasArts’ SCUMM games, over time support has been added for many other games: see the full list [on our wiki](https://wiki.scummvm.org/index.php?title=Category:Supported_Games). Noteworthy titles include Broken Sword, Myst and Blade Runner, although there are countless other hidden gems to explore.
|
||||
|
||||
For more information, compatibility lists, details on donating, the
|
||||
latest release, progress reports and more, please visit the ScummVM [home
|
||||
page](https://www.scummvm.org/).
|
||||
|
||||
## Quickstart
|
||||
|
||||
For the impatient among you, here is how to get ScummVM running in five simple steps.
|
||||
|
||||
1. Download ScummVM from [our website](https://www.scummvm.org/downloads/) and install it.
|
||||
|
||||
2. Create a directory on your hard drive and copy the game datafiles from the original media to this directory. Repeat this for every game you want to play.
|
||||
|
||||
3. Start ScummVM, choose 'Add game', select the directory containing the game datafiles (do not try to select the datafiles themselves!) and press Choose.
|
||||
|
||||
4. The Game Options dialog opens to allow configuration of various settings for the game. These can be reconfigured at any time, but for now everything should be OK at the default settings.
|
||||
|
||||
5. Select the game you want to play in the list, and press Start. To play a game next time, skip to step 5, unless you want to add more games.
|
||||
|
||||
>
|
||||
> Hint:
|
||||
>
|
||||
> To add multiple games in one go, press and hold the shift key, then click 'Add game' -- the label will change to 'Mass Add' and if you press it, you are again asked to select a directory, only this time ScummVM will search through all subdirectories for supported games.
|
||||
|
||||
|
||||
|
||||
## Reporting a bug
|
||||
|
||||
To report a bug, go to the ScummVM [Issue Tracker](https://bugs.scummvm.org/) and log in with your GitHub account.
|
||||
|
||||
Please make sure the bug is reproducible, and still occurs in the latest git/[Daily build](https://buildbot.scummvm.org/#/dailybuilds) version. Also check the [compatibility list](https://www.scummvm.org/compatibility/) for that game, to ensure the issue is not already known. Please do not report bugs for games that are not listed as completable on the [Supported Games](https://wiki.scummvm.org/index.php?title=Category:Supported_Games) wiki page, or on the compatibility list. We already know those games have bugs!
|
||||
|
||||
Please include the following information in the bug report:
|
||||
|
||||
- ScummVM version (test the latest git/[Daily build](https://buildbot.scummvm.org/#/dailybuilds))
|
||||
- Bug details, including instructions for how to reproduce the bug. If possible, include log files, screenshots, and any other relevant information.
|
||||
- Game language
|
||||
- Game version (for example, talkie or floppy)
|
||||
- Platform and Compiler (for example, Win32, Linux or FreeBSD)
|
||||
- An attached saved game, if possible.
|
||||
- If this bug only occurred recently, include the last version without the bug, and the first version with the bug. That way we can fix it quicker by looking at the changes made.
|
||||
|
||||
Finally, please report each issue separately; do not file multiple issues on the same ticket. It is difficult to track the status of each individual bug when they aren't on their own tickets.
|
||||
|
||||
## Documentation
|
||||
|
||||
### User documentation
|
||||
|
||||
For everything you need to know about how to use ScummVM, see our [user documentation](https://docs.scummvm.org/).
|
||||
|
||||
### The ScummVM Wiki
|
||||
|
||||
[The wiki](https://wiki.scummvm.org/) is the place to go for information about every game supported by ScummVM. If you're a developer, there's also some very handy information in the Developer section.
|
||||
|
||||
### Changelog
|
||||
|
||||
Our extensive change log is available [here](NEWS.md).
|
||||
|
||||
## SAST Tools
|
||||
|
||||
[PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=github&utm_medium=organic&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
|
||||
|
||||
## Credits
|
||||
|
||||
A massive thank you to the entire team for making the ScummVM project possible. See the credits [here](AUTHORS)!
|
||||
|
||||
-----
|
||||
|
||||
> Good Luck and Happy Adventuring\!
|
||||
> The ScummVM team.
|
||||
> <https://www.scummvm.org/>
|
||||
3
TODO
Normal file
3
TODO
Normal file
@@ -0,0 +1,3 @@
|
||||
We moved TODO list to our Wiki. You may find it at the following link:
|
||||
|
||||
https://wiki.scummvm.org/index.php/TODO
|
||||
2328
audio/adlib.cpp
Normal file
2328
audio/adlib.cpp
Normal file
File diff suppressed because it is too large
Load Diff
575
audio/adlib_ctmidi.cpp
Normal file
575
audio/adlib_ctmidi.cpp
Normal file
@@ -0,0 +1,575 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/adlib_ctmidi.h"
|
||||
|
||||
const AdLibIbkInstrumentDefinition MidiDriver_ADLIB_CTMIDI::CTMIDI_INSTRUMENT_BANK[128] = {
|
||||
// 0x00
|
||||
{ 0x01, 0x11, 0x4f, 0x00, 0xf1, 0xd2, 0x51, 0x43, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x02, 0x12, 0x4f, 0x06, 0xf1, 0xd2, 0x51, 0x43, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x11, 0x4a, 0x04, 0xf1, 0xd2, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x03, 0x11, 0x4f, 0x04, 0xf1, 0xd2, 0x53, 0x74, 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0x11, 0x66, 0x00, 0xf1, 0xd2, 0x51, 0xc3, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc0, 0xd2, 0x52, 0x05, 0xf1, 0xd2, 0x53, 0x94, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x12, 0x18, 0x86, 0x00, 0xf3, 0xfc, 0x00, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xd0, 0x12, 0x4e, 0x03, 0xa8, 0x92, 0x32, 0xa7, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x08
|
||||
{ 0xc8, 0xd1, 0x4f, 0x08, 0xf2, 0xf3, 0x64, 0x77, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x33, 0x34, 0x0e, 0x00, 0x01, 0x7d, 0x11, 0x34, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x17, 0x16, 0x50, 0x00, 0xd1, 0xd3, 0x52, 0x92, 0x00, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe7, 0xe1, 0x21, 0x06, 0xf5, 0xf6, 0x77, 0x14, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x95, 0x81, 0x4e, 0x00, 0xda, 0xf9, 0x25, 0x15, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x27, 0x21, 0x1f, 0x03, 0xf5, 0xf5, 0x96, 0x57, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x87, 0xf1, 0x4e, 0x80, 0xb1, 0xe6, 0x33, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x11, 0x87, 0x80, 0xa1, 0x7d, 0x11, 0x43, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x10
|
||||
{ 0x32, 0xb1, 0x8c, 0x03, 0x91, 0xa1, 0x07, 0x19, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0xb4, 0x54, 0x83, 0xf1, 0xf5, 0x07, 0x19, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x24, 0x21, 0x40, 0x53, 0xff, 0xff, 0x0f, 0x0f, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xd2, 0xf1, 0x44, 0x80, 0x91, 0xa1, 0x57, 0x09, 0x01, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0x02, 0x52, 0x88, 0xf0, 0xf0, 0x1f, 0x1f, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x21, 0x32, 0x4f, 0x0b, 0xf2, 0x52, 0x0b, 0x0b, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0xf2, 0x93, 0x07, 0xd8, 0xb3, 0x0b, 0x0b, 0x02, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x20, 0x31, 0x5d, 0x07, 0xf2, 0x52, 0x0b, 0x0b, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x18
|
||||
{ 0x01, 0x01, 0x1b, 0x04, 0xf4, 0xf3, 0x25, 0x46, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x11, 0x01, 0x0f, 0x07, 0xf4, 0xf3, 0x25, 0x46, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0x01, 0x27, 0x00, 0xf1, 0xf4, 0x1f, 0x88, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x12, 0x13, 0x44, 0x03, 0xea, 0xd2, 0x32, 0xe7, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x31, 0x45, 0x00, 0xa4, 0xf5, 0x32, 0xe7, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x21, 0x21, 0x0f, 0x03, 0xf5, 0xf1, 0x17, 0x78, 0x02, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0x20, 0x41, 0x07, 0xd1, 0xc1, 0x34, 0xa5, 0x03, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x10, 0x12, 0x43, 0x02, 0xa7, 0xe3, 0x97, 0xe7, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x20
|
||||
{ 0x20, 0x21, 0x28, 0x01, 0xc5, 0xd2, 0x15, 0xa4, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x21, 0x16, 0x05, 0xf2, 0xf3, 0x9f, 0x78, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x21, 0x11, 0x05, 0x82, 0xf3, 0x9f, 0x78, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x21, 0x21, 0x23, 0x00, 0x73, 0x93, 0x1a, 0x87, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x21, 0x0e, 0x09, 0x62, 0xf3, 0x55, 0x68, 0x02, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x22, 0x0c, 0x00, 0x62, 0xd5, 0xb5, 0x98, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x70, 0x72, 0x93, 0x43, 0x64, 0xa1, 0x43, 0x43, 0x00, 0x00, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x30, 0x32, 0x8d, 0x85, 0x44, 0x92, 0x43, 0x43, 0x02, 0x00, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x28
|
||||
{ 0xe1, 0xe2, 0x4e, 0x00, 0x65, 0x61, 0x43, 0x44, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xa1, 0xa2, 0x8e, 0x05, 0x65, 0x63, 0x43, 0x45, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xb0, 0x61, 0x87, 0x40, 0xd1, 0x62, 0x11, 0x15, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0x20, 0x8a, 0x80, 0xb1, 0xa0, 0x11, 0x15, 0x02, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf1, 0xe2, 0x89, 0x43, 0x73, 0x43, 0x01, 0x05, 0x02, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x21, 0x57, 0x80, 0xf8, 0xf7, 0xf9, 0xe6, 0x03, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x32, 0x01, 0x24, 0x82, 0xf1, 0xf5, 0x35, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x04, 0x00, 0xaa, 0xd2, 0xc8, 0xb3, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x30
|
||||
{ 0xe0, 0xf1, 0x4f, 0x03, 0xd4, 0x55, 0x0b, 0x0b, 0x02, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf0, 0x52, 0x00, 0x96, 0x35, 0x05, 0x01, 0x02, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe1, 0xf1, 0x4f, 0x00, 0x36, 0x45, 0x05, 0x02, 0x02, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe2, 0xe1, 0x48, 0x81, 0x21, 0x41, 0x43, 0x45, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x16, 0x00, 0x41, 0x20, 0x52, 0x72, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x11, 0x00, 0x01, 0xd0, 0x52, 0x72, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x1a, 0x00, 0x61, 0x30, 0x52, 0x73, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x50, 0x50, 0x0b, 0x07, 0x84, 0xa4, 0x4b, 0x99, 0x00, 0x00, 0x0a, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x38
|
||||
{ 0x31, 0x61, 0x1c, 0x84, 0x41, 0x92, 0x0b, 0x3b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xb1, 0x61, 0x1c, 0x05, 0x41, 0x92, 0x1f, 0x3b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x20, 0x21, 0x18, 0x00, 0x52, 0xa2, 0x15, 0x24, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc1, 0xc1, 0x94, 0x84, 0x74, 0xa3, 0xea, 0xf5, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x21, 0x21, 0x28, 0x00, 0x41, 0x81, 0xb4, 0x98, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x21, 0x21, 0x1d, 0x01, 0x51, 0xe1, 0xae, 0x3e, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xe0, 0x93, 0x82, 0x51, 0x81, 0xa6, 0x97, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xe1, 0x93, 0x83, 0x51, 0xe1, 0xa6, 0x97, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x40
|
||||
{ 0xe0, 0xf2, 0x4b, 0x0b, 0xd8, 0xb3, 0x0b, 0x0b, 0x02, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x49, 0x0b, 0xb8, 0xb3, 0x0b, 0x0b, 0x02, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf0, 0x4e, 0x0b, 0x98, 0xc3, 0x0b, 0x0b, 0x01, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x4c, 0x0b, 0x88, 0xd3, 0x0b, 0x0b, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf1, 0xe4, 0xc5, 0x08, 0x7e, 0x8c, 0x17, 0x0e, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x60, 0x72, 0x4f, 0x0a, 0xd8, 0xb3, 0x0b, 0x0b, 0x00, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x72, 0xd1, 0x80, 0xd5, 0x91, 0x19, 0x1b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x32, 0x71, 0xc8, 0x80, 0xd5, 0x73, 0x19, 0x1b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x48
|
||||
{ 0xe2, 0x62, 0x6a, 0x00, 0x9e, 0x55, 0x8f, 0x2a, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0x61, 0xec, 0x00, 0x7e, 0x65, 0x8f, 0x2a, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x62, 0xa2, 0x88, 0x8d, 0x84, 0x75, 0x27, 0x17, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x62, 0xa2, 0x84, 0x8d, 0x84, 0x75, 0x27, 0x17, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe3, 0x62, 0x6d, 0x00, 0x57, 0x57, 0x04, 0x77, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf1, 0xe1, 0x28, 0x00, 0x57, 0x67, 0x34, 0x5d, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xd1, 0x72, 0xc7, 0x03, 0x31, 0x42, 0x0f, 0x09, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf2, 0x72, 0xc7, 0x05, 0x51, 0x42, 0x05, 0x69, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x50
|
||||
{ 0x23, 0x31, 0x4f, 0x06, 0x51, 0x60, 0x5b, 0x25, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x22, 0x31, 0x48, 0x06, 0x31, 0xc0, 0x9b, 0x65, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf1, 0xe1, 0x28, 0x04, 0x57, 0x67, 0x34, 0x0d, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe1, 0xe1, 0x23, 0x00, 0x57, 0x67, 0x04, 0x4d, 0x03, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe2, 0x31, 0x42, 0x12, 0x78, 0xf3, 0x0b, 0x0b, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe2, 0xe2, 0x21, 0x07, 0x11, 0x40, 0x52, 0x73, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x23, 0xa4, 0xc0, 0x00, 0x51, 0x35, 0x07, 0x79, 0x01, 0x02, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x24, 0xa0, 0xc0, 0x01, 0x51, 0x75, 0x07, 0x09, 0x01, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x58
|
||||
{ 0xe0, 0xf0, 0x16, 0x03, 0xb1, 0xe0, 0x51, 0x75, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x03, 0xa4, 0xc0, 0x04, 0x52, 0xf4, 0x03, 0x55, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe1, 0xe1, 0x93, 0x81, 0x31, 0xa1, 0xa6, 0x97, 0x01, 0x01, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0x71, 0xc4, 0x87, 0x10, 0x11, 0x01, 0xc1, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc1, 0xe0, 0x4f, 0x00, 0xb1, 0x12, 0x53, 0x74, 0x02, 0x02, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc0, 0x41, 0x6d, 0x07, 0xf9, 0xf2, 0x21, 0xb3, 0x01, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe3, 0xe2, 0x4c, 0x07, 0x21, 0xa1, 0x43, 0x45, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe3, 0xe2, 0x0c, 0x09, 0x11, 0x80, 0x52, 0x73, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x60
|
||||
{ 0x26, 0x88, 0xc0, 0x00, 0x55, 0xf8, 0x47, 0x19, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x23, 0xe4, 0xd4, 0x00, 0xe5, 0x35, 0x03, 0x65, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x27, 0x32, 0xc0, 0x07, 0x32, 0xa4, 0x62, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xd0, 0x31, 0x4e, 0x03, 0x98, 0xa2, 0x32, 0x47, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0x71, 0xc0, 0x04, 0x93, 0x43, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x1a, 0x82, 0x13, 0x33, 0x52, 0x13, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe0, 0xf1, 0x1a, 0x04, 0x45, 0x32, 0xba, 0x91, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x11, 0x15, 0x18, 0x0d, 0x58, 0xa2, 0x02, 0x72, 0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x68
|
||||
{ 0x10, 0x18, 0x80, 0x45, 0xf1, 0xf1, 0x53, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x17, 0x86, 0x80, 0xa1, 0x7d, 0x11, 0x23, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x10, 0x18, 0x80, 0x40, 0xf1, 0xf6, 0x53, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x34, 0x21, 0x02, 0xf5, 0x93, 0x56, 0xe8, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x03, 0x15, 0x4f, 0x03, 0xf1, 0xd6, 0x39, 0x74, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x22, 0x43, 0x06, 0x6e, 0x8b, 0x17, 0x0c, 0x01, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x31, 0x22, 0x1c, 0x89, 0x61, 0x52, 0x03, 0x67, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x60, 0xf0, 0x0c, 0x89, 0x81, 0x61, 0x03, 0x0c, 0x00, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x70
|
||||
{ 0x27, 0x05, 0x55, 0x05, 0x31, 0xa7, 0x62, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x95, 0x16, 0x81, 0x00, 0xe7, 0x96, 0x01, 0x67, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0c, 0x01, 0x87, 0x80, 0xf0, 0xf2, 0x05, 0x05, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x35, 0x11, 0x44, 0x00, 0xf8, 0xf5, 0xff, 0x75, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x10, 0x10, 0x0b, 0x08, 0xa7, 0xd5, 0xec, 0xf5, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x20, 0x01, 0x0b, 0x07, 0xa8, 0xd6, 0xc8, 0xb7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x01, 0x0b, 0x00, 0x88, 0xd5, 0xc4, 0xb7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0c, 0x10, 0x8f, 0x80, 0x41, 0x33, 0x31, 0x2b, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x78
|
||||
{ 0x17, 0xf7, 0x00, 0x00, 0x3b, 0xea, 0xdf, 0x97, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x12, 0x18, 0x06, 0x09, 0x73, 0x3c, 0x02, 0x74, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x02, 0x08, 0x00, 0x02, 0x3e, 0x14, 0x01, 0xf3, 0x02, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf5, 0xf6, 0xd4, 0x00, 0xeb, 0x45, 0x03, 0x68, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0xca, 0x00, 0xc0, 0xda, 0xb0, 0x71, 0x17, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf0, 0xe2, 0x00, 0xc0, 0x1e, 0x11, 0x11, 0x11, 0x01, 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xe7, 0xe8, 0x00, 0x0e, 0x34, 0x10, 0x00, 0xb2, 0x02, 0x02, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0c, 0x04, 0x00, 0x00, 0xf0, 0xf6, 0xf0, 0xe6, 0x02, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
const int8 MidiDriver_ADLIB_CTMIDI::INSTRUMENT_TRANSPOSE[128] = {
|
||||
0, -12, 12, 0, 0, 12, -12, 0,
|
||||
0, -24, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, -12, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
12, 12, 12, 0, 0, 12, 12, 0,
|
||||
-12, -12, 0, 12, -12, -12, 0, 12,
|
||||
0, 0, -12, 0, 0, 0, 12, 12,
|
||||
0, 0, 12, 0, 0, 0, 12, 0,
|
||||
0, 0, 12, 12, 0, 12, 0, 0,
|
||||
0, 0, -12, -12, 0, 0, -12, -12,
|
||||
0, 0, 0, 0, 0, -12, -19, 0,
|
||||
0, -12, 0, 0, 0, 0, 0, 0,
|
||||
-31, -12, 0, 12, 12, 12, 12, 0,
|
||||
12, 0, 12, 0, 0, 0, 0, 12,
|
||||
0, 0, 0, 0, 12, 12, 12, 0,
|
||||
0, 0, 0, 0, -24, -36, 0, 0
|
||||
};
|
||||
|
||||
const AdLibIbkInstrumentDefinition MidiDriver_ADLIB_CTMIDI::CTMIDI_RHYTHM_BANK[47] = {
|
||||
// 0x00
|
||||
{ 0x00, 0x00, 0x0b, 0x00, 0xa8, 0xd6, 0x4c, 0x45, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0x00, 0x0b, 0x00, 0xaa, 0xd2, 0xc8, 0xb7, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x26, 0x00, 0x00, 0x00, 0xf0, 0xfa, 0xf0, 0xb7, 0x03, 0x03, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x10, 0xc2, 0x07, 0x23, 0xf7, 0xe0, 0xf5, 0x41, 0x02, 0x02, 0x82, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xf2, 0xf1, 0x0a, 0x38, 0x88, 0xad, 0xf4, 0x88, 0x02, 0x02, 0x02, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xd0, 0xc2, 0x81, 0x23, 0xa6, 0xe0, 0xf6, 0x41, 0x02, 0x02, 0x81, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x40, 0xc2, 0x00, 0x23, 0xf5, 0xe0, 0x38, 0x41, 0x00, 0x02, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xc2, 0x03, 0x23, 0xb8, 0xe0, 0xb5, 0x41, 0x01, 0x02, 0x7d, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x08
|
||||
{ 0x40, 0xc2, 0x00, 0x23, 0xf5, 0xe0, 0x38, 0x41, 0x00, 0x02, 0xf1, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xb3, 0x08, 0xc1, 0x88, 0x18, 0xa5, 0x50, 0x01, 0x00, 0xa3, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xc2, 0x00, 0x23, 0xc6, 0xe0, 0x98, 0x41, 0x00, 0x02, 0x83, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xb3, 0x09, 0xc1, 0x86, 0x18, 0xa5, 0x50, 0x01, 0x00, 0xa3, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xc2, 0x00, 0x23, 0xc6, 0xe0, 0x98, 0x41, 0x00, 0x02, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xc2, 0x00, 0x23, 0xc6, 0xe0, 0x98, 0x41, 0x00, 0x02, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x04, 0xc2, 0x0c, 0x23, 0xc5, 0xe0, 0xf6, 0x41, 0x00, 0x02, 0x05, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xc2, 0x00, 0x23, 0xc6, 0xe0, 0x98, 0x41, 0x00, 0x02, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x10
|
||||
{ 0x01, 0xc2, 0x82, 0x23, 0xf6, 0xe0, 0xd5, 0x41, 0x01, 0x02, 0x83, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x03, 0xbf, 0x09, 0xff, 0xe3, 0xd0, 0x97, 0x50, 0x00, 0x00, 0xbb, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0e, 0xbf, 0x07, 0xff, 0xb5, 0xd1, 0x15, 0x50, 0x01, 0x00, 0xbb, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x07, 0xc1, 0x77, 0xd1, 0x73, 0x50, 0x01, 0x00, 0xbb, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0e, 0xf1, 0xc7, 0x38, 0x95, 0xad, 0x78, 0x8e, 0x00, 0x02, 0x02, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x00, 0xff, 0xf8, 0xd2, 0xb6, 0x50, 0x01, 0x00, 0xba, 0x0a, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x0a, 0xc2, 0xc7, 0x23, 0x95, 0xe0, 0x78, 0x41, 0x00, 0x02, 0x7c, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x07, 0xc1, 0xf9, 0xd4, 0xb5, 0x50, 0x00, 0x00, 0xbb, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x18
|
||||
{ 0xd1, 0xc2, 0x05, 0x23, 0xe7, 0xe0, 0x65, 0x41, 0x01, 0x02, 0x9d, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xfe, 0x00, 0x38, 0xe7, 0xa9, 0x94, 0x82, 0x00, 0x02, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x00, 0xff, 0xe7, 0xd8, 0x94, 0x50, 0x00, 0x00, 0xbb, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x00, 0xff, 0x96, 0xd8, 0x67, 0x50, 0x00, 0x00, 0xba, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x00, 0xff, 0xb4, 0xda, 0x26, 0x50, 0x00, 0x00, 0xba, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x00, 0xc1, 0xb4, 0xdb, 0x26, 0x50, 0x00, 0x00, 0xba, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x95, 0x13, 0x81, 0x00, 0xe7, 0x95, 0x01, 0x65, 0x00, 0x00, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x95, 0x13, 0x81, 0x00, 0xe7, 0x95, 0x01, 0x65, 0x00, 0x00, 0x0e, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x20
|
||||
{ 0x10, 0xbf, 0x00, 0xc1, 0x96, 0xde, 0x67, 0x50, 0x00, 0x00, 0xba, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x11, 0xbf, 0x00, 0xff, 0x96, 0xdf, 0x67, 0x50, 0x00, 0x00, 0xba, 0x09, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xbf, 0x0e, 0xc1, 0x58, 0xd0, 0xdc, 0x50, 0x02, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xbf, 0x0e, 0xff, 0x5a, 0xd2, 0xd6, 0x50, 0x02, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x52, 0xbf, 0x07, 0xc1, 0x49, 0xd3, 0x04, 0x50, 0x03, 0x00, 0xbb, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x52, 0xbf, 0x07, 0xc1, 0x41, 0xd4, 0x02, 0x50, 0x03, 0x00, 0xbb, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xbf, 0x0e, 0xff, 0x5a, 0xd5, 0xd6, 0x50, 0x01, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x10, 0xbf, 0x0e, 0xc1, 0x53, 0xd6, 0x9f, 0x50, 0x01, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
// 0x28
|
||||
{ 0x11, 0xfe, 0x00, 0x38, 0xf5, 0xa9, 0x75, 0x80, 0x00, 0x02, 0x02, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x04, 0xc2, 0x00, 0x23, 0xf8, 0xe0, 0xb6, 0x41, 0x01, 0x02, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x04, 0xc2, 0x00, 0x23, 0xf8, 0xe0, 0xb7, 0x41, 0x01, 0x02, 0x03, 0x08, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x01, 0xbf, 0x0b, 0xc1, 0x5e, 0xd8, 0xdc, 0x50, 0x01, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0x00, 0xbf, 0x07, 0xc1, 0x5c, 0xda, 0xdc, 0x50, 0x01, 0x00, 0xba, 0x07, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc5, 0xd5, 0x4f, 0x00, 0xf2, 0xf4, 0x60, 0x7a, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00 },
|
||||
{ 0xc5, 0xd5, 0x4f, 0x00, 0xf2, 0xf2, 0x60, 0x72, 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x00 }
|
||||
};
|
||||
|
||||
const uint8 MidiDriver_ADLIB_CTMIDI::RHYTHM_NOTE_INSTRUMENT_MAP[47] = {
|
||||
0x80, 0x81, 0x82, 0x83, 0x84,
|
||||
0x00, 0x8f, 0x87, 0x8f, 0x89, 0x8f, 0x8b, 0x8f,
|
||||
0x8f, 0x8e, 0x8f, 0x90, 0x00, 0x00, 0x93, 0x00,
|
||||
0xa0, 0x00, 0x00, 0x00, 0x99, 0x9a, 0x9b, 0x9c,
|
||||
0x9d, 0x8f, 0x8f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
|
||||
0xa5, 0xa6, 0x00, 0xa8, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00
|
||||
};
|
||||
|
||||
const uint8 MidiDriver_ADLIB_CTMIDI::RHYTHM_NOTES[47] = {
|
||||
0x2f, 0x24, 0x43, 0x3c, 0x3c,
|
||||
0x3c, 0x30, 0x3c, 0x34, 0x3c, 0x37, 0x3c, 0x3c,
|
||||
0x40, 0x3c, 0x43, 0x3c, 0x3c, 0x3c, 0x3c, 0x3c,
|
||||
0x30, 0x3c, 0x3c, 0x3c, 0x43, 0x3e, 0x43, 0x43,
|
||||
0x3c, 0x3c, 0x37, 0x35, 0x30, 0x3c, 0x3c, 0x4f,
|
||||
0x4f, 0x3c, 0x3c, 0x5b, 0x3c, 0x35, 0x3c, 0x3c,
|
||||
0x4f, 0x4f
|
||||
};
|
||||
|
||||
const uint16 MidiDriver_ADLIB_CTMIDI::CTMIDI_NOTE_FREQUENCIES[768] = {
|
||||
0x0157, 0x0157, 0x0158, 0x0158, 0x0158, 0x0158, 0x0159, 0x0159,
|
||||
0x0159, 0x015A, 0x015A, 0x015A, 0x015B, 0x015B, 0x015B, 0x015C,
|
||||
0x015C, 0x015C, 0x015D, 0x015D, 0x015D, 0x015D, 0x015E, 0x015E,
|
||||
0x015E, 0x015F, 0x015F, 0x015F, 0x0160, 0x0160, 0x0160, 0x0161,
|
||||
0x0161, 0x0161, 0x0162, 0x0162, 0x0162, 0x0163, 0x0163, 0x0163,
|
||||
0x0164, 0x0164, 0x0164, 0x0164, 0x0165, 0x0165, 0x0165, 0x0166,
|
||||
0x0166, 0x0166, 0x0167, 0x0167, 0x0167, 0x0168, 0x0168, 0x0168,
|
||||
0x0169, 0x0169, 0x0169, 0x016A, 0x016A, 0x016A, 0x016B, 0x016B,
|
||||
0x016B, 0x016C, 0x016C, 0x016C, 0x016D, 0x016D, 0x016D, 0x016E,
|
||||
0x016E, 0x016E, 0x016F, 0x016F, 0x016F, 0x0170, 0x0170, 0x0170,
|
||||
0x0171, 0x0171, 0x0171, 0x0172, 0x0172, 0x0172, 0x0173, 0x0173,
|
||||
0x0173, 0x0174, 0x0174, 0x0174, 0x0175, 0x0175, 0x0175, 0x0176,
|
||||
0x0176, 0x0176, 0x0177, 0x0177, 0x0177, 0x0178, 0x0178, 0x0178,
|
||||
0x0179, 0x0179, 0x0179, 0x017A, 0x017A, 0x017A, 0x017B, 0x017B,
|
||||
0x017B, 0x017C, 0x017C, 0x017C, 0x017D, 0x017D, 0x017D, 0x017E,
|
||||
0x017E, 0x017E, 0x017F, 0x017F, 0x0180, 0x0180, 0x0180, 0x0181,
|
||||
0x0181, 0x0181, 0x0182, 0x0182, 0x0182, 0x0183, 0x0183, 0x0183,
|
||||
0x0184, 0x0184, 0x0184, 0x0185, 0x0185, 0x0185, 0x0186, 0x0186,
|
||||
0x0187, 0x0187, 0x0187, 0x0188, 0x0188, 0x0188, 0x0189, 0x0189,
|
||||
0x0189, 0x018A, 0x018A, 0x018A, 0x018B, 0x018B, 0x018B, 0x018C,
|
||||
0x018C, 0x018D, 0x018D, 0x018D, 0x018E, 0x018E, 0x018E, 0x018F,
|
||||
0x018F, 0x018F, 0x0190, 0x0190, 0x0191, 0x0191, 0x0191, 0x0192,
|
||||
0x0192, 0x0192, 0x0193, 0x0193, 0x0193, 0x0194, 0x0194, 0x0195,
|
||||
0x0195, 0x0195, 0x0196, 0x0196, 0x0196, 0x0197, 0x0197, 0x0197,
|
||||
0x0198, 0x0198, 0x0199, 0x0199, 0x0199, 0x019A, 0x019A, 0x019A,
|
||||
0x019B, 0x019B, 0x019C, 0x019C, 0x019C, 0x019D, 0x019D, 0x019D,
|
||||
0x019E, 0x019E, 0x019E, 0x019F, 0x019F, 0x01A0, 0x01A0, 0x01A0,
|
||||
0x01A1, 0x01A1, 0x01A1, 0x01A2, 0x01A2, 0x01A3, 0x01A3, 0x01A3,
|
||||
0x01A4, 0x01A4, 0x01A5, 0x01A5, 0x01A5, 0x01A6, 0x01A6, 0x01A6,
|
||||
0x01A7, 0x01A7, 0x01A8, 0x01A8, 0x01A8, 0x01A9, 0x01A9, 0x01A9,
|
||||
0x01AA, 0x01AA, 0x01AB, 0x01AB, 0x01AB, 0x01AC, 0x01AC, 0x01AD,
|
||||
0x01AD, 0x01AD, 0x01AE, 0x01AE, 0x01AE, 0x01AF, 0x01AF, 0x01B0,
|
||||
0x01B0, 0x01B0, 0x01B1, 0x01B1, 0x01B2, 0x01B2, 0x01B2, 0x01B3,
|
||||
0x01B3, 0x01B4, 0x01B4, 0x01B4, 0x01B5, 0x01B5, 0x01B6, 0x01B6,
|
||||
0x01B6, 0x01B7, 0x01B7, 0x01B8, 0x01B8, 0x01B8, 0x01B9, 0x01B9,
|
||||
0x01BA, 0x01BA, 0x01BA, 0x01BB, 0x01BB, 0x01BC, 0x01BC, 0x01BC,
|
||||
0x01BD, 0x01BD, 0x01BE, 0x01BE, 0x01BE, 0x01BF, 0x01BF, 0x01C0,
|
||||
0x01C0, 0x01C0, 0x01C1, 0x01C1, 0x01C2, 0x01C2, 0x01C2, 0x01C3,
|
||||
0x01C3, 0x01C4, 0x01C4, 0x01C4, 0x01C5, 0x01C5, 0x01C6, 0x01C6,
|
||||
0x01C6, 0x01C7, 0x01C7, 0x01C8, 0x01C8, 0x01C9, 0x01C9, 0x01C9,
|
||||
0x01CA, 0x01CA, 0x01CB, 0x01CB, 0x01CB, 0x01CC, 0x01CC, 0x01CD,
|
||||
0x01CD, 0x01CD, 0x01CE, 0x01CE, 0x01CF, 0x01CF, 0x01D0, 0x01D0,
|
||||
0x01D0, 0x01D1, 0x01D1, 0x01D2, 0x01D2, 0x01D3, 0x01D3, 0x01D3,
|
||||
0x01D4, 0x01D4, 0x01D5, 0x01D5, 0x01D5, 0x01D6, 0x01D6, 0x01D7,
|
||||
0x01D7, 0x01D8, 0x01D8, 0x01D8, 0x01D9, 0x01D9, 0x01DA, 0x01DA,
|
||||
0x01DB, 0x01DB, 0x01DB, 0x01DC, 0x01DC, 0x01DD, 0x01DD, 0x01DE,
|
||||
0x01DE, 0x01DE, 0x01DF, 0x01DF, 0x01E0, 0x01E0, 0x01E1, 0x01E1,
|
||||
0x01E1, 0x01E2, 0x01E2, 0x01E3, 0x01E3, 0x01E4, 0x01E4, 0x01E5,
|
||||
0x01E5, 0x01E5, 0x01E6, 0x01E6, 0x01E7, 0x01E7, 0x01E8, 0x01E8,
|
||||
0x01E8, 0x01E9, 0x01E9, 0x01EA, 0x01EA, 0x01EB, 0x01EB, 0x01EC,
|
||||
0x01EC, 0x01EC, 0x01ED, 0x01ED, 0x01EE, 0x01EE, 0x01EF, 0x01EF,
|
||||
0x01F0, 0x01F0, 0x01F0, 0x01F1, 0x01F1, 0x01F2, 0x01F2, 0x01F3,
|
||||
0x01F3, 0x01F4, 0x01F4, 0x01F5, 0x01F5, 0x01F5, 0x01F6, 0x01F6,
|
||||
0x01F7, 0x01F7, 0x01F8, 0x01F8, 0x01F9, 0x01F9, 0x01FA, 0x01FA,
|
||||
0x01FA, 0x01FB, 0x01FB, 0x01FC, 0x01FC, 0x01FD, 0x01FD, 0x01FE,
|
||||
0x01FE, 0x01FF, 0x01FF, 0x01FF, 0x0200, 0x0200, 0x0201, 0x0201,
|
||||
0x0202, 0x0202, 0x0203, 0x0203, 0x0204, 0x0204, 0x0205, 0x0205,
|
||||
0x0206, 0x0206, 0x0206, 0x0207, 0x0207, 0x0208, 0x0208, 0x0209,
|
||||
0x0209, 0x020A, 0x020A, 0x020B, 0x020B, 0x020C, 0x020C, 0x020D,
|
||||
0x020D, 0x020E, 0x020E, 0x020E, 0x020F, 0x020F, 0x0210, 0x0210,
|
||||
0x0211, 0x0211, 0x0212, 0x0212, 0x0213, 0x0213, 0x0214, 0x0214,
|
||||
0x0215, 0x0215, 0x0216, 0x0216, 0x0217, 0x0217, 0x0218, 0x0218,
|
||||
0x0219, 0x0219, 0x021A, 0x021A, 0x021A, 0x021B, 0x021B, 0x021C,
|
||||
0x021C, 0x021D, 0x021D, 0x021E, 0x021E, 0x021F, 0x021F, 0x0220,
|
||||
0x0220, 0x0221, 0x0221, 0x0222, 0x0222, 0x0223, 0x0223, 0x0224,
|
||||
0x0224, 0x0225, 0x0225, 0x0226, 0x0226, 0x0227, 0x0227, 0x0228,
|
||||
0x0228, 0x0229, 0x0229, 0x022A, 0x022A, 0x022B, 0x022B, 0x022C,
|
||||
0x022C, 0x022D, 0x022D, 0x022E, 0x022E, 0x022F, 0x022F, 0x0230,
|
||||
0x0230, 0x0231, 0x0231, 0x0232, 0x0232, 0x0233, 0x0233, 0x0234,
|
||||
0x0234, 0x0235, 0x0235, 0x0236, 0x0236, 0x0237, 0x0237, 0x0238,
|
||||
0x0238, 0x0239, 0x0239, 0x023A, 0x023B, 0x023B, 0x023C, 0x023C,
|
||||
0x023D, 0x023D, 0x023E, 0x023E, 0x023F, 0x023F, 0x0240, 0x0240,
|
||||
0x0241, 0x0241, 0x0242, 0x0242, 0x0243, 0x0243, 0x0244, 0x0244,
|
||||
0x0245, 0x0245, 0x0246, 0x0246, 0x0247, 0x0248, 0x0248, 0x0249,
|
||||
0x0249, 0x024A, 0x024A, 0x024B, 0x024B, 0x024C, 0x024C, 0x024D,
|
||||
0x024D, 0x024E, 0x024E, 0x024F, 0x024F, 0x0250, 0x0251, 0x0251,
|
||||
0x0252, 0x0252, 0x0253, 0x0253, 0x0254, 0x0254, 0x0255, 0x0255,
|
||||
0x0256, 0x0256, 0x0257, 0x0258, 0x0258, 0x0259, 0x0259, 0x025A,
|
||||
0x025A, 0x025B, 0x025B, 0x025C, 0x025C, 0x025D, 0x025E, 0x025E,
|
||||
0x025F, 0x025F, 0x0260, 0x0260, 0x0261, 0x0261, 0x0262, 0x0262,
|
||||
0x0263, 0x0264, 0x0264, 0x0265, 0x0265, 0x0266, 0x0266, 0x0267,
|
||||
0x0267, 0x0268, 0x0269, 0x0269, 0x026A, 0x026A, 0x026B, 0x026B,
|
||||
0x026C, 0x026C, 0x026D, 0x026E, 0x026E, 0x026F, 0x026F, 0x0270,
|
||||
0x0270, 0x0271, 0x0272, 0x0272, 0x0273, 0x0273, 0x0274, 0x0274,
|
||||
0x0275, 0x0275, 0x0276, 0x0277, 0x0277, 0x0278, 0x0278, 0x0279,
|
||||
0x0279, 0x027A, 0x027B, 0x027B, 0x027C, 0x027C, 0x027D, 0x027D,
|
||||
0x027E, 0x027F, 0x027F, 0x0280, 0x0280, 0x0281, 0x0282, 0x0282,
|
||||
0x0283, 0x0283, 0x0284, 0x0284, 0x0285, 0x0286, 0x0286, 0x0287,
|
||||
0x0287, 0x0288, 0x0289, 0x0289, 0x028A, 0x028A, 0x028B, 0x028B,
|
||||
0x028C, 0x028D, 0x028D, 0x028E, 0x028E, 0x028F, 0x0290, 0x0290,
|
||||
0x0291, 0x0291, 0x0292, 0x0293, 0x0293, 0x0294, 0x0294, 0x0295,
|
||||
0x0296, 0x0296, 0x0297, 0x0297, 0x0298, 0x0299, 0x0299, 0x029A,
|
||||
0x029A, 0x029B, 0x029C, 0x029C, 0x029D, 0x029D, 0x029E, 0x029F,
|
||||
0x029F, 0x02A0, 0x02A0, 0x02A1, 0x02A2, 0x02A2, 0x02A3, 0x02A3,
|
||||
0x02A4, 0x02A5, 0x02A5, 0x02A6, 0x02A6, 0x02A7, 0x02A8, 0x02A8,
|
||||
0x02A9, 0x02AA, 0x02AA, 0x02AB, 0x02AB, 0x02AC, 0x02AD, 0x02AD
|
||||
};
|
||||
|
||||
const int8 MidiDriver_ADLIB_CTMIDI::NOTE_VOLUME_MODIFIER_VALUES[18] = {
|
||||
-63, -16, -8, -6, -4, -3, -2, -1, 0, 2, 4, 5, 6, 7, 8, 9, 10, 12
|
||||
};
|
||||
|
||||
MidiDriver_ADLIB_CTMIDI::MidiDriver_ADLIB_CTMIDI(OPL::Config::OplType oplType, int timerFrequency) :
|
||||
MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
|
||||
memset(_ctmidiInstrumentBank, 0, sizeof(_ctmidiInstrumentBank));
|
||||
memset(_ctmidiRhythmBank, 0, sizeof(_ctmidiRhythmBank));
|
||||
|
||||
_instrumentWriteMode = INSTRUMENT_WRITE_MODE_FIRST_NOTE_ON;
|
||||
_modulationDepth = MODULATION_DEPTH_LOW;
|
||||
_vibratoDepth = VIBRATO_DEPTH_LOW;
|
||||
_rhythmModeRewriteSharedRegister = true;
|
||||
// CTMIDI.DRV actually uses default volume 0x80, which is not a valid MIDI value.
|
||||
// This might cause marginally lower volumes in this implementation if a track
|
||||
// does not set volume controller values.
|
||||
setControllerDefault(MidiDriver_Multisource::CONTROLLER_DEFAULT_VOLUME, 0x7F);
|
||||
}
|
||||
|
||||
int MidiDriver_ADLIB_CTMIDI::open() {
|
||||
// Initialize instrument banks
|
||||
for (int i = 0; i < 128; i++) {
|
||||
CTMIDI_INSTRUMENT_BANK[i].toOplInstrumentDefinition(_ctmidiInstrumentBank[i]);
|
||||
_ctmidiInstrumentBank[i].transpose = INSTRUMENT_TRANSPOSE[i];
|
||||
}
|
||||
|
||||
_instrumentBank = _ctmidiInstrumentBank;
|
||||
|
||||
for (int i = 0; i < 47; i++) {
|
||||
uint8 instrument = RHYTHM_NOTE_INSTRUMENT_MAP[i];
|
||||
uint8 rhythmBankIndex = instrument - 0x80;
|
||||
if (instrument < 0x80 || rhythmBankIndex >= 47) {
|
||||
// Unused or invalid instrument
|
||||
_ctmidiRhythmBank[i].rhythmType = RHYTHM_TYPE_UNDEFINED;
|
||||
} else {
|
||||
CTMIDI_RHYTHM_BANK[rhythmBankIndex].toOplInstrumentDefinition(_ctmidiRhythmBank[i]);
|
||||
_ctmidiRhythmBank[i].rhythmNote = RHYTHM_NOTES[i];
|
||||
}
|
||||
}
|
||||
|
||||
_rhythmBank = _ctmidiRhythmBank;
|
||||
_rhythmBankFirstNote = 35;
|
||||
_rhythmBankLastNote = 75;
|
||||
|
||||
int result = MidiDriver_ADLIB_Multisource::open();
|
||||
|
||||
if (result == 0) {
|
||||
// Rhythm mode is permanently enabled in this driver
|
||||
setRhythmMode(true);
|
||||
initRhythmDefaults();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_CTMIDI::deinitSource(uint8 source) {
|
||||
MidiDriver_ADLIB_Multisource::deinitSource(source);
|
||||
|
||||
// Assume only the music source uses the rhythm channel
|
||||
if (source == 0)
|
||||
initRhythmDefaults();
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_CTMIDI::initRhythmDefaults() {
|
||||
// Write default block / F-num values for the rhythm channels
|
||||
writeRegister(0xA6, 0x00);
|
||||
writeRegister(0xB6, 0x0A);
|
||||
writeRegister(0xA7, 0x0B);
|
||||
writeRegister(0xB7, 0x0A);
|
||||
writeRegister(0xA8, 0x57);
|
||||
writeRegister(0xB8, 0x09);
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_CTMIDI::programChange(uint8 channel, uint8 program, uint8 source) {
|
||||
MidiDriver_ADLIB_Multisource::programChange(channel, program, source);
|
||||
|
||||
// Deallocate all OPL channels from this MIDI channel that are not playing a note.
|
||||
// Note: original code also stops all active notes on this channel. This is
|
||||
// against the MIDI specification and is not implemented here.
|
||||
for (int i = 0; i < _numMelodicChannels; i++) {
|
||||
uint8 oplChannel = _melodicChannels[i];
|
||||
ActiveNote &activeNote = _activeNotes[oplChannel];
|
||||
if (activeNote.channelAllocated && activeNote.noteActive &&
|
||||
activeNote.source == source && activeNote.channel == channel) {
|
||||
activeNote.channelAllocated = false;
|
||||
activeNote.lastWrittenInstrumentId = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 MidiDriver_ADLIB_CTMIDI::allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) {
|
||||
if (channel == MIDI_RHYTHM_CHANNEL) {
|
||||
// For rhythm notes, just use the standard channel allocation
|
||||
return MidiDriver_ADLIB_Multisource::allocateOplChannel(channel, source, instrumentInfo);
|
||||
}
|
||||
|
||||
// 1. Look for an OPL channel already allocated to this MIDI channel, but not playing a note
|
||||
// 2. Look for an unallocated OPL channel
|
||||
// 3. Look for an OPL channel allocated to another MIDI channel, but not playing a note
|
||||
// 4. Look for an OPL channel playing a note for this MIDI channel or a higher MIDI channel,
|
||||
// with the lowest note counter value
|
||||
uint8 alreadyAllocatedChannel = 0xFF, unallocatedChannel = 0xFF, inactiveChannel = 0xFF, lowestCounterChannel = 0xFF;
|
||||
uint32 lowestNoteCounter = 0xFFFFFFFF;
|
||||
for (int i = 0; i < _numMelodicChannels; i++) {
|
||||
uint8 oplChannel = _melodicChannels[i];
|
||||
ActiveNote &activeNote = _activeNotes[oplChannel];
|
||||
if (activeNote.channelAllocated && activeNote.source == source && activeNote.channel == channel && !activeNote.noteActive) {
|
||||
alreadyAllocatedChannel = oplChannel;
|
||||
break;
|
||||
}
|
||||
if (unallocatedChannel == 0xFF && !activeNote.channelAllocated) {
|
||||
unallocatedChannel = oplChannel;
|
||||
}
|
||||
else if (inactiveChannel == 0xFF && activeNote.channelAllocated &&
|
||||
(activeNote.source != source || activeNote.channel != channel) && !activeNote.noteActive) {
|
||||
inactiveChannel = oplChannel;
|
||||
}
|
||||
else if (activeNote.channelAllocated && (activeNote.source != source || activeNote.channel >= channel) &&
|
||||
activeNote.noteActive && lowestNoteCounter > activeNote.noteCounterValue) {
|
||||
lowestCounterChannel = oplChannel;
|
||||
lowestNoteCounter = activeNote.noteCounterValue;
|
||||
}
|
||||
}
|
||||
|
||||
uint8 allocatedChannel = 0xFF;
|
||||
if (alreadyAllocatedChannel != 0xFF) {
|
||||
allocatedChannel = alreadyAllocatedChannel;
|
||||
}
|
||||
else if (unallocatedChannel != 0xFF) {
|
||||
allocatedChannel = unallocatedChannel;
|
||||
}
|
||||
else if (inactiveChannel != 0xFF) {
|
||||
allocatedChannel = inactiveChannel;
|
||||
}
|
||||
else if (lowestCounterChannel != 0xFF) {
|
||||
allocatedChannel = lowestCounterChannel;
|
||||
}
|
||||
|
||||
if (allocatedChannel != 0xFF) {
|
||||
_activeNotes[allocatedChannel].channelAllocated = true;
|
||||
}
|
||||
|
||||
return allocatedChannel;
|
||||
}
|
||||
|
||||
void MidiDriver_ADLIB_CTMIDI::writeFrequency(uint8 oplChannel, OplInstrumentRhythmType rhythmType) {
|
||||
Common::StackLock lock(_activeNotesMutex);
|
||||
|
||||
if (rhythmType == RHYTHM_TYPE_CYMBAL || rhythmType == RHYTHM_TYPE_HI_HAT || rhythmType == RHYTHM_TYPE_SNARE_DRUM)
|
||||
// Frequency is not written for these rhythm instruments
|
||||
return;
|
||||
|
||||
MidiDriver_ADLIB_Multisource::writeFrequency(oplChannel, rhythmType);
|
||||
|
||||
if (rhythmType == RHYTHM_TYPE_TOM_TOM) {
|
||||
// For the tom-tom, write an additional harmonic frequency to OPL channel 7
|
||||
ActiveNote *activeNote = &_activeRhythmNotes[rhythmType - 1];
|
||||
// Transpose the note up by a perfect fifth
|
||||
uint8 oplNote = activeNote->oplNote + 7;
|
||||
if (oplNote > 0x7F)
|
||||
return;
|
||||
oplChannel = 7;
|
||||
|
||||
// Calculate the frequency.
|
||||
uint16 channelOffset = determineChannelRegisterOffset(oplChannel, false);
|
||||
uint16 frequency = calculateFrequency(activeNote->channel, activeNote->source, oplNote);
|
||||
if (frequency == 0xFFFF)
|
||||
return;
|
||||
// Write the low 8 frequency bits.
|
||||
writeRegister(OPL_REGISTER_BASE_FNUMLOW + channelOffset, frequency & 0xFF);
|
||||
// Write the high 2 frequency bits and block.
|
||||
writeRegister(OPL_REGISTER_BASE_FNUMHIGH_BLOCK_KEYON + channelOffset, frequency >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
uint16 MidiDriver_ADLIB_CTMIDI::calculateFrequency(uint8 channel, uint8 source, uint8 note) {
|
||||
int32 noteValue = note << 6;
|
||||
noteValue += (((int32)_controlData[source][channel].pitchBend) - 0x2000) >> 6;
|
||||
// Transpose down 1 octave
|
||||
noteValue -= 0x300;
|
||||
if (noteValue < 0 || noteValue >= 0x1800)
|
||||
// Note value is out of range. Note should not be played.
|
||||
return 0xFFFF;
|
||||
|
||||
uint16 block = noteValue / 0x300;
|
||||
uint16 fnum = CTMIDI_NOTE_FREQUENCIES[noteValue % 0x300];
|
||||
|
||||
return (block << 10) | fnum;
|
||||
}
|
||||
|
||||
uint8 MidiDriver_ADLIB_CTMIDI::calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity,
|
||||
const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) {
|
||||
if ((instrumentDef.rhythmType == RHYTHM_TYPE_UNDEFINED || instrumentDef.rhythmType == RHYTHM_TYPE_BASS_DRUM) &&
|
||||
operatorNum == 0) {
|
||||
// Original code does not apply velocity and channel volume to additive operators,
|
||||
// which is probably a bug.
|
||||
return instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F;
|
||||
}
|
||||
|
||||
// Determine a modifier value for the instrument definition operator volume
|
||||
// based on the note velocity and MIDI channel volume
|
||||
uint16 volumeModifierIndex = (((velocity << 1) | 1) * (_controlData[source][channel].volume << 1)) >> 11;
|
||||
volumeModifierIndex = (volumeModifierIndex >> 1) + (volumeModifierIndex & 0x1);
|
||||
assert(volumeModifierIndex < 18); // (volumeModifierIndex >= 0 implied by uint16)
|
||||
int8 volumeModifier = NOTE_VOLUME_MODIFIER_VALUES[volumeModifierIndex];
|
||||
|
||||
uint8 operatorVolume = 0x3F - (instrumentDef.getOperatorDefinition(operatorNum).level & 0x3F);
|
||||
int16 volume = operatorVolume + volumeModifier;
|
||||
|
||||
return 0x3F - CLIP(volume, (int16)0, (int16)0x3F);
|
||||
}
|
||||
73
audio/adlib_ctmidi.h
Normal file
73
audio/adlib_ctmidi.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_ADLIB_CTMIDI_H
|
||||
#define AUDIO_ADLIB_CTMIDI_H
|
||||
|
||||
#include "audio/adlib_ms.h"
|
||||
|
||||
/**
|
||||
* General MIDI AdLib driver based on CTMIDI.DRV version 1.04
|
||||
*/
|
||||
class MidiDriver_ADLIB_CTMIDI : public MidiDriver_ADLIB_Multisource {
|
||||
public:
|
||||
// Instrument bank from CTMIDI.DRV. Transpose is stored separately.
|
||||
static const AdLibIbkInstrumentDefinition CTMIDI_INSTRUMENT_BANK[128];
|
||||
// Instrument transpose parameter values
|
||||
static const int8 INSTRUMENT_TRANSPOSE[128];
|
||||
|
||||
// Rhythm bank from CTMIDI.DRV. OPL note is stored separately.
|
||||
static const AdLibIbkInstrumentDefinition CTMIDI_RHYTHM_BANK[47];
|
||||
// Mapping from MIDI note value to rhythm instrument
|
||||
static const uint8 RHYTHM_NOTE_INSTRUMENT_MAP[47];
|
||||
// The OPL note to play for each rhythm instrument
|
||||
static const uint8 RHYTHM_NOTES[47];
|
||||
|
||||
// F-num lookup table from CTMIDI.DRV
|
||||
static const uint16 CTMIDI_NOTE_FREQUENCIES[768];
|
||||
// Volume modifier lookup table
|
||||
static const int8 NOTE_VOLUME_MODIFIER_VALUES[18];
|
||||
|
||||
MidiDriver_ADLIB_CTMIDI(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
|
||||
~MidiDriver_ADLIB_CTMIDI() { };
|
||||
|
||||
int open() override;
|
||||
void deinitSource(uint8 source) override;
|
||||
|
||||
protected:
|
||||
// Instrument bank after conversion
|
||||
OplInstrumentDefinition _ctmidiInstrumentBank[128];
|
||||
// Rhythm bank after conversion
|
||||
OplInstrumentDefinition _ctmidiRhythmBank[47];
|
||||
|
||||
/**
|
||||
* Sets the block / F-num registers for the rhythm instruments to default
|
||||
* values.
|
||||
*/
|
||||
void initRhythmDefaults();
|
||||
void programChange(uint8 channel, uint8 program, uint8 source) override;
|
||||
uint8 allocateOplChannel(uint8 channel, uint8 source, InstrumentInfo &instrumentInfo) override;
|
||||
void writeFrequency(uint8 oplChannel, OplInstrumentRhythmType rhythmType) override;
|
||||
uint16 calculateFrequency(uint8 channel, uint8 source, uint8 note) override;
|
||||
uint8 calculateUnscaledVolume(uint8 channel, uint8 source, uint8 velocity, const OplInstrumentDefinition &instrumentDef, uint8 operatorNum) override;
|
||||
};
|
||||
|
||||
#endif
|
||||
130
audio/adlib_hmisos.cpp
Normal file
130
audio/adlib_hmisos.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/adlib_hmisos.h"
|
||||
|
||||
const byte MidiDriver_ADLIB_HMISOS::INSTRUMENT_BANK_SIGNATURE[8] = { 0x00, 0x00, 'A', 'D', 'L', 'I', 'B', '-' };
|
||||
|
||||
MidiDriver_ADLIB_HMISOS::MidiDriver_ADLIB_HMISOS(OPL::Config::OplType oplType, int timerFrequency) :
|
||||
MidiDriver_ADLIB_Multisource(oplType, timerFrequency) {
|
||||
memset(_sosInstrumentBank, 0, sizeof(_sosInstrumentBank));
|
||||
memset(_sosRhythmBank, 0, sizeof(_sosRhythmBank));
|
||||
|
||||
_instrumentWriteMode = INSTRUMENT_WRITE_MODE_NOTE_ON;
|
||||
_modulationDepth = MODULATION_DEPTH_LOW;
|
||||
_vibratoDepth = VIBRATO_DEPTH_LOW;
|
||||
_rhythmModeRewriteSharedRegister = true;
|
||||
}
|
||||
|
||||
int MidiDriver_ADLIB_HMISOS::open() {
|
||||
|
||||
return MidiDriver_ADLIB_Multisource::open();
|
||||
}
|
||||
|
||||
bool MidiDriver_ADLIB_HMISOS::loadInstrumentBanks(Common::SeekableReadStream *instrumentBankStream, Common::SeekableReadStream *rhythmBankStream) {
|
||||
int numInstruments = loadInstrumentBank(instrumentBankStream, false);
|
||||
if (numInstruments < 0)
|
||||
return false;
|
||||
_instrumentBank = _sosInstrumentBank;
|
||||
|
||||
if (rhythmBankStream != nullptr) {
|
||||
numInstruments = loadInstrumentBank(rhythmBankStream, true);
|
||||
if (numInstruments < 0)
|
||||
return false;
|
||||
_rhythmBank = _sosRhythmBank;
|
||||
_rhythmBankFirstNote = 0;
|
||||
_rhythmBankLastNote = numInstruments;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int MidiDriver_ADLIB_HMISOS::loadInstrumentBank(Common::SeekableReadStream *stream, bool rhythmBank) {
|
||||
OplInstrumentDefinition *instrumentBank = rhythmBank ? _sosRhythmBank : _sosInstrumentBank;
|
||||
|
||||
byte signature[8];
|
||||
stream->read(signature, 8);
|
||||
|
||||
if (memcmp(INSTRUMENT_BANK_SIGNATURE, signature, 8)) {
|
||||
warning("Invalid HMI SOS AdLib instrument bank signature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint16 instrumentsUsed = stream->readUint16LE();
|
||||
if (instrumentsUsed > 128)
|
||||
instrumentsUsed = 128;
|
||||
//uint16 instrumentsTotal = stream->readUint16LE();
|
||||
stream->skip(2);
|
||||
uint32 namesOffset = stream->readUint32LE();
|
||||
uint32 instDataOffset = stream->readUint32LE();
|
||||
|
||||
stream->seek(namesOffset, SEEK_SET);
|
||||
byte rhythmNotes[128];
|
||||
for (uint16 i = 0; i < instrumentsUsed; i++) {
|
||||
stream->skip(2); // Instrument data offset
|
||||
// The flags byte is used to specify the rhythm note
|
||||
rhythmNotes[i] = stream->readByte();
|
||||
stream->skip(9); // Instrument name
|
||||
}
|
||||
|
||||
stream->seek(instDataOffset, SEEK_SET);
|
||||
AdLibBnkInstrumentDefinition bnkInstDef;
|
||||
for (uint16 i = 0; i < instrumentsUsed; i++) {
|
||||
stream->skip(2); // Instrument type and voice number
|
||||
|
||||
bnkInstDef.operator0.keyScalingLevel = stream->readByte();
|
||||
bnkInstDef.operator0.frequencyMultiplier = stream->readByte();
|
||||
bnkInstDef.operator0.feedback = stream->readByte();
|
||||
bnkInstDef.operator0.attack = stream->readByte();
|
||||
bnkInstDef.operator0.sustain = stream->readByte();
|
||||
bnkInstDef.operator0.envelopeGainType = stream->readByte();
|
||||
bnkInstDef.operator0.decay = stream->readByte();
|
||||
bnkInstDef.operator0.release = stream->readByte();
|
||||
bnkInstDef.operator0.level = stream->readByte();
|
||||
bnkInstDef.operator0.amplitudeModulation = stream->readByte();
|
||||
bnkInstDef.operator0.vibrato = stream->readByte();
|
||||
bnkInstDef.operator0.keyScalingRate = stream->readByte();
|
||||
// Connection bit in SOS BNK is not flipped
|
||||
bnkInstDef.operator0.connection = stream->readByte() == 0 ? 1 : 0;
|
||||
|
||||
bnkInstDef.operator1.keyScalingLevel = stream->readByte();
|
||||
bnkInstDef.operator1.frequencyMultiplier = stream->readByte();
|
||||
bnkInstDef.operator1.feedback = stream->readByte();
|
||||
bnkInstDef.operator1.attack = stream->readByte();
|
||||
bnkInstDef.operator1.sustain = stream->readByte();
|
||||
bnkInstDef.operator1.envelopeGainType = stream->readByte();
|
||||
bnkInstDef.operator1.decay = stream->readByte();
|
||||
bnkInstDef.operator1.release = stream->readByte();
|
||||
bnkInstDef.operator1.level = stream->readByte();
|
||||
bnkInstDef.operator1.amplitudeModulation = stream->readByte();
|
||||
bnkInstDef.operator1.vibrato = stream->readByte();
|
||||
bnkInstDef.operator1.keyScalingRate = stream->readByte();
|
||||
bnkInstDef.operator1.connection = stream->readByte() == 0 ? 1 : 0;
|
||||
|
||||
bnkInstDef.waveformSelect0 = stream->readByte();
|
||||
bnkInstDef.waveformSelect1 = stream->readByte();
|
||||
|
||||
bnkInstDef.toOplInstrumentDefinition(instrumentBank[i]);
|
||||
instrumentBank[i].rhythmNote = rhythmNotes[i];
|
||||
}
|
||||
|
||||
return instrumentsUsed;
|
||||
}
|
||||
57
audio/adlib_hmisos.h
Normal file
57
audio/adlib_hmisos.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_ADLIB_HMISOS_H
|
||||
#define AUDIO_ADLIB_HMISOS_H
|
||||
|
||||
#include "audio/adlib_ms.h"
|
||||
|
||||
#include "common/stream.h"
|
||||
|
||||
/**
|
||||
* General MIDI AdLib driver based on HMI Sound Operating System version dated 1994-11-27
|
||||
* (as used by The Riddle Of Master Lu).
|
||||
*
|
||||
* Currently, only instrument bank loading is implemented.
|
||||
* TODO:
|
||||
* - Frequency calculation
|
||||
* - Level calculation
|
||||
* - Channel allocation
|
||||
* - Controller 0x66 (pitch bend range)
|
||||
*/
|
||||
class MidiDriver_ADLIB_HMISOS : public MidiDriver_ADLIB_Multisource {
|
||||
public:
|
||||
static const byte INSTRUMENT_BANK_SIGNATURE[8];
|
||||
|
||||
MidiDriver_ADLIB_HMISOS(OPL::Config::OplType oplType, int timerFrequency = OPL::OPL::kDefaultCallbackFrequency);
|
||||
~MidiDriver_ADLIB_HMISOS() {};
|
||||
|
||||
int open() override;
|
||||
bool loadInstrumentBanks(Common::SeekableReadStream *instrumentBankStream, Common::SeekableReadStream *rhythmBankStream = nullptr);
|
||||
|
||||
protected:
|
||||
int loadInstrumentBank(Common::SeekableReadStream *instrumentBankStream, bool rhythmBank);
|
||||
|
||||
OplInstrumentDefinition _sosInstrumentBank[128];
|
||||
OplInstrumentDefinition _sosRhythmBank[128];
|
||||
};
|
||||
|
||||
#endif
|
||||
2102
audio/adlib_ms.cpp
Normal file
2102
audio/adlib_ms.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1290
audio/adlib_ms.h
Normal file
1290
audio/adlib_ms.h
Normal file
File diff suppressed because it is too large
Load Diff
358
audio/alsa_opl.cpp
Normal file
358
audio/alsa_opl.cpp
Normal file
@@ -0,0 +1,358 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/* OPL implementation for hardware OPL using ALSA Direct FM API.
|
||||
*
|
||||
* Caveats and limitations:
|
||||
* - Pretends to be a softsynth (emitting silence).
|
||||
* - Dual OPL2 mode requires OPL3 hardware.
|
||||
* - Every register write leads to a series of register writes on the hardware,
|
||||
* due to the lack of direct register access in the ALSA Direct FM API.
|
||||
* - No timers
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_ALLOW_ALL
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/str.h"
|
||||
#include "audio/fmopl.h"
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include <sound/asound_fm.h>
|
||||
|
||||
namespace OPL {
|
||||
namespace ALSA {
|
||||
|
||||
class OPL : public ::OPL::OPL, public Audio::RealChip {
|
||||
private:
|
||||
enum {
|
||||
kOpl2Voices = 9,
|
||||
kVoices = 18,
|
||||
kOpl2Operators = 18,
|
||||
kOperators = 36
|
||||
};
|
||||
|
||||
Config::OplType _type;
|
||||
int _iface;
|
||||
snd_hwdep_t *_opl;
|
||||
snd_dm_fm_voice _oper[kOperators];
|
||||
snd_dm_fm_note _voice[kVoices];
|
||||
snd_dm_fm_params _params;
|
||||
int index[2];
|
||||
static const int voiceToOper0[kVoices];
|
||||
static const int regOffsetToOper[0x20];
|
||||
|
||||
void writeOplReg(int c, int r, int v);
|
||||
void clear();
|
||||
|
||||
public:
|
||||
OPL(Config::OplType type);
|
||||
~OPL();
|
||||
|
||||
bool init();
|
||||
void reset();
|
||||
|
||||
void write(int a, int v);
|
||||
|
||||
void writeReg(int r, int v);
|
||||
};
|
||||
|
||||
const int OPL::voiceToOper0[OPL::kVoices] =
|
||||
{ 0, 1, 2, 6, 7, 8, 12, 13, 14, 18, 19, 20, 24, 25, 26, 30, 31, 32 };
|
||||
|
||||
const int OPL::regOffsetToOper[0x20] =
|
||||
{ 0, 1, 2, 3, 4, 5, -1, -1, 6, 7, 8, 9, 10, 11, -1, -1,
|
||||
12, 13, 14, 15, 16, 17, -1, -1, -1, -1, -1, -1, -1, -1, -1 };
|
||||
|
||||
OPL::OPL(Config::OplType type) : _type(type), _opl(nullptr), _iface(0) {
|
||||
}
|
||||
|
||||
OPL::~OPL() {
|
||||
stop();
|
||||
|
||||
if (_opl) {
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
|
||||
snd_hwdep_close(_opl);
|
||||
}
|
||||
}
|
||||
|
||||
void OPL::clear() {
|
||||
index[0] = index[1] = 0;
|
||||
|
||||
memset(_oper, 0, sizeof(_oper));
|
||||
memset(_voice, 0, sizeof(_voice));
|
||||
memset(&_params, 0, sizeof(_params));
|
||||
|
||||
for (int i = 0; i < kOperators; ++i) {
|
||||
_oper[i].op = (i / 3) % 2;
|
||||
_oper[i].voice = (i / 6) * 3 + (i % 3);
|
||||
}
|
||||
|
||||
for (int i = 0; i < kVoices; ++i)
|
||||
_voice[i].voice = i;
|
||||
|
||||
// For OPL3 hardware we need to set up the panning in OPL2 modes
|
||||
if (_iface == SND_HWDEP_IFACE_OPL3) {
|
||||
if (_type == Config::kDualOpl2) {
|
||||
for (int i = 0; i < kOpl2Operators; ++i)
|
||||
_oper[i].left = 1; // FIXME below
|
||||
for (int i = kOpl2Operators; i < kOperators; ++i)
|
||||
_oper[i].right = 1;
|
||||
} else if (_type == Config::kOpl2) {
|
||||
for (int i = 0; i < kOpl2Operators; ++i) {
|
||||
_oper[i].left = 1;
|
||||
_oper[i].right = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OPL::init() {
|
||||
clear();
|
||||
|
||||
int card = -1;
|
||||
snd_ctl_t *ctl;
|
||||
snd_hwdep_info_t *info;
|
||||
snd_hwdep_info_alloca(&info);
|
||||
|
||||
int iface = SND_HWDEP_IFACE_OPL3;
|
||||
if (_type == Config::kOpl2)
|
||||
iface = SND_HWDEP_IFACE_OPL2;
|
||||
|
||||
// Look for OPL hwdep interface
|
||||
while (!snd_card_next(&card) && card >= 0) {
|
||||
int dev = -1;
|
||||
Common::String name = Common::String::format("hw:%d", card);
|
||||
|
||||
if (snd_ctl_open(&ctl, name.c_str(), 0) < 0)
|
||||
continue;
|
||||
|
||||
while (!snd_ctl_hwdep_next_device(ctl, &dev) && dev >= 0) {
|
||||
name = Common::String::format("hw:%d,%d", card, dev);
|
||||
|
||||
if (snd_hwdep_open(&_opl, name.c_str(), SND_HWDEP_OPEN_WRITE) < 0)
|
||||
continue;
|
||||
|
||||
if (!snd_hwdep_info(_opl, info)) {
|
||||
int found = snd_hwdep_info_get_iface(info);
|
||||
// OPL3 can be used for (Dual) OPL2 mode
|
||||
if (found == iface || found == SND_HWDEP_IFACE_OPL3) {
|
||||
snd_ctl_close(ctl);
|
||||
_iface = found;
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrong interface, try next device
|
||||
snd_hwdep_close(_opl);
|
||||
_opl = nullptr;
|
||||
}
|
||||
|
||||
snd_ctl_close(ctl);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void OPL::reset() {
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_RESET, nullptr);
|
||||
if (_iface == SND_HWDEP_IFACE_OPL3)
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_MODE, (void *)SNDRV_DM_FM_MODE_OPL3);
|
||||
|
||||
clear();
|
||||
|
||||
// Sync up with the hardware
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
|
||||
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kVoices : kOpl2Voices); ++i)
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[i]);
|
||||
for (uint i = 0; i < (_iface == SND_HWDEP_IFACE_OPL3 ? kOperators : kOpl2Operators); ++i)
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[i]);
|
||||
}
|
||||
|
||||
void OPL::write(int port, int val) {
|
||||
val &= 0xff;
|
||||
int chip = (port & 2) >> 1;
|
||||
|
||||
if (port & 1) {
|
||||
switch(_type) {
|
||||
case Config::kOpl2:
|
||||
writeOplReg(0, index[0], val);
|
||||
break;
|
||||
case Config::kDualOpl2:
|
||||
if (port & 8) {
|
||||
writeOplReg(0, index[0], val);
|
||||
writeOplReg(1, index[1], val);
|
||||
} else
|
||||
writeOplReg(chip, index[chip], val);
|
||||
break;
|
||||
case Config::kOpl3:
|
||||
writeOplReg(chip, index[chip], val);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch(_type) {
|
||||
case Config::kOpl2:
|
||||
index[0] = val;
|
||||
break;
|
||||
case Config::kDualOpl2:
|
||||
if (port & 8) {
|
||||
index[0] = val;
|
||||
index[1] = val;
|
||||
} else
|
||||
index[chip] = val;
|
||||
break;
|
||||
case Config::kOpl3:
|
||||
index[chip] = val;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OPL::writeReg(int r, int v) {
|
||||
switch (_type) {
|
||||
case Config::kOpl2:
|
||||
writeOplReg(0, r, v);
|
||||
break;
|
||||
case Config::kDualOpl2:
|
||||
writeOplReg(0, r, v);
|
||||
writeOplReg(1, r, v);
|
||||
break;
|
||||
case Config::kOpl3:
|
||||
writeOplReg(r >= 0x100, r & 0xff, v);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void OPL::writeOplReg(int c, int r, int v) {
|
||||
if (r == 0x04 && c == 1 && _type == Config::kOpl3) {
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_CONNECTION, reinterpret_cast<void *>(v & 0x3f));
|
||||
} else if (r == 0x08 && c == 0) {
|
||||
_params.kbd_split = (v >> 6) & 0x1;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
|
||||
} else if (r == 0xbd && c == 0) {
|
||||
_params.hihat = v & 0x1;
|
||||
_params.cymbal = (v >> 1) & 0x1;
|
||||
_params.tomtom = (v >> 2) & 0x1;
|
||||
_params.snare = (v >> 3) & 0x1;
|
||||
_params.bass = (v >> 4) & 0x1;
|
||||
_params.rhythm = (v >> 5) & 0x1;
|
||||
_params.vib_depth = (v >> 6) & 0x1;
|
||||
_params.am_depth = (v >> 7) & 0x1;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_PARAMS, (void *)&_params);
|
||||
} else if (r < 0xa0 || r >= 0xe0) {
|
||||
// Operator
|
||||
int idx = regOffsetToOper[r & 0x1f];
|
||||
|
||||
if (idx == -1)
|
||||
return;
|
||||
|
||||
if (c == 1)
|
||||
idx += kOpl2Operators;
|
||||
|
||||
switch (r & 0xf0) {
|
||||
case 0x20:
|
||||
case 0x30:
|
||||
_oper[idx].harmonic = v & 0xf;
|
||||
_oper[idx].kbd_scale = (v >> 4) & 0x1;
|
||||
_oper[idx].do_sustain = (v >> 5) & 0x1;
|
||||
_oper[idx].vibrato = (v >> 6) & 0x1;
|
||||
_oper[idx].am = (v >> 7) & 0x1;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
|
||||
break;
|
||||
case 0x40:
|
||||
case 0x50:
|
||||
_oper[idx].volume = ~v & 0x3f;
|
||||
_oper[idx].scale_level = (v >> 6) & 0x3;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
|
||||
break;
|
||||
case 0x60:
|
||||
case 0x70:
|
||||
_oper[idx].decay = v & 0xf;
|
||||
_oper[idx].attack = (v >> 4) & 0xf;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
|
||||
break;
|
||||
case 0x80:
|
||||
case 0x90:
|
||||
_oper[idx].release = v & 0xf;
|
||||
_oper[idx].sustain = (v >> 4) & 0xf;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
|
||||
break;
|
||||
case 0xe0:
|
||||
case 0xf0:
|
||||
_oper[idx].waveform = v & (_type == Config::kOpl3 ? 0x7 : 0x3);
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[idx]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Voice
|
||||
int idx = r & 0xf;
|
||||
|
||||
if (idx >= kOpl2Voices)
|
||||
return;
|
||||
|
||||
if (c == 1)
|
||||
idx += kOpl2Voices;
|
||||
|
||||
int opIdx = voiceToOper0[idx];
|
||||
|
||||
switch (r & 0xf0) {
|
||||
case 0xa0:
|
||||
_voice[idx].fnum = (_voice[idx].fnum & 0x300) | (v & 0xff);
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
|
||||
break;
|
||||
case 0xb0:
|
||||
_voice[idx].fnum = ((v << 8) & 0x300) | (_voice[idx].fnum & 0xff);
|
||||
_voice[idx].octave = (v >> 2) & 0x7;
|
||||
_voice[idx].key_on = (v >> 5) & 0x1;
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_PLAY_NOTE, (void *)&_voice[idx]);
|
||||
break;
|
||||
case 0xc0:
|
||||
_oper[opIdx].connection = _oper[opIdx + 3].connection = v & 0x1;
|
||||
_oper[opIdx].feedback = _oper[opIdx + 3].feedback = (v >> 1) & 0x7;
|
||||
if (_type == Config::kOpl3) {
|
||||
_oper[opIdx].left = _oper[opIdx + 3].left = (v >> 4) & 0x1;
|
||||
_oper[opIdx].right = _oper[opIdx + 3].right = (v >> 5) & 0x1;
|
||||
}
|
||||
snd_hwdep_ioctl(_opl, SNDRV_DM_FM_IOCTL_SET_VOICE, (void *)&_oper[opIdx]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OPL *create(Config::OplType type) {
|
||||
return new OPL(type);
|
||||
}
|
||||
|
||||
} // End of namespace ALSA
|
||||
} // End of namespace OPL
|
||||
516
audio/audiostream.cpp
Normal file
516
audio/audiostream.cpp
Normal file
@@ -0,0 +1,516 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/file.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/flac.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/quicktime.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/vorbis.h"
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
struct StreamFileFormat {
|
||||
/** Decodername */
|
||||
const char *decoderName;
|
||||
const char *fileExtension;
|
||||
/**
|
||||
* Pointer to a function which tries to open a file of type StreamFormat.
|
||||
* Return NULL in case of an error (invalid/nonexisting file).
|
||||
*/
|
||||
SeekableAudioStream *(*openStreamFile)(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
};
|
||||
|
||||
static const StreamFileFormat STREAM_FILEFORMATS[] = {
|
||||
/* decoderName, fileExt, openStreamFunction */
|
||||
#ifdef USE_FLAC
|
||||
{ "FLAC", ".flac", makeFLACStream },
|
||||
{ "FLAC", ".fla", makeFLACStream },
|
||||
#endif
|
||||
#ifdef USE_VORBIS
|
||||
{ "Ogg Vorbis", ".ogg", makeVorbisStream },
|
||||
#endif
|
||||
#ifdef USE_MAD
|
||||
{ "MPEG Layer 3", ".mp3", makeMP3Stream },
|
||||
#endif
|
||||
{ "MPEG-4 Audio", ".m4a", makeQuickTimeStream },
|
||||
{ "WAV", ".wav", makeWAVStream },
|
||||
};
|
||||
|
||||
SeekableAudioStream *SeekableAudioStream::openStreamFile(const Common::Path &basename) {
|
||||
SeekableAudioStream *stream = nullptr;
|
||||
Common::File *fileHandle = new Common::File();
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(STREAM_FILEFORMATS); ++i) {
|
||||
Common::Path filename = basename.append(STREAM_FILEFORMATS[i].fileExtension);
|
||||
fileHandle->open(filename);
|
||||
if (fileHandle->isOpen()) {
|
||||
// Create the stream object
|
||||
stream = STREAM_FILEFORMATS[i].openStreamFile(fileHandle, DisposeAfterUse::YES);
|
||||
fileHandle = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete fileHandle;
|
||||
|
||||
if (stream == nullptr)
|
||||
debug(1, "SeekableAudioStream::openStreamFile: Could not open compressed AudioFile %s", basename.toString(Common::Path::kNativeSeparator).c_str());
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- LoopingAudioStream ---
|
||||
#pragma mark -
|
||||
|
||||
LoopingAudioStream::LoopingAudioStream(Common::DisposablePtr<RewindableAudioStream>&& stream, uint loops, bool rewind)
|
||||
: _parent(Common::move(stream)), _loops(loops), _completeIterations(0) {
|
||||
assert(_parent);
|
||||
|
||||
if (rewind && !_parent->rewind()) {
|
||||
// TODO: Properly indicate error
|
||||
_loops = _completeIterations = 1;
|
||||
}
|
||||
if (_parent->endOfStream()) {
|
||||
// Apparently this is an empty stream
|
||||
_loops = _completeIterations = 1;
|
||||
}
|
||||
}
|
||||
|
||||
LoopingAudioStream::LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse, bool rewind)
|
||||
: LoopingAudioStream(Common::move(Common::DisposablePtr<RewindableAudioStream>(stream, disposeAfterUse)), loops, rewind) {}
|
||||
|
||||
int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if ((_loops && _completeIterations == _loops) || !numSamples)
|
||||
return 0;
|
||||
|
||||
int samplesRead = _parent->readBuffer(buffer, numSamples);
|
||||
|
||||
if (_parent->endOfStream()) {
|
||||
++_completeIterations;
|
||||
if (_completeIterations == _loops)
|
||||
return samplesRead;
|
||||
|
||||
const int remainingSamples = numSamples - samplesRead;
|
||||
|
||||
if (!_parent->rewind()) {
|
||||
// TODO: Properly indicate error
|
||||
_loops = _completeIterations;
|
||||
return samplesRead;
|
||||
}
|
||||
if (_parent->endOfStream()) {
|
||||
// Apparently this is an empty stream
|
||||
_loops = _completeIterations;
|
||||
}
|
||||
|
||||
return samplesRead + readBuffer(buffer + samplesRead, remainingSamples);
|
||||
}
|
||||
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
bool LoopingAudioStream::endOfData() const {
|
||||
return (_loops != 0 && _completeIterations == _loops) || _parent->endOfData();
|
||||
}
|
||||
|
||||
bool LoopingAudioStream::endOfStream() const {
|
||||
return _loops != 0 && _completeIterations == _loops;
|
||||
}
|
||||
|
||||
AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops) {
|
||||
if (loops != 1)
|
||||
return new LoopingAudioStream(stream, loops);
|
||||
else
|
||||
return stream;
|
||||
}
|
||||
|
||||
AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops) {
|
||||
if (!start.totalNumberOfFrames() && (!end.totalNumberOfFrames() || end == stream->getLength())) {
|
||||
return makeLoopingAudioStream(stream, loops);
|
||||
} else {
|
||||
if (!end.totalNumberOfFrames())
|
||||
end = stream->getLength();
|
||||
|
||||
if (start >= end) {
|
||||
warning("makeLoopingAudioStream: start (%d) >= end (%d)", start.msecs(), end.msecs());
|
||||
delete stream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return makeLoopingAudioStream(new SubSeekableAudioStream(stream, start, end), loops);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- SubLoopingAudioStream ---
|
||||
#pragma mark -
|
||||
|
||||
SubLoopingAudioStream::SubLoopingAudioStream(SeekableAudioStream *stream,
|
||||
uint loops,
|
||||
const Timestamp loopStart,
|
||||
const Timestamp loopEnd,
|
||||
DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _parent(stream, disposeAfterUse), _loops(loops), _completeIterations(0),
|
||||
_pos(0, getRate() * (isStereo() ? 2 : 1)),
|
||||
_loopStart(convertTimeToStreamPos(loopStart, getRate(), isStereo())),
|
||||
_loopEnd(convertTimeToStreamPos(loopEnd, getRate(), isStereo())) {
|
||||
assert(loopStart < loopEnd);
|
||||
assert(stream);
|
||||
|
||||
if (!_parent->rewind())
|
||||
_loops = _completeIterations = 1;
|
||||
}
|
||||
|
||||
int SubLoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
if ((_loops && _completeIterations == _loops) || !numSamples)
|
||||
return 0;
|
||||
|
||||
int framesLeft = MIN(_loopEnd.frameDiff(_pos), numSamples);
|
||||
int framesRead = _parent->readBuffer(buffer, framesLeft);
|
||||
_pos = _pos.addFrames(framesRead);
|
||||
|
||||
if (framesRead < framesLeft && _parent->endOfStream()) {
|
||||
// TODO: Proper error indication.
|
||||
if (!_completeIterations)
|
||||
_completeIterations = 1;
|
||||
_loops = _completeIterations;
|
||||
return framesRead;
|
||||
} else if (_pos == _loopEnd) {
|
||||
++_completeIterations;
|
||||
if (_completeIterations == _loops)
|
||||
return framesRead;
|
||||
|
||||
if (!_parent->seek(_loopStart)) {
|
||||
// TODO: Proper error indication.
|
||||
_loops = _completeIterations;
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
_pos = _loopStart;
|
||||
framesLeft = numSamples - framesLeft;
|
||||
return framesRead + readBuffer(buffer + framesRead, framesLeft);
|
||||
} else {
|
||||
return framesRead;
|
||||
}
|
||||
}
|
||||
|
||||
bool SubLoopingAudioStream::endOfData() const {
|
||||
// We're out of data if this stream is finished or the parent
|
||||
// has run out of data for now.
|
||||
return (_loops != 0 && _completeIterations == _loops) || _parent->endOfData();
|
||||
}
|
||||
|
||||
bool SubLoopingAudioStream::endOfStream() const {
|
||||
// The end of the stream has been reached only when we've gone
|
||||
// through all the iterations.
|
||||
return _loops != 0 && _completeIterations == _loops;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- SubSeekableAudioStream ---
|
||||
#pragma mark -
|
||||
|
||||
SubSeekableAudioStream::SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _parent(parent, disposeAfterUse),
|
||||
_start(convertTimeToStreamPos(start, getRate(), isStereo())),
|
||||
_pos(0, getRate() * (isStereo() ? 2 : 1)),
|
||||
_length(convertTimeToStreamPos(end, getRate(), isStereo()) - _start) {
|
||||
|
||||
assert(_length.totalNumberOfFrames() % (isStereo() ? 2 : 1) == 0);
|
||||
_parent->seek(_start);
|
||||
}
|
||||
|
||||
int SubSeekableAudioStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int framesLeft = MIN(_length.frameDiff(_pos), numSamples);
|
||||
int framesRead = _parent->readBuffer(buffer, framesLeft);
|
||||
_pos = _pos.addFrames(framesRead);
|
||||
return framesRead;
|
||||
}
|
||||
|
||||
bool SubSeekableAudioStream::seek(const Timestamp &where) {
|
||||
_pos = convertTimeToStreamPos(where, getRate(), isStereo());
|
||||
if (_pos > _length) {
|
||||
_pos = _length;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_parent->seek(_pos + _start)) {
|
||||
return true;
|
||||
} else {
|
||||
_pos = _length;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Queueing audio stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
void QueuingAudioStream::queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags) {
|
||||
AudioStream *stream = makeRawStream(data, size, getRate(), flags, disposeAfterUse);
|
||||
queueAudioStream(stream, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
|
||||
class QueuingAudioStreamImpl : public QueuingAudioStream {
|
||||
private:
|
||||
/**
|
||||
* We queue a number of (pointers to) audio stream objects.
|
||||
* In addition, we need to remember for each stream whether
|
||||
* to dispose it after all data has been read from it.
|
||||
* Hence, we don't store pointers to stream objects directly,
|
||||
* but rather StreamHolder structs.
|
||||
*/
|
||||
struct StreamHolder {
|
||||
AudioStream *_stream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
StreamHolder(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _stream(stream),
|
||||
_disposeAfterUse(disposeAfterUse) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* The sampling rate of this audio stream.
|
||||
*/
|
||||
const int _rate;
|
||||
|
||||
/**
|
||||
* Whether this audio stream is mono (=false) or stereo (=true).
|
||||
*/
|
||||
const int _stereo;
|
||||
|
||||
/**
|
||||
* This flag is set by the finish() method only. See there for more details.
|
||||
*/
|
||||
bool _finished;
|
||||
|
||||
/**
|
||||
* A mutex to avoid access problems (causing e.g. corruption of
|
||||
* the linked list) in thread aware environments.
|
||||
*/
|
||||
Common::Mutex _mutex;
|
||||
|
||||
/**
|
||||
* The queue of audio streams.
|
||||
*/
|
||||
Common::Queue<StreamHolder> _queue;
|
||||
|
||||
public:
|
||||
QueuingAudioStreamImpl(int rate, bool stereo)
|
||||
: _rate(rate), _stereo(stereo), _finished(false) {}
|
||||
~QueuingAudioStreamImpl();
|
||||
|
||||
// Implement the AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
bool isStereo() const override { return _stereo; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
bool endOfData() const override {
|
||||
Common::StackLock lock(_mutex);
|
||||
return _queue.empty() || _queue.front()._stream->endOfData();
|
||||
}
|
||||
|
||||
bool endOfStream() const override {
|
||||
Common::StackLock lock(_mutex);
|
||||
return _finished && _queue.empty();
|
||||
}
|
||||
|
||||
// Implement the QueuingAudioStream API
|
||||
void queueAudioStream(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse) override;
|
||||
|
||||
void finish() override {
|
||||
Common::StackLock lock(_mutex);
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
uint32 numQueuedStreams() const override {
|
||||
Common::StackLock lock(_mutex);
|
||||
return _queue.size();
|
||||
}
|
||||
};
|
||||
|
||||
QueuingAudioStreamImpl::~QueuingAudioStreamImpl() {
|
||||
while (!_queue.empty()) {
|
||||
StreamHolder tmp = _queue.pop();
|
||||
if (tmp._disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete tmp._stream;
|
||||
}
|
||||
}
|
||||
|
||||
void QueuingAudioStreamImpl::queueAudioStream(AudioStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
assert(!_finished);
|
||||
if ((stream->getRate() != getRate()) || (stream->isStereo() != isStereo()))
|
||||
error("QueuingAudioStreamImpl::queueAudioStream: stream has mismatched parameters");
|
||||
|
||||
Common::StackLock lock(_mutex);
|
||||
_queue.push(StreamHolder(stream, disposeAfterUse));
|
||||
}
|
||||
|
||||
int QueuingAudioStreamImpl::readBuffer(int16 *buffer, const int numSamples) {
|
||||
Common::StackLock lock(_mutex);
|
||||
int samplesDecoded = 0;
|
||||
|
||||
while (samplesDecoded < numSamples && !_queue.empty()) {
|
||||
AudioStream *stream = _queue.front()._stream;
|
||||
samplesDecoded += stream->readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded);
|
||||
|
||||
// Done with the stream completely
|
||||
if (stream->endOfStream()) {
|
||||
StreamHolder tmp = _queue.pop();
|
||||
if (tmp._disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Done with data but not the stream, bail out
|
||||
if (stream->endOfData())
|
||||
break;
|
||||
}
|
||||
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo) {
|
||||
return new QueuingAudioStreamImpl(rate, stereo);
|
||||
}
|
||||
|
||||
Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo) {
|
||||
Timestamp result(where.convertToFramerate(rate * (isStereo ? 2 : 1)));
|
||||
|
||||
// When the Stream is a stereo stream, we have to assure
|
||||
// that the sample position is an even number.
|
||||
if (isStereo && (result.totalNumberOfFrames() & 1))
|
||||
result = result.addFrames(-1); // We cut off one sample here.
|
||||
|
||||
// Since Timestamp allows sub-frame-precision it might lead to odd behaviors
|
||||
// when we would just return result.
|
||||
//
|
||||
// An example is when converting the timestamp 500ms to a 11025 Hz based
|
||||
// stream. It would have an internal frame counter of 5512.5. Now when
|
||||
// doing calculations at frame precision, this might lead to unexpected
|
||||
// results: The frame difference between a timestamp 1000ms and the above
|
||||
// mentioned timestamp (both with 11025 as framerate) would be 5512,
|
||||
// instead of 5513, which is what a frame-precision based code would expect.
|
||||
//
|
||||
// By creating a new Timestamp with the given parameters, we create a
|
||||
// Timestamp with frame-precision, which just drops a sub-frame-precision
|
||||
// information (i.e. rounds down).
|
||||
return Timestamp(result.secs(), result.numberOfFrames(), result.framerate());
|
||||
}
|
||||
|
||||
/**
|
||||
* An AudioStream wrapper that cuts off the amount of samples read after a
|
||||
* given time length is reached.
|
||||
*/
|
||||
class LimitingAudioStream : public AudioStream {
|
||||
public:
|
||||
LimitingAudioStream(AudioStream *parentStream, const Audio::Timestamp &length, DisposeAfterUse::Flag disposeAfterUse) :
|
||||
_parentStream(parentStream), _samplesRead(0), _disposeAfterUse(disposeAfterUse),
|
||||
_totalSamples(length.convertToFramerate(getRate()).totalNumberOfFrames() * getChannels()) {}
|
||||
|
||||
~LimitingAudioStream() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _parentStream;
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
// Cap us off so we don't read past _totalSamples
|
||||
int samplesRead = _parentStream->readBuffer(buffer, MIN<int>(numSamples, _totalSamples - _samplesRead));
|
||||
_samplesRead += samplesRead;
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
bool endOfData() const override { return _parentStream->endOfData() || reachedLimit(); }
|
||||
bool endOfStream() const override { return _parentStream->endOfStream() || reachedLimit(); }
|
||||
bool isStereo() const override { return _parentStream->isStereo(); }
|
||||
int getRate() const override { return _parentStream->getRate(); }
|
||||
|
||||
private:
|
||||
int getChannels() const { return isStereo() ? 2 : 1; }
|
||||
bool reachedLimit() const { return _samplesRead >= _totalSamples; }
|
||||
|
||||
AudioStream *_parentStream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
uint32 _totalSamples, _samplesRead;
|
||||
};
|
||||
|
||||
AudioStream *makeLimitingAudioStream(AudioStream *parentStream, const Timestamp &length, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return new LimitingAudioStream(parentStream, length, disposeAfterUse);
|
||||
}
|
||||
|
||||
/**
|
||||
* An AudioStream that plays nothing and immediately returns that
|
||||
* the endOfStream() has been reached
|
||||
*/
|
||||
class NullAudioStream : public AudioStream {
|
||||
public:
|
||||
bool isStereo() const override { return false; }
|
||||
int getRate() const override;
|
||||
int readBuffer(int16 *data, const int numSamples) override { return 0; }
|
||||
bool endOfData() const override { return true; }
|
||||
};
|
||||
|
||||
int NullAudioStream::getRate() const {
|
||||
return g_system->getMixer()->getOutputRate();
|
||||
}
|
||||
|
||||
AudioStream *makeNullAudioStream() {
|
||||
return new NullAudioStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* An AudioStream that just returns silent samples and runs infinitely.
|
||||
*/
|
||||
class SilentAudioStream : public AudioStream {
|
||||
public:
|
||||
SilentAudioStream(int rate, bool stereo) : _rate(rate), _isStereo(stereo) {}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
memset(buffer, 0, numSamples * 2);
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
bool endOfData() const override { return false; } // it never ends!
|
||||
bool isStereo() const override { return _isStereo; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
private:
|
||||
int _rate;
|
||||
bool _isStereo;
|
||||
};
|
||||
|
||||
AudioStream *makeSilentAudioStream(int rate, bool stereo) {
|
||||
return new SilentAudioStream(rate, stereo);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
523
audio/audiostream.h
Normal file
523
audio/audiostream.h
Normal file
@@ -0,0 +1,523 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_AUDIOSTREAM_H
|
||||
#define AUDIO_AUDIOSTREAM_H
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include "audio/timestamp.h"
|
||||
|
||||
namespace Common {
|
||||
class Path;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
/**
|
||||
* @defgroup audio_audiostream Audio streams
|
||||
* @ingroup audio
|
||||
*
|
||||
* @brief API for managing audio input streams.
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generic audio input stream. Subclasses of this are used to feed arbitrary
|
||||
* sampled audio data into ScummVM's audio mixer.
|
||||
*/
|
||||
class AudioStream {
|
||||
public:
|
||||
virtual ~AudioStream() {}
|
||||
|
||||
/**
|
||||
* Fill the given buffer with up to @p numSamples samples.
|
||||
*
|
||||
* Data must be in native endianness, 16 bits per sample, signed. For stereo
|
||||
* stream, the buffer will be filled with interleaved left and right channel
|
||||
* samples, starting with the left sample. Furthermore, the samples in the
|
||||
* left and right are summed up. So if you request 4 samples from a stereo
|
||||
* stream, you will get a total of two left channel and two right channel
|
||||
* samples.
|
||||
*
|
||||
* @return The actual number of samples read, or -1 if a critical error occurred.
|
||||
*
|
||||
* @note You *must* check whether the returned value is less than what you requested.
|
||||
* This indicates that the stream is fully used up.
|
||||
*
|
||||
*/
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples) = 0;
|
||||
|
||||
/** Check whether this is a stereo stream. */
|
||||
virtual bool isStereo() const = 0;
|
||||
|
||||
/** Sample rate of the stream. */
|
||||
virtual int getRate() const = 0;
|
||||
|
||||
/**
|
||||
* Check whether end of data has been reached.
|
||||
*
|
||||
* If this returns true, it indicates that at this time there is no data
|
||||
* available in the stream. However, there might be more data in the future.
|
||||
*
|
||||
* This is used by e.g. a rate converter to decide whether to keep on
|
||||
* converting data or to stop.
|
||||
*/
|
||||
virtual bool endOfData() const = 0;
|
||||
|
||||
/**
|
||||
* Check whether end of stream has been reached.
|
||||
*
|
||||
* If this returns true, it indicates that all data in this stream is used up
|
||||
* and no additional data will appear in it in the future.
|
||||
*
|
||||
* This is used by the mixer to decide whether a given stream shall be
|
||||
* removed from the list of active streams (and thus be destroyed).
|
||||
* By default, this maps to endOfData().
|
||||
*/
|
||||
virtual bool endOfStream() const { return endOfData(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* A rewindable audio stream.
|
||||
*
|
||||
* This allows for resetting the AudioStream to its initial state.
|
||||
* Note that rewinding itself is not required to be working when the stream
|
||||
* is being played by the mixer.
|
||||
*/
|
||||
class RewindableAudioStream : public virtual AudioStream {
|
||||
public:
|
||||
/**
|
||||
* Rewind the stream to its start.
|
||||
*
|
||||
* @return True on success, false otherwise.
|
||||
*/
|
||||
virtual bool rewind() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic loopable audio stream. Subclasses of this are used to feed
|
||||
* looping sampled audio data into ScummVM's audio mixer.
|
||||
*/
|
||||
class LoopableAudioStream : public virtual AudioStream {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Return the number of loops that the stream has played.
|
||||
*/
|
||||
virtual uint getCompleteIterations() const = 0;
|
||||
|
||||
/**
|
||||
* Set the number of remaining loops the stream should play
|
||||
* before stopping.
|
||||
*/
|
||||
virtual void setRemainingIterations(uint loops) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A looping audio stream.
|
||||
*
|
||||
* This object does nothing besides using a RewindableAudioStream
|
||||
* to play a stream in a loop.
|
||||
*/
|
||||
class LoopingAudioStream : public LoopableAudioStream {
|
||||
public:
|
||||
/**
|
||||
* Create a looping audio stream object.
|
||||
*
|
||||
* On creation of the LoopingAudioStream object, the underlying stream will be rewound.
|
||||
*
|
||||
* @see makeLoopingAudioStream
|
||||
*
|
||||
* @param stream The stream to loop.
|
||||
* @param loops How often to loop (0 = infinite).
|
||||
* @param disposeAfterUse Destroy the stream after the LoopingAudioStream has finished playback.
|
||||
* @param rewind If true, rewind the underlying stream.
|
||||
*/
|
||||
LoopingAudioStream(RewindableAudioStream *stream, uint loops, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, bool rewind = true);
|
||||
|
||||
/**
|
||||
* Create a looping audio stream object.
|
||||
*
|
||||
* On creation of the LoopingAudioStream object, the underlying stream will be rewound.
|
||||
*
|
||||
* @see makeLoopingAudioStream
|
||||
*
|
||||
* @param stream The stream to loop.
|
||||
* @param loops How often to loop (0 = infinite).
|
||||
* @param disposeAfterUse Destroy the stream after the LoopingAudioStream has finished playback.
|
||||
* @param rewind If true, rewind the underlying stream.
|
||||
*/
|
||||
LoopingAudioStream(Common::DisposablePtr<RewindableAudioStream>&& stream, uint loops, bool rewind = true);
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
bool endOfData() const;
|
||||
bool endOfStream() const;
|
||||
|
||||
bool isStereo() const { return _parent->isStereo(); }
|
||||
int getRate() const { return _parent->getRate(); }
|
||||
|
||||
uint getCompleteIterations() const { return _completeIterations; }
|
||||
void setRemainingIterations(uint loops) { _loops = _completeIterations + loops; }
|
||||
private:
|
||||
Common::DisposablePtr<RewindableAudioStream> _parent;
|
||||
|
||||
uint _loops;
|
||||
uint _completeIterations;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper functionality to efficiently create a stream that might be looped.
|
||||
*
|
||||
* This function does not return a LoopingAudioStream, because it does
|
||||
* not create one when the loop count is "1". This allows to keep the runtime
|
||||
* overhead down when the code does not require any functionality that is only offered
|
||||
* by LoopingAudioStream.
|
||||
*
|
||||
* @param stream The stream to loop (will be automatically destroyed, when the looping is done).
|
||||
* @param loops How often to loop (0 = infinite).
|
||||
*
|
||||
* @return A new AudioStream that offers the desired functionality.
|
||||
*/
|
||||
AudioStream *makeLoopingAudioStream(RewindableAudioStream *stream, uint loops);
|
||||
|
||||
/**
|
||||
* A seekable audio stream.
|
||||
*
|
||||
* Subclasses of this class implement an interface for seeking.
|
||||
* The seeking itself is not required to be working while the stream
|
||||
* is being played by the mixer.
|
||||
*/
|
||||
class SeekableAudioStream : public virtual RewindableAudioStream {
|
||||
public:
|
||||
/**
|
||||
* Attempt to load a file by trying all available formats.
|
||||
*
|
||||
* In case of an error, the file handle will be closed, but deleting
|
||||
* it is still the responsibility of the caller.
|
||||
*
|
||||
* @param basename File name without an extension.
|
||||
*
|
||||
* @return A SeekableAudioStream ready to use in case of success.
|
||||
* NULL in case of an error (e.g. invalid/non-existing file).
|
||||
*/
|
||||
static SeekableAudioStream *openStreamFile(const Common::Path &basename);
|
||||
|
||||
/**
|
||||
* Seek to a given offset in the stream.
|
||||
*
|
||||
* @param where Offset in milliseconds.
|
||||
*
|
||||
* @return True on success, false on failure.
|
||||
*/
|
||||
bool seek(uint32 where) {
|
||||
return seek(Timestamp(where, getRate()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek to a given offset in the stream.
|
||||
*
|
||||
* @param where Offset as a timestamp.
|
||||
*
|
||||
* @return True on success, false on failure.
|
||||
*/
|
||||
virtual bool seek(const Timestamp &where) = 0;
|
||||
|
||||
/**
|
||||
* Return the length of the stream.
|
||||
*
|
||||
* @return Length as a timestamp.
|
||||
*/
|
||||
virtual Timestamp getLength() const = 0;
|
||||
|
||||
virtual bool rewind() { return seek(0); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrapper functionality to efficiently create a stream that might be looped
|
||||
* in a certain interval.
|
||||
*
|
||||
* This automatically starts the stream at time "start"!
|
||||
*
|
||||
* This function does not return a LoopingAudioStream, because it does
|
||||
* not create one when the loop count is "1". This allows to keep the runtime
|
||||
* overhead down when the code does not require any functionality that is only offered
|
||||
* by LoopingAudioStream.
|
||||
*
|
||||
* @param stream The stream to loop (will be automatically destroyed when the looping is done).
|
||||
* @param start Start time of the stream interval to be looped.
|
||||
* @param end End of the stream interval to be looped (a zero time means till the end).
|
||||
* @param loops How often to loop (0 = infinite).
|
||||
*
|
||||
* @return A new AudioStream that offers the desired functionality.
|
||||
*/
|
||||
AudioStream *makeLoopingAudioStream(SeekableAudioStream *stream, Timestamp start, Timestamp end, uint loops);
|
||||
|
||||
/**
|
||||
* A looping audio stream that features looping of a nested part of the
|
||||
* stream.
|
||||
*
|
||||
* @note Currently this implementation stops after the nested loop finishes
|
||||
* playback.
|
||||
*
|
||||
* @b Important: This can be merged with SubSeekableAudioStream for playback purposes.
|
||||
* To do this, it must be extended to accept a start time.
|
||||
*/
|
||||
class SubLoopingAudioStream : public LoopableAudioStream {
|
||||
public:
|
||||
/**
|
||||
* Constructor for a SubLoopingAudioStream.
|
||||
*
|
||||
* On creation of the SubLoopingAudioStream object,
|
||||
* the underlying stream will be rewound.
|
||||
*
|
||||
* @param stream The stream to loop.
|
||||
* @param loops How often the stream should be looped (0 means infinite).
|
||||
* @param loopStart Start of the loop (this must be smaller than @p loopEnd).
|
||||
* @param loopEnd End of the loop (thus must be greater than @p loopStart).
|
||||
* @param disposeAfterUse Whether the stream should be disposed when the
|
||||
* SubLoopingAudioStream is destroyed.
|
||||
*/
|
||||
SubLoopingAudioStream(SeekableAudioStream *stream, uint loops,
|
||||
const Timestamp loopStart,
|
||||
const Timestamp loopEnd,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
bool endOfData() const;
|
||||
bool endOfStream() const;
|
||||
|
||||
bool isStereo() const { return _parent->isStereo(); }
|
||||
int getRate() const { return _parent->getRate(); }
|
||||
|
||||
uint getCompleteIterations() const { return _completeIterations; }
|
||||
void setRemainingIterations(uint loops) { _loops = _completeIterations + loops; }
|
||||
private:
|
||||
Common::DisposablePtr<SeekableAudioStream> _parent;
|
||||
|
||||
uint _loops;
|
||||
uint _completeIterations;
|
||||
Timestamp _pos;
|
||||
Timestamp _loopStart, _loopEnd;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A SubSeekableAudioStream class that provides access to a SeekableAudioStream
|
||||
* just in the range [start, end).
|
||||
*
|
||||
* The same caveats apply to SubSeekableAudioStream as do to SeekableAudioStream.
|
||||
*
|
||||
* Manipulating the parent stream directly will break the substream.
|
||||
*/
|
||||
class SubSeekableAudioStream : public SeekableAudioStream {
|
||||
public:
|
||||
/**
|
||||
* Create a new SubSeekableAudioStream.
|
||||
*
|
||||
* @param parent Parent stream object.
|
||||
* @param start Start time.
|
||||
* @param end End time.
|
||||
* @param disposeAfterUse Whether the parent stream object should be destroyed on destruction of the SubSeekableAudioStream.
|
||||
*/
|
||||
SubSeekableAudioStream(SeekableAudioStream *parent, const Timestamp start, const Timestamp end, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool isStereo() const { return _parent->isStereo(); }
|
||||
|
||||
int getRate() const { return _parent->getRate(); }
|
||||
|
||||
bool endOfData() const { return (_pos >= _length) || _parent->endOfData(); }
|
||||
bool endOfStream() const { return (_pos >= _length) || _parent->endOfStream(); }
|
||||
|
||||
bool seek(const Timestamp &where);
|
||||
|
||||
Timestamp getLength() const { return _length; }
|
||||
private:
|
||||
Common::DisposablePtr<SeekableAudioStream> _parent;
|
||||
|
||||
const Timestamp _start;
|
||||
const Timestamp _length;
|
||||
Timestamp _pos;
|
||||
};
|
||||
|
||||
/**
|
||||
* A QueuingAudioStream class that allows for queuing multiple audio streams for playback.
|
||||
*/
|
||||
|
||||
class QueuingAudioStream : public Audio::AudioStream {
|
||||
public:
|
||||
|
||||
/**
|
||||
* Queue an audio stream for playback.
|
||||
*
|
||||
* This stream plays all queued streams, in the order in which they were queued.
|
||||
* If disposeAfterUse is set to DisposeAfterUse::YES, then the queued stream
|
||||
* is deleted after all data contained in it has been played.
|
||||
*/
|
||||
virtual void queueAudioStream(Audio::AudioStream *audStream,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) = 0;
|
||||
|
||||
/**
|
||||
* Queue a block of raw audio data for playback.
|
||||
*
|
||||
* This stream plays all queued blocks, in the order in which they were queued.
|
||||
* If disposeAfterUse is set to DisposeAfterUse::YES, then the queued block is
|
||||
* released using free() after all data contained in it has been played.
|
||||
*
|
||||
* @note Make sure to allocate the data block with malloc(), not with new[].
|
||||
*
|
||||
* @param data Pointer to the audio data block.
|
||||
* @param size Length of the audio data block.
|
||||
* @param disposeAfterUse If equal to DisposeAfterUse::YES, the block is released using free() after use.
|
||||
* @param flags A bit-ORed combination of RawFlags describing the audio data format.
|
||||
*/
|
||||
void queueBuffer(byte *data, uint32 size, DisposeAfterUse::Flag disposeAfterUse, byte flags);
|
||||
|
||||
/**
|
||||
* Mark this stream as finished.
|
||||
*
|
||||
* This is used to signal that no further data will be queued to the stream.
|
||||
* The stream is only considered as ended after this has been done.
|
||||
*/
|
||||
virtual void finish() = 0;
|
||||
|
||||
/**
|
||||
* Return the number of streams still queued for playback (including
|
||||
* the currently playing stream).
|
||||
*/
|
||||
virtual uint32 numQueuedStreams() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Factory function for a QueuingAudioStream.
|
||||
*/
|
||||
QueuingAudioStream *makeQueuingAudioStream(int rate, bool stereo);
|
||||
|
||||
/**
|
||||
* Convert a point in time to a precise sample offset
|
||||
* with the given parameters.
|
||||
*
|
||||
* @param where Point in time.
|
||||
* @param rate Rate of the stream.
|
||||
* @param isStereo Whether the stream is a stereo stream.
|
||||
*/
|
||||
Timestamp convertTimeToStreamPos(const Timestamp &where, int rate, bool isStereo);
|
||||
|
||||
/**
|
||||
* Factory function for an AudioStream wrapper that cuts off the amount of samples read after a
|
||||
* given time length is reached.
|
||||
*
|
||||
* @param parentStream The stream to limit.
|
||||
* @param length The time length to limit the stream to.
|
||||
* @param disposeAfterUse Whether the parent stream object should be destroyed on destruction of the returned stream.
|
||||
*/
|
||||
AudioStream *makeLimitingAudioStream(AudioStream *parentStream, const Timestamp &length, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
/**
|
||||
* An AudioStream designed to work in terms of packets.
|
||||
*
|
||||
* It is similar in concept to the QueuingAudioStream, but does not
|
||||
* necessarily rely on the data from each queued AudioStream
|
||||
* being separate.
|
||||
*/
|
||||
class PacketizedAudioStream : public virtual AudioStream {
|
||||
public:
|
||||
virtual ~PacketizedAudioStream() {}
|
||||
|
||||
/**
|
||||
* Queue the next packet to be decoded.
|
||||
*/
|
||||
virtual void queuePacket(Common::SeekableReadStream *data) = 0;
|
||||
|
||||
/**
|
||||
* Mark this stream as finished. That is, signal that no further data
|
||||
*
|
||||
* This is used to signal that no further data will be queued to the stream.
|
||||
* The stream is only considered as ended after this has been done.
|
||||
*/
|
||||
virtual void finish() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* A PacketizedAudioStream that works closer to a QueuingAudioStream.
|
||||
*
|
||||
* It queues individual packets as a whole AudioStream to an internal
|
||||
* QueuingAudioStream. This is used for writing quick wrappers against
|
||||
* e.g. RawStream, which can be then made into PacketizedAudioStreams.
|
||||
*/
|
||||
class StatelessPacketizedAudioStream : public PacketizedAudioStream {
|
||||
public:
|
||||
/**
|
||||
* Create a StatelessPacketizedAudioStream with the specified sample rate
|
||||
* and for the specified number of channels.
|
||||
*/
|
||||
StatelessPacketizedAudioStream(uint rate, uint channels) :
|
||||
_rate(rate), _channels(channels), _stream(makeQueuingAudioStream(rate, channels == 2)) {}
|
||||
virtual ~StatelessPacketizedAudioStream() {}
|
||||
|
||||
// AudioStream API
|
||||
bool isStereo() const { return _channels == 2; }
|
||||
int getRate() const { return _rate; }
|
||||
int readBuffer(int16 *data, const int numSamples) { return _stream->readBuffer(data, numSamples); }
|
||||
bool endOfData() const { return _stream->endOfData(); }
|
||||
bool endOfStream() const { return _stream->endOfStream(); }
|
||||
|
||||
// PacketizedAudioStream API
|
||||
void queuePacket(Common::SeekableReadStream *data) { _stream->queueAudioStream(makeStream(data)); }
|
||||
void finish() { _stream->finish(); }
|
||||
|
||||
/**
|
||||
* Return how many channels this stream is using.
|
||||
*/
|
||||
uint getChannels() const { return _channels; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Create an AudioStream for a given packet.
|
||||
*/
|
||||
virtual AudioStream *makeStream(Common::SeekableReadStream *data) = 0;
|
||||
|
||||
private:
|
||||
uint _rate;
|
||||
uint _channels;
|
||||
Common::ScopedPtr<QueuingAudioStream> _stream;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an AudioStream that plays nothing and immediately returns that
|
||||
* endOfStream() has been reached.
|
||||
*/
|
||||
AudioStream *makeNullAudioStream();
|
||||
|
||||
/**
|
||||
* Create an AudioStream that just returns silent samples and runs infinitely.
|
||||
*/
|
||||
AudioStream *makeSilentAudioStream(int rate, bool stereo);
|
||||
|
||||
/** @} */
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
423
audio/casio.cpp
Normal file
423
audio/casio.cpp
Normal file
@@ -0,0 +1,423 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/casio.h"
|
||||
|
||||
#include "common/config-manager.h"
|
||||
|
||||
MidiDriver_Casio::ActiveNote::ActiveNote() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::ActiveNote::clear() {
|
||||
source = 0x7F;
|
||||
channel = 0xFF;
|
||||
note = 0xFF;
|
||||
sustained = false;
|
||||
}
|
||||
|
||||
const int MidiDriver_Casio::CASIO_CHANNEL_POLYPHONY[] = { 6, 4, 2, 4 };
|
||||
|
||||
const uint8 MidiDriver_Casio::INSTRUMENT_REMAPPING_CT460_TO_MT540[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x10, 0x0C, 0x07,
|
||||
0x0B, 0x1B, 0x11, 0x08, 0x09, 0x14, 0x0A, 0x15, 0x0D, 0x0E,
|
||||
0x0F, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1C, 0x1D, 0x12, 0x13
|
||||
};
|
||||
|
||||
const uint8 MidiDriver_Casio::INSTRUMENT_REMAPPING_MT540_TO_CT460[] {
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x09, 0x0D, 0x0E,
|
||||
0x10, 0x0A, 0x08, 0x12, 0x13, 0x14, 0x07, 0x0C, 0x1C, 0x1D,
|
||||
0x0F, 0x11, 0x15, 0x16, 0x17, 0x18, 0x19, 0x0B, 0x1A, 0x1B
|
||||
};
|
||||
|
||||
const uint8 MidiDriver_Casio::RHYTHM_INSTRUMENT_MT540 = 0x08;
|
||||
const uint8 MidiDriver_Casio::RHYTHM_INSTRUMENT_CT460 = 0x0D;
|
||||
const uint8 MidiDriver_Casio::BASS_INSTRUMENT_MT540 = 0x12;
|
||||
const uint8 MidiDriver_Casio::BASS_INSTRUMENT_CT460 = 0x1C;
|
||||
|
||||
MidiDriver_Casio::MidiDriver_Casio(MusicType midiType) : _midiType(midiType), _driver(nullptr),
|
||||
_deviceType(MT_CT460), _isOpen(false), _rhythmNoteRemapping(nullptr), _sendUntrackedNoteOff(true) {
|
||||
if (!(_midiType == MT_MT540 || _midiType == MT_CT460)) {
|
||||
error("MidiDriver_Casio - Unsupported music data type %i", _midiType);
|
||||
}
|
||||
Common::fill(_instruments, _instruments + ARRAYSIZE(_instruments), 0);
|
||||
Common::fill(_rhythmChannel, _rhythmChannel + ARRAYSIZE(_rhythmChannel), false);
|
||||
Common::fill(_sustain, _sustain + ARRAYSIZE(_sustain), false);
|
||||
}
|
||||
|
||||
MidiDriver_Casio::~MidiDriver_Casio() {
|
||||
close();
|
||||
|
||||
if (_driver) {
|
||||
_driver->setTimerCallback(nullptr, nullptr);
|
||||
delete _driver;
|
||||
_driver = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int MidiDriver_Casio::open() {
|
||||
assert(!_driver);
|
||||
|
||||
// Detect the output device.
|
||||
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_PREFER_GM);
|
||||
MusicType deviceMusicType = MidiDriver::getMusicType(dev);
|
||||
// System MIDI ports have type GM. This driver supports no other type.
|
||||
if (deviceMusicType != MT_GM)
|
||||
error("MidiDriver_Casio::open - Detected device has unsupported type %i", deviceMusicType);
|
||||
|
||||
// Create the driver.
|
||||
MidiDriver *driver = MidiDriver::createMidi(dev);
|
||||
|
||||
// Check the MIDI mode setting to determine if the device connected to the
|
||||
// system MIDI port is an MT-540 or a CT-460/CSM-1.
|
||||
int midiMode = ConfMan.getInt("midi_mode");
|
||||
MusicType deviceType;
|
||||
if (midiMode == 3) {
|
||||
deviceType = MT_MT540;
|
||||
} else if (midiMode == 4) {
|
||||
deviceType = MT_CT460;
|
||||
} else {
|
||||
error("MidiDriver_Casio::open - Unsupported MIDI mode %i", midiMode);
|
||||
}
|
||||
|
||||
return open(driver, deviceType);
|
||||
}
|
||||
|
||||
int MidiDriver_Casio::open(MidiDriver* driver, MusicType deviceType) {
|
||||
assert(!_driver);
|
||||
|
||||
_driver = driver;
|
||||
_deviceType = deviceType;
|
||||
if (!(_deviceType == MT_MT540 || _deviceType == MT_CT460)) {
|
||||
error("MidiDriver_Casio::open - Unsupported device type %i", _deviceType);
|
||||
}
|
||||
|
||||
if (!_driver)
|
||||
return 255;
|
||||
|
||||
int result = _driver->open();
|
||||
if (result != MidiDriver::MERR_ALREADY_OPEN && result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Initialize the device by setting the instrument to 0 on all channels.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
programChange(i, 0, -1);
|
||||
}
|
||||
|
||||
_timerRate = _driver->getBaseTempo();
|
||||
_driver->setTimerCallback(this, timerCallback);
|
||||
|
||||
_isOpen = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::close() {
|
||||
if (_driver && _isOpen) {
|
||||
stopAllNotes();
|
||||
|
||||
_driver->setTimerCallback(nullptr, nullptr);
|
||||
_driver->close();
|
||||
_isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MidiDriver_Casio::isOpen() const {
|
||||
return _isOpen;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::send(int8 source, uint32 b) {
|
||||
byte dataChannel = b & 0xf;
|
||||
int8 outputChannel = source < 0 ? dataChannel : mapSourceChannel(source, dataChannel);
|
||||
if (outputChannel < 0 || outputChannel >= 4)
|
||||
// Only process events for channels 0-3.
|
||||
return;
|
||||
|
||||
processEvent(source, b, outputChannel);
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::metaEvent(int8 source, byte type, const byte *data, uint16 length) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
if (type == 0x2F && source >= 0) // End of Track
|
||||
deinitSource(source);
|
||||
|
||||
_driver->metaEvent(type, data, length);
|
||||
}
|
||||
|
||||
int8 MidiDriver_Casio::mapSourceChannel(uint8 source, uint8 dataChannel) {
|
||||
// TODO Multisource functionality has not been fully implemented. Current
|
||||
// implementation assumes 1 source with access to all channels.
|
||||
return dataChannel;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::processEvent(int8 source, uint32 b, uint8 outputChannel) {
|
||||
assert(source < MAXIMUM_SOURCES);
|
||||
|
||||
byte command = b & 0xF0;
|
||||
byte op1 = (b >> 8) & 0xFF;
|
||||
byte op2 = (b >> 16) & 0xFF;
|
||||
|
||||
// The only commands supported by the Casio devices are note on, note off
|
||||
// and program change.
|
||||
switch (command) {
|
||||
case MIDI_COMMAND_NOTE_OFF:
|
||||
noteOff(outputChannel, command, op1, op2, source);
|
||||
break;
|
||||
case MIDI_COMMAND_NOTE_ON:
|
||||
noteOn(outputChannel, op1, op2, source);
|
||||
break;
|
||||
case MIDI_COMMAND_PROGRAM_CHANGE:
|
||||
programChange(outputChannel, op1, source);
|
||||
break;
|
||||
case MIDI_COMMAND_CONTROL_CHANGE:
|
||||
controlChange(outputChannel, op1, op2, source);
|
||||
break;
|
||||
default:
|
||||
warning("MidiDriver_Casio::processEvent - Received unsupported event %02x", command);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::noteOff(byte outputChannel, byte command, byte note, byte velocity, int8 source) {
|
||||
// Apply rhythm note mapping.
|
||||
int8 mappedNote = mapNote(outputChannel, note);
|
||||
if (mappedNote < 0)
|
||||
// Rhythm note with no Casio equivalent.
|
||||
return;
|
||||
|
||||
_mutex.lock();
|
||||
|
||||
// Remove this note from the active note registry.
|
||||
bool foundActiveNote = false;
|
||||
for (int i = 0; i < ARRAYSIZE(_activeNotes); i++) {
|
||||
if (_activeNotes[i].channel == outputChannel && _activeNotes[i].note == mappedNote &&
|
||||
_activeNotes[i].source == source && !_activeNotes[i].sustained) {
|
||||
foundActiveNote = true;
|
||||
if (outputChannel < 4 && _sustain[outputChannel]) {
|
||||
_activeNotes[i].sustained = true;
|
||||
} else {
|
||||
_activeNotes[i].clear();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_mutex.unlock();
|
||||
|
||||
if (!foundActiveNote && !_sendUntrackedNoteOff)
|
||||
return;
|
||||
|
||||
_driver->send(command | outputChannel, mappedNote, velocity);
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::noteOn(byte outputChannel, byte note, byte velocity, int8 source) {
|
||||
if (velocity == 0) {
|
||||
// Note on with velocity 0 is a note off.
|
||||
noteOff(outputChannel, MIDI_COMMAND_NOTE_ON, note, velocity, source);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply rhythm note mapping.
|
||||
int8 mappedNote = mapNote(outputChannel, note);
|
||||
if (mappedNote < 0)
|
||||
// Rhythm note with no Casio equivalent.
|
||||
return;
|
||||
|
||||
_mutex.lock();
|
||||
|
||||
// Add this note to the active note registry.
|
||||
for (int i = 0; i < ARRAYSIZE(_activeNotes); i++) {
|
||||
if (_activeNotes[i].note == 0xFF) {
|
||||
_activeNotes[i].channel = outputChannel;
|
||||
_activeNotes[i].note = mappedNote;
|
||||
_activeNotes[i].source = source;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
_mutex.unlock();
|
||||
|
||||
byte calculatedVelocity = calculateVelocity(source, velocity);
|
||||
|
||||
_driver->send(MIDI_COMMAND_NOTE_ON | outputChannel, mappedNote, calculatedVelocity);
|
||||
}
|
||||
|
||||
int8 MidiDriver_Casio::mapNote(byte outputChannel, byte note) {
|
||||
int8 mappedNote = note;
|
||||
|
||||
if (_rhythmNoteRemapping && isRhythmChannel(outputChannel)) {
|
||||
mappedNote = _rhythmNoteRemapping[note];
|
||||
if (mappedNote == 0)
|
||||
mappedNote = -1;
|
||||
}
|
||||
|
||||
return mappedNote;
|
||||
}
|
||||
|
||||
byte MidiDriver_Casio::calculateVelocity(int8 source, byte velocity) {
|
||||
byte calculatedVelocity = velocity;
|
||||
// Apply volume settings to velocity.
|
||||
// The Casio devices do not apply note velocity, so the only volume control
|
||||
// possible is setting the velocity to 0 if one of the volume settings is 0.
|
||||
if (source >= 0) {
|
||||
// Scale to source volume.
|
||||
if (_sources[source].volume == 0)
|
||||
calculatedVelocity = 0;
|
||||
}
|
||||
if (_userVolumeScaling) {
|
||||
if (_userMute) {
|
||||
calculatedVelocity = 0;
|
||||
} else {
|
||||
// Scale to user volume.
|
||||
uint16 userVolume = _sources[source].type == SOURCE_TYPE_SFX ? _userSfxVolume : _userMusicVolume;
|
||||
if (userVolume == 0)
|
||||
calculatedVelocity = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return calculatedVelocity;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::programChange(byte outputChannel, byte patchId, int8 source, bool applyRemapping) {
|
||||
if (outputChannel < 4)
|
||||
// Register the new instrument.
|
||||
_instruments[outputChannel] = patchId;
|
||||
|
||||
// Apply instrument mapping.
|
||||
byte mappedInstrument = mapInstrument(patchId, applyRemapping);
|
||||
|
||||
if (outputChannel < 4) {
|
||||
_rhythmChannel[outputChannel] =
|
||||
mappedInstrument == (_deviceType == MT_MT540 ? RHYTHM_INSTRUMENT_MT540 : RHYTHM_INSTRUMENT_CT460);
|
||||
}
|
||||
|
||||
_driver->send(MIDI_COMMAND_PROGRAM_CHANGE | outputChannel | (mappedInstrument << 8));
|
||||
}
|
||||
|
||||
byte MidiDriver_Casio::mapInstrument(byte program, bool applyRemapping) {
|
||||
byte mappedInstrument = program;
|
||||
|
||||
if (applyRemapping && _instrumentRemapping)
|
||||
// Apply custom instrument mapping.
|
||||
mappedInstrument = _instrumentRemapping[program];
|
||||
|
||||
// Apply MT-540 <> CT-460 instrument mapping if necessary.
|
||||
if (mappedInstrument < ARRAYSIZE(INSTRUMENT_REMAPPING_MT540_TO_CT460)) {
|
||||
if (_midiType == MT_MT540 && _deviceType == MT_CT460) {
|
||||
mappedInstrument = INSTRUMENT_REMAPPING_MT540_TO_CT460[mappedInstrument];
|
||||
} else if (_midiType == MT_CT460 && _deviceType == MT_MT540) {
|
||||
mappedInstrument = INSTRUMENT_REMAPPING_CT460_TO_MT540[mappedInstrument];
|
||||
}
|
||||
}
|
||||
|
||||
return mappedInstrument;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::controlChange(byte outputChannel, byte controllerNumber, byte controllerValue, int8 source) {
|
||||
if (outputChannel >= 4)
|
||||
return;
|
||||
|
||||
if (controllerNumber == MIDI_CONTROLLER_SUSTAIN) {
|
||||
_sustain[outputChannel] = controllerValue >= 0x40;
|
||||
|
||||
if (!_sustain[outputChannel]) {
|
||||
// Remove sustained notes if sustain is turned off.
|
||||
|
||||
_mutex.lock();
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(_activeNotes); i++) {
|
||||
if (_activeNotes[i].channel == outputChannel && _activeNotes[i].sustained) {
|
||||
_activeNotes[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
_driver->send(MIDI_COMMAND_CONTROL_CHANGE | outputChannel | (controllerNumber << 8) | (controllerValue << 16));
|
||||
} else {
|
||||
// No other controllers are supported.
|
||||
//warning("MidiDriver_Casio::controlChange - Received event for unsupported controller %02x", controllerNumber);
|
||||
}
|
||||
}
|
||||
|
||||
bool MidiDriver_Casio::isRhythmChannel(uint8 outputChannel) {
|
||||
if (outputChannel >= 4)
|
||||
return false;
|
||||
|
||||
return _rhythmChannel[outputChannel];
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::stopAllNotes(bool stopSustainedNotes) {
|
||||
stopAllNotes(0xFF, 0xFF);
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::stopAllNotes(uint8 source, uint8 channel) {
|
||||
_mutex.lock();
|
||||
|
||||
// Turn off sustain.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (channel == 0xFF || i == channel) {
|
||||
controlChange(i, MIDI_CONTROLLER_SUSTAIN, 0, source > 127 ? -1 : source);
|
||||
}
|
||||
}
|
||||
|
||||
// Send note off events for all notes in the active note registry for this
|
||||
// source and channel.
|
||||
for (int i = 0; i < ARRAYSIZE(_activeNotes); i++) {
|
||||
if (_activeNotes[i].note != 0xFF && (source == 0xFF || _activeNotes[i].source == source) &&
|
||||
(channel == 0xFF || _activeNotes[i].channel == channel)) {
|
||||
_driver->send(MIDI_COMMAND_NOTE_OFF | _activeNotes[i].channel, _activeNotes[i].note, 0);
|
||||
_activeNotes[i].clear();
|
||||
}
|
||||
}
|
||||
|
||||
_mutex.unlock();
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::applySourceVolume(uint8 source) {
|
||||
// Because the Casio devices do not support the volume controller, source
|
||||
// volume is applied to note velocity and it cannot be applied directly.
|
||||
}
|
||||
|
||||
MidiChannel *MidiDriver_Casio::allocateChannel() {
|
||||
// MidiChannel objects are not supported by this driver.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MidiChannel *MidiDriver_Casio::getPercussionChannel() {
|
||||
// MidiChannel objects are not supported by this driver.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32 MidiDriver_Casio::getBaseTempo() {
|
||||
if (_driver) {
|
||||
return _driver->getBaseTempo();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MidiDriver_Casio::timerCallback(void *data) {
|
||||
MidiDriver_Casio *driver = static_cast<MidiDriver_Casio *>(data);
|
||||
driver->onTimer();
|
||||
}
|
||||
282
audio/casio.h
Normal file
282
audio/casio.h
Normal file
@@ -0,0 +1,282 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_CASIO_H
|
||||
#define AUDIO_CASIO_H
|
||||
|
||||
#include "audio/mididrv.h"
|
||||
#include "audio/mididrv_ms.h"
|
||||
|
||||
/**
|
||||
* MIDI driver implementation for the Casio MT-540, CT-640 and CSM-1 devices.
|
||||
*
|
||||
* This driver provides source volume and user volume scaling, as well as
|
||||
* fades (due to device limitations these are applied to note velocity instead
|
||||
* of the volume controller). It also provides instrument mapping between the
|
||||
* MT-540 and CT-640/CSM-1 instrument map and can map rhythm notes from the
|
||||
* input MIDI data by specifying a remapping.
|
||||
*
|
||||
* TODO This driver does not provide a full multisource functionality
|
||||
* implementation because this was not needed for the game for which it was
|
||||
* added (Elvira). It assumes only 1 source sends MIDI data at the same time
|
||||
* and this source has access to all MIDI channels.
|
||||
*
|
||||
* Some details of these Casio devices:
|
||||
* - They seem to support only note on, note off and program change MIDI
|
||||
* events. Because they do not support the volume controller, volume is
|
||||
* applied to note velocity (and I'm not sure if they support even that...).
|
||||
* All Notes Off is performed by keeping track of active notes and sending
|
||||
* note off events for all active notes.
|
||||
* - They only use MIDI channels 0-3. The MT-32 and GM devices have channel 9
|
||||
* as the fixed rhythm channel. The Casio devices can switch any used channel
|
||||
* to a rhythm channel by setting a specific instrument.
|
||||
* - They have only 30 instruments, 3 of which are rhythm or SFX banks.
|
||||
* - All devices seem to have the same capabilities, but the instrument
|
||||
* numbering is different between the MT-540 on the one hand and the CT-640
|
||||
* and CSM-1 on the other.
|
||||
*/
|
||||
class MidiDriver_Casio : public MidiDriver_Multisource {
|
||||
protected:
|
||||
// Tracks a note currently playing on the device.
|
||||
struct ActiveNote {
|
||||
// The source that played the note (0x7F if no note is tracked).
|
||||
int8 source;
|
||||
// The output MIDI channel on which the note is playing
|
||||
// (0xFF if no note is tracked).
|
||||
uint8 channel;
|
||||
// The MIDI note number of the playing note
|
||||
// (0xFF if no note is tracked).
|
||||
uint8 note;
|
||||
// True if this note is sustained (turned off but held due to the
|
||||
// sustain controller).
|
||||
bool sustained;
|
||||
|
||||
ActiveNote();
|
||||
|
||||
// Sets the struct to values indicating no note is currently tracked.
|
||||
void clear();
|
||||
};
|
||||
|
||||
public:
|
||||
// The maximum polyphony for each output channel.
|
||||
static const int CASIO_CHANNEL_POLYPHONY[4];
|
||||
|
||||
// Array for remapping instrument numbers from CT-460/CSM-1 to MT-540.
|
||||
static const uint8 INSTRUMENT_REMAPPING_CT460_TO_MT540[30];
|
||||
// Array for remapping instrument numbers from MT-540 to CT-460/CSM-1.
|
||||
static const uint8 INSTRUMENT_REMAPPING_MT540_TO_CT460[30];
|
||||
|
||||
// The instrument number used for rhythm sounds on the MT-540.
|
||||
static const uint8 RHYTHM_INSTRUMENT_MT540;
|
||||
// The instrument number used for rhythm sounds on the CT-460 and CSM-1.
|
||||
static const uint8 RHYTHM_INSTRUMENT_CT460;
|
||||
|
||||
// The instrument number used for the bass instruments on the MT-540.
|
||||
static const uint8 BASS_INSTRUMENT_MT540;
|
||||
// The instrument number used for the bass instruments on the CT-460 and
|
||||
// CSM-1.
|
||||
static const uint8 BASS_INSTRUMENT_CT460;
|
||||
|
||||
/**
|
||||
* Constructs a new Casio MidiDriver instance.
|
||||
*
|
||||
* @param midiType The type of MIDI data that will be sent to the driver
|
||||
* (MT-540 or CT-460/CSM-1).
|
||||
*/
|
||||
MidiDriver_Casio(MusicType midiType);
|
||||
~MidiDriver_Casio();
|
||||
|
||||
int open() override;
|
||||
/**
|
||||
* Opens the driver wrapping the specified MidiDriver instance.
|
||||
*
|
||||
* @param driver The driver that will receive MIDI events from this driver.
|
||||
* @param deviceType The type of MIDI device that will receive MIDI events
|
||||
* from this driver (MT-540 or CT-460/CSM-1).
|
||||
* @return 0 if the driver was opened successfully; >0 if an error occurred.
|
||||
*/
|
||||
virtual int open(MidiDriver *driver, MusicType deviceType);
|
||||
void close() override;
|
||||
bool isOpen() const override;
|
||||
|
||||
using MidiDriver_BASE::send;
|
||||
void send(int8 source, uint32 b) override;
|
||||
void metaEvent(int8 source, byte type, const byte *data, uint16 length) override;
|
||||
|
||||
void stopAllNotes(bool stopSustainedNotes = false) override;
|
||||
MidiChannel *allocateChannel() override;
|
||||
MidiChannel *getPercussionChannel() override;
|
||||
uint32 getBaseTempo() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Maps a data MIDI channel to an output MIDI channel for the specified
|
||||
* source.
|
||||
* TODO This driver has no default implementation for a channel allocation
|
||||
* scheme. It assumes only one source is active at a time and has access to
|
||||
* all output channels. The default implementation for this method just
|
||||
* returns the data channel.
|
||||
*
|
||||
* @param source The source for which the MIDI channel should be mapped.
|
||||
* @param dataChannel The data channel that should be mapped.
|
||||
* @return The output MIDI channel.
|
||||
*/
|
||||
virtual int8 mapSourceChannel(uint8 source, uint8 dataChannel);
|
||||
/**
|
||||
* Processes a MIDI event.
|
||||
*
|
||||
* @param source The source sending the MIDI event.
|
||||
* @param b The MIDI event data.
|
||||
* @param outputChannel The MIDI channel on which the event should be sent.
|
||||
*/
|
||||
virtual void processEvent(int8 source, uint32 b, uint8 outputChannel);
|
||||
/**
|
||||
* Processes a MIDI note off event.
|
||||
*
|
||||
* @param outputChannel The MIDI channel on which the event should be sent.
|
||||
* @param command The MIDI command that triggered the note off event (other
|
||||
* than note off (0x80) this can also be note on (0x90) with velocity 0).
|
||||
* @param note The MIDI note that should be turned off.
|
||||
* @param velocity The note off velocity.
|
||||
* @param source The source sending the MIDI event.
|
||||
*/
|
||||
virtual void noteOff(byte outputChannel, byte command, byte note, byte velocity, int8 source);
|
||||
/**
|
||||
* Processes a MIDI note on event.
|
||||
*
|
||||
* @param outputChannel The MIDI channel on which the event should be sent.
|
||||
* @param note The MIDI note that should be turned on.
|
||||
* @param velocity The note velocity,
|
||||
* @param source The source sending the MIDI event.
|
||||
*/
|
||||
virtual void noteOn(byte outputChannel, byte note, byte velocity, int8 source);
|
||||
/**
|
||||
* Processes a MIDI program change event.
|
||||
*
|
||||
* @param outputChannel The MIDI channel on which the event should be sent.
|
||||
* @param patchId The instrument that should be set.
|
||||
* @param source The source sending the MIDI event.
|
||||
* @param applyRemapping True if the instrument remapping
|
||||
* (_instrumentRemapping) should be applied.
|
||||
*/
|
||||
virtual void programChange(byte outputChannel, byte patchId, int8 source, bool applyRemapping = true);
|
||||
/**
|
||||
* Processes a MIDI control change event.
|
||||
*
|
||||
* @param outputChannel The MIDI channel on which the event should be sent.
|
||||
* @param controllerNumber The controller for which the value should be set.
|
||||
* @param controllerValue The controller value that should be set.
|
||||
* @param source The source sending the MIDI event.
|
||||
*/
|
||||
virtual void controlChange(byte outputChannel, byte controllerNumber, byte controllerValue, int8 source);
|
||||
|
||||
/**
|
||||
* Maps the specified note to a different note according to the rhythm note
|
||||
* mapping. This mapping is only applied if the note is played on a rhythm
|
||||
* channel.
|
||||
*
|
||||
* @param outputChannel The MIDI channel on which the note is/will be
|
||||
* active.
|
||||
* @param note The note that should be mapped.
|
||||
* @return The mapped note, or the specified note if it was not mapped.
|
||||
*/
|
||||
virtual int8 mapNote(byte outputChannel, byte note);
|
||||
/**
|
||||
* Calculates the velocity for a note on event. This applies source volume
|
||||
* and user volume settings to the specified velocity value.
|
||||
*
|
||||
* @param source The source that sent the note on event.
|
||||
* @param velocity The velocity specified in the note on event.
|
||||
* @return The calculated velocity.
|
||||
*/
|
||||
virtual byte calculateVelocity(int8 source, byte velocity);
|
||||
/**
|
||||
* Maps the specified instrument to the instrument value that should be
|
||||
* sent to the MIDI device. This applies the current instrument remapping
|
||||
* (if present and if applyRemapping is specified) and maps MT-540
|
||||
* instruments to CT-460/CSM-1 instruments (or the other way around) if
|
||||
* necessary.
|
||||
*
|
||||
* @param program The instrument that should be mapped.
|
||||
* @param applyRemapping True if the instrument remapping
|
||||
* (_instrumentRemapping) should be applied.
|
||||
* @return The mapped instrument, or the specified instrument if no mapping
|
||||
* was necessary.
|
||||
*/
|
||||
virtual byte mapInstrument(byte program, bool applyRemapping = true);
|
||||
/**
|
||||
* Returns whether the specified MIDI channel is a rhythm channel. On the
|
||||
* Casio devices, the rhythm channel is not fixed but is created by setting
|
||||
* a channel to a specific instrument (see the rhythm instrument constants).
|
||||
*
|
||||
* @param outputChannel The channel that should be checked.
|
||||
* @return True if the specified channel is a rhythm channel, false
|
||||
* otherwise.
|
||||
*/
|
||||
virtual bool isRhythmChannel(uint8 outputChannel);
|
||||
// This implementation does nothing, because source volume is applied to
|
||||
// note velocity and cannot be applied immediately.
|
||||
void applySourceVolume(uint8 source) override;
|
||||
void stopAllNotes(uint8 source, uint8 channel) override;
|
||||
|
||||
// The wrapped MIDI driver.
|
||||
MidiDriver *_driver;
|
||||
// The type of Casio device accessed by the wrapped driver: MT-540 or
|
||||
// CT-460/CSM-1.
|
||||
MusicType _deviceType;
|
||||
// The type of MIDI data supplied to the driver: MT-540 or CT-460/CSM-1.
|
||||
MusicType _midiType;
|
||||
// True if this MIDI driver has been opened.
|
||||
bool _isOpen;
|
||||
|
||||
// The current instrument on each MIDI output channel. This is the
|
||||
// instrument number as specified in the program change events (before
|
||||
// remapping is applied).
|
||||
byte _instruments[4];
|
||||
// Indicates if each output channel is currently a rhythm channel (i.e. it
|
||||
// has the rhythm instrument set).
|
||||
bool _rhythmChannel[4];
|
||||
// Tracks the notes currently active on the MIDI device.
|
||||
ActiveNote _activeNotes[32];
|
||||
// Tracks the sustain controller status of the output channels.
|
||||
bool _sustain[4];
|
||||
|
||||
// Optional remapping for rhythm notes. Should point to a 128 byte array
|
||||
// which maps the rhythm note numbers in the input MIDI data to the rhythm
|
||||
// note numbers used by the output device.
|
||||
byte *_rhythmNoteRemapping;
|
||||
|
||||
// If true the driver will send note off events for notes which are not in
|
||||
// the active note registry. Typically this should be true to prevent
|
||||
// hanging notes in case there are more active notes than can be stored in
|
||||
// the active note registry. Can be set to false if these note offs are not
|
||||
// desirable, f.e. because the driver will be receiving note off events
|
||||
// without corresponding note on events.
|
||||
bool _sendUntrackedNoteOff;
|
||||
|
||||
// Mutex for operations on active notes.
|
||||
Common::Mutex _mutex;
|
||||
|
||||
public:
|
||||
static void timerCallback(void *data);
|
||||
};
|
||||
|
||||
#endif
|
||||
165
audio/chip.cpp
Normal file
165
audio/chip.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/chip.h"
|
||||
#include "audio/mixer.h"
|
||||
|
||||
#include "common/timer.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
void Chip::start(TimerCallback *callback, int timerFrequency) {
|
||||
_callback.reset(callback);
|
||||
startCallbacks(timerFrequency);
|
||||
}
|
||||
|
||||
void Chip::stop() {
|
||||
stopCallbacks();
|
||||
_callback.reset();
|
||||
}
|
||||
|
||||
RealChip::RealChip() : _baseFreq(0), _remainingTicks(0) {
|
||||
}
|
||||
|
||||
RealChip::~RealChip() {
|
||||
// Stop callbacks, just in case. If it's still playing at this
|
||||
// point, there's probably a bigger issue, though. The subclass
|
||||
// needs to call stop() or the pointer can still use be used in
|
||||
// the mixer thread at the same time.
|
||||
stop();
|
||||
}
|
||||
|
||||
void RealChip::setCallbackFrequency(int timerFrequency) {
|
||||
stopCallbacks();
|
||||
startCallbacks(timerFrequency);
|
||||
}
|
||||
|
||||
void RealChip::startCallbacks(int timerFrequency) {
|
||||
_baseFreq = timerFrequency;
|
||||
assert(_baseFreq > 0);
|
||||
|
||||
// We can't request more a timer faster than 100Hz. We'll handle this by calling
|
||||
// the proc multiple times in onTimer() later on.
|
||||
if (timerFrequency > kMaxFreq)
|
||||
timerFrequency = kMaxFreq;
|
||||
|
||||
_remainingTicks = 0;
|
||||
g_system->getTimerManager()->installTimerProc(timerProc, 1000000 / timerFrequency, this, "RealChip");
|
||||
}
|
||||
|
||||
void RealChip::stopCallbacks() {
|
||||
g_system->getTimerManager()->removeTimerProc(timerProc);
|
||||
_baseFreq = 0;
|
||||
_remainingTicks = 0;
|
||||
}
|
||||
|
||||
void RealChip::timerProc(void *refCon) {
|
||||
static_cast<RealChip *>(refCon)->onTimer();
|
||||
}
|
||||
|
||||
void RealChip::onTimer() {
|
||||
uint callbacks = 1;
|
||||
|
||||
if (_baseFreq > kMaxFreq) {
|
||||
// We run faster than our max, so run the callback multiple
|
||||
// times to approximate the actual timer callback frequency.
|
||||
uint totalTicks = _baseFreq + _remainingTicks;
|
||||
callbacks = totalTicks / kMaxFreq;
|
||||
_remainingTicks = totalTicks % kMaxFreq;
|
||||
}
|
||||
|
||||
// Call the callback multiple times. The if is on the inside of the
|
||||
// loop in case the callback removes itself.
|
||||
for (uint i = 0; i < callbacks; i++)
|
||||
if (_callback && _callback->isValid())
|
||||
(*_callback)();
|
||||
}
|
||||
|
||||
EmulatedChip::EmulatedChip() :
|
||||
_nextTick(0),
|
||||
_samplesPerTick(0),
|
||||
_baseFreq(0),
|
||||
_handle(new Audio::SoundHandle()) { }
|
||||
|
||||
EmulatedChip::~EmulatedChip() {
|
||||
// Stop callbacks, just in case. If it's still playing at this
|
||||
// point, there's probably a bigger issue, though. The subclass
|
||||
// needs to call stop() or the pointer can still use be used in
|
||||
// the mixer thread at the same time.
|
||||
stop();
|
||||
|
||||
delete _handle;
|
||||
}
|
||||
|
||||
int EmulatedChip::readBuffer(int16 *buffer, const int numSamples) {
|
||||
const int stereoFactor = isStereo() ? 2 : 1;
|
||||
int len = numSamples / stereoFactor;
|
||||
int step;
|
||||
|
||||
do {
|
||||
step = len;
|
||||
if (step > (_nextTick >> FIXP_SHIFT))
|
||||
step = (_nextTick >> FIXP_SHIFT);
|
||||
|
||||
generateSamples(buffer, step * stereoFactor);
|
||||
|
||||
_nextTick -= step << FIXP_SHIFT;
|
||||
if (!(_nextTick >> FIXP_SHIFT)) {
|
||||
if (_callback && _callback->isValid())
|
||||
(*_callback)();
|
||||
|
||||
_nextTick += _samplesPerTick;
|
||||
}
|
||||
|
||||
buffer += step * stereoFactor;
|
||||
len -= step;
|
||||
} while (len);
|
||||
|
||||
return numSamples;
|
||||
}
|
||||
|
||||
int EmulatedChip::getRate() const {
|
||||
return g_system->getMixer()->getOutputRate();
|
||||
}
|
||||
|
||||
void EmulatedChip::startCallbacks(int timerFrequency) {
|
||||
setCallbackFrequency(timerFrequency);
|
||||
g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, _handle, this, -1, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO, true);
|
||||
}
|
||||
|
||||
void EmulatedChip::stopCallbacks() {
|
||||
g_system->getMixer()->stopHandle(*_handle);
|
||||
}
|
||||
|
||||
void EmulatedChip::setCallbackFrequency(int timerFrequency) {
|
||||
_baseFreq = timerFrequency;
|
||||
assert(_baseFreq != 0);
|
||||
|
||||
int d = getRate() / _baseFreq;
|
||||
int r = getRate() % _baseFreq;
|
||||
|
||||
// This is equivalent to (getRate() << FIXP_SHIFT) / BASE_FREQ
|
||||
// but less prone to arithmetic overflow.
|
||||
|
||||
_samplesPerTick = (d << FIXP_SHIFT) + (r << FIXP_SHIFT) / _baseFreq;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
156
audio/chip.h
Normal file
156
audio/chip.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_CHIP_H
|
||||
#define AUDIO_CHIP_H
|
||||
|
||||
#include "common/func.h"
|
||||
#include "common/ptr.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Audio {
|
||||
class SoundHandle;
|
||||
|
||||
class Chip {
|
||||
public:
|
||||
virtual ~Chip() {}
|
||||
|
||||
/**
|
||||
* The type of the timer callback functor.
|
||||
*/
|
||||
typedef Common::Functor0<void> TimerCallback;
|
||||
|
||||
/**
|
||||
* Start the sound chip with callbacks.
|
||||
*/
|
||||
void start(TimerCallback *callback, int timerFrequency);
|
||||
|
||||
/**
|
||||
* Stop the sound chip
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Change the callback frequency. This must only be called from a
|
||||
* timer proc.
|
||||
*/
|
||||
virtual void setCallbackFrequency(int timerFrequency) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Start the callbacks.
|
||||
*/
|
||||
virtual void startCallbacks(int timerFrequency) = 0;
|
||||
|
||||
/**
|
||||
* Stop the callbacks.
|
||||
*/
|
||||
virtual void stopCallbacks() = 0;
|
||||
|
||||
/**
|
||||
* The functor for callbacks.
|
||||
*/
|
||||
Common::ScopedPtr<TimerCallback> _callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* A Chip that represents a real sound chip, as opposed to an emulated one.
|
||||
*
|
||||
* This will use an actual timer instead of using one calculated from
|
||||
* the number of samples in an AudioStream::readBuffer call.
|
||||
*/
|
||||
class RealChip : virtual public Chip {
|
||||
public:
|
||||
RealChip();
|
||||
virtual ~RealChip();
|
||||
|
||||
// Chip API
|
||||
void setCallbackFrequency(int timerFrequency);
|
||||
|
||||
protected:
|
||||
// Chip API
|
||||
void startCallbacks(int timerFrequency);
|
||||
void stopCallbacks();
|
||||
virtual void onTimer();
|
||||
|
||||
private:
|
||||
static void timerProc(void *refCon);
|
||||
|
||||
uint _baseFreq;
|
||||
uint _remainingTicks;
|
||||
|
||||
enum {
|
||||
kMaxFreq = 100
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* A Chip that represents an emulated sound chip.
|
||||
*
|
||||
* This will send callbacks based on the number of samples
|
||||
* decoded in readBuffer().
|
||||
*/
|
||||
class EmulatedChip : virtual public Chip, protected Audio::AudioStream {
|
||||
protected:
|
||||
static const int FIXP_SHIFT = 16;
|
||||
|
||||
public:
|
||||
EmulatedChip();
|
||||
virtual ~EmulatedChip();
|
||||
|
||||
// Chip API
|
||||
void setCallbackFrequency(int timerFrequency) override;
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
int getRate() const override;
|
||||
bool endOfData() const override { return false; }
|
||||
|
||||
protected:
|
||||
// Chip API
|
||||
void startCallbacks(int timerFrequency) override final;
|
||||
void stopCallbacks() override final;
|
||||
|
||||
/**
|
||||
* Read up to 'length' samples.
|
||||
*
|
||||
* Data will be in native endianess, 16 bit per sample, signed.
|
||||
* For stereo chips, buffer will be filled with interleaved
|
||||
* left and right channel samples, starting with a left sample.
|
||||
* Furthermore, the samples in the left and right are summed up.
|
||||
* So if you request 4 samples from a stereo chip, you will get
|
||||
* a total of two left channel and two right channel samples.
|
||||
*/
|
||||
virtual void generateSamples(int16 *buffer, int numSamples) = 0;
|
||||
|
||||
private:
|
||||
int _baseFreq;
|
||||
|
||||
int _nextTick;
|
||||
int _samplesPerTick;
|
||||
|
||||
Audio::SoundHandle *_handle;
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
46
audio/cms.cpp
Normal file
46
audio/cms.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/cms.h"
|
||||
#include "audio/softsynth/cms.h"
|
||||
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace CMS {
|
||||
|
||||
CMS *Config::create() {
|
||||
// For now this is fixed to the DOSBox emulator.
|
||||
return new DOSBoxCMS();
|
||||
}
|
||||
|
||||
bool CMS::_hasInstance = false;
|
||||
|
||||
CMS::CMS() {
|
||||
if (_hasInstance)
|
||||
error("There are multiple CMS output instances running.");
|
||||
_hasInstance = true;
|
||||
}
|
||||
|
||||
CMS::~CMS() {
|
||||
_hasInstance = false;
|
||||
}
|
||||
|
||||
} // End of namespace CMS
|
||||
87
audio/cms.h
Normal file
87
audio/cms.h
Normal file
@@ -0,0 +1,87 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_CMS_H
|
||||
#define AUDIO_CMS_H
|
||||
|
||||
#include "audio/chip.h"
|
||||
|
||||
namespace CMS {
|
||||
|
||||
class CMS;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
/**
|
||||
* Creates a CMS driver.
|
||||
*/
|
||||
static CMS *create();
|
||||
};
|
||||
|
||||
class CMS : virtual public Audio::Chip {
|
||||
private:
|
||||
static bool _hasInstance;
|
||||
|
||||
public:
|
||||
// The default number of timer callbacks per second.
|
||||
static const int DEFAULT_CALLBACK_FREQUENCY = 250;
|
||||
|
||||
CMS();
|
||||
virtual ~CMS();
|
||||
|
||||
/**
|
||||
* Initializes the CMS emulator.
|
||||
*
|
||||
* @return true on success, false on failure
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* Reinitializes the CMS emulator
|
||||
*/
|
||||
virtual void reset() = 0;
|
||||
|
||||
/**
|
||||
* Writes a byte to the given I/O port. CMS responds to 2 sets of 2 ports:
|
||||
* 0x220/0x222 - value for the 1st/2nd chip (channels 0-5/6-B)
|
||||
* 0x221/0x223 - register for the 1st/2nd chip
|
||||
*
|
||||
* @param a port address
|
||||
* @param v value, which will be written
|
||||
*/
|
||||
virtual void write(int a, int v) = 0;
|
||||
|
||||
/**
|
||||
* Function to directly write to a specific CMS register. We allow writing
|
||||
* to secondary CMS chip registers by using register values >= 0x100.
|
||||
*
|
||||
* @param r hardware register number to write to
|
||||
* @param v value, which will be written
|
||||
*/
|
||||
virtual void writeReg(int r, int v) = 0;
|
||||
|
||||
using Audio::Chip::start;
|
||||
void start(TimerCallback *callback) { start(callback, DEFAULT_CALLBACK_FREQUENCY); }
|
||||
};
|
||||
|
||||
} // End of namespace CMS
|
||||
|
||||
#endif
|
||||
341
audio/decoders/3do.cpp
Normal file
341
audio/decoders/3do.cpp
Normal file
@@ -0,0 +1,341 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/decoders/3do.h"
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// Reuses ADPCM table
|
||||
#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable
|
||||
#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable
|
||||
|
||||
RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) {
|
||||
if (stereo) {
|
||||
warning("make3DO_ADP4Stream(): stereo currently not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (audioLengthMSecsPtr) {
|
||||
// Caller requires the milliseconds of audio
|
||||
uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample
|
||||
if (stereo) {
|
||||
audioLengthMSecs /= 2;
|
||||
}
|
||||
*audioLengthMSecsPtr = audioLengthMSecs;
|
||||
}
|
||||
|
||||
return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
|
||||
}
|
||||
|
||||
Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace)
|
||||
: _sampleRate(sampleRate), _stereo(stereo),
|
||||
_stream(stream, disposeAfterUse) {
|
||||
|
||||
_callerDecoderData = persistentSpace;
|
||||
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
|
||||
_initialRead = true;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Audio3DO_ADP4_Stream::reset() {
|
||||
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
|
||||
_streamBytesLeft = _stream->size();
|
||||
_stream->seek(0);
|
||||
}
|
||||
|
||||
bool Audio3DO_ADP4_Stream::rewind() {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) {
|
||||
int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex];
|
||||
int32 decodedSample = _curDecoderData.lastSample;
|
||||
int16 delta = currentStep >> 3;
|
||||
|
||||
if (compressedNibble & 1)
|
||||
delta += currentStep >> 2;
|
||||
|
||||
if (compressedNibble & 2)
|
||||
delta += currentStep >> 1;
|
||||
|
||||
if (compressedNibble & 4)
|
||||
delta += currentStep;
|
||||
|
||||
if (compressedNibble & 8) {
|
||||
decodedSample -= delta;
|
||||
} else {
|
||||
decodedSample += delta;
|
||||
}
|
||||
|
||||
_curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767);
|
||||
|
||||
_curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07];
|
||||
_curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1);
|
||||
|
||||
return _curDecoderData.lastSample;
|
||||
}
|
||||
|
||||
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
|
||||
int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
|
||||
int8 *byteCachePtr = nullptr;
|
||||
int byteCacheSize = 0;
|
||||
int requestedBytesLeft = 0;
|
||||
int decodedSamplesCount = 0;
|
||||
|
||||
int8 compressedByte = 0;
|
||||
|
||||
if (endOfData())
|
||||
return 0; // no more bytes left
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data over
|
||||
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
|
||||
if (_initialRead) {
|
||||
_initialRead = false;
|
||||
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
|
||||
}
|
||||
}
|
||||
|
||||
requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample
|
||||
if (requestedBytesLeft > _streamBytesLeft)
|
||||
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
|
||||
|
||||
// in case caller requests an uneven amount of samples, we will return an even amount
|
||||
|
||||
// buffering, so that direct decoding of files and such runs way faster
|
||||
while (requestedBytesLeft) {
|
||||
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
|
||||
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
|
||||
} else {
|
||||
byteCacheSize = requestedBytesLeft;
|
||||
}
|
||||
|
||||
requestedBytesLeft -= byteCacheSize;
|
||||
_streamBytesLeft -= byteCacheSize;
|
||||
|
||||
// Fill our byte cache
|
||||
_stream->read(byteCache, byteCacheSize);
|
||||
|
||||
byteCachePtr = byteCache;
|
||||
|
||||
// Mono
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
|
||||
buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4);
|
||||
decodedSamplesCount++;
|
||||
buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f);
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data back
|
||||
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
|
||||
}
|
||||
|
||||
return decodedSamplesCount;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
static const int16 audio_3DO_SDX2_SquareTable[256] = {
|
||||
-32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322,
|
||||
-27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762,
|
||||
-23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602,
|
||||
-19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842,
|
||||
-15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482,
|
||||
-12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522,
|
||||
-9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962,
|
||||
-6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802,
|
||||
-4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042,
|
||||
-2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682,
|
||||
-1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722,
|
||||
-648, -578, -512, -450, -392, -338, -288, -242, -200, -162,
|
||||
-128, -98, -72, -50, -32, -18, -8, -2, 0, 2,
|
||||
8, 18, 32, 50, 72, 98, 128, 162, 200, 242,
|
||||
288, 338, 392, 450, 512, 578, 648, 722, 800, 882,
|
||||
968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922,
|
||||
2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362,
|
||||
3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202,
|
||||
5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442,
|
||||
7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082,
|
||||
10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122,
|
||||
13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562,
|
||||
16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402,
|
||||
20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642,
|
||||
25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282,
|
||||
29768, 30258, 30752, 31250, 31752, 32258
|
||||
};
|
||||
|
||||
Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace)
|
||||
: _sampleRate(sampleRate), _stereo(stereo),
|
||||
_stream(stream, disposeAfterUse) {
|
||||
|
||||
_callerDecoderData = persistentSpace;
|
||||
memset(&_initialDecoderData, 0, sizeof(_initialDecoderData));
|
||||
_initialRead = true;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void Audio3DO_SDX2_Stream::reset() {
|
||||
memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData));
|
||||
_streamBytesLeft = _stream->size();
|
||||
_stream->seek(0);
|
||||
}
|
||||
|
||||
bool Audio3DO_SDX2_Stream::rewind() {
|
||||
reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written
|
||||
int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int8 byteCache[AUDIO_3DO_CACHE_SIZE];
|
||||
int8 *byteCachePtr = nullptr;
|
||||
int byteCacheSize = 0;
|
||||
int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample
|
||||
int decodedSamplesCount = 0;
|
||||
|
||||
int8 compressedByte = 0;
|
||||
uint8 squareTableOffset = 0;
|
||||
int16 decodedSample = 0;
|
||||
|
||||
if (endOfData())
|
||||
return 0; // no more bytes left
|
||||
|
||||
if (_stereo) {
|
||||
// We expect numSamples to be even in case of Stereo audio
|
||||
assert((numSamples & 1) == 0);
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data over
|
||||
memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
|
||||
if (_initialRead) {
|
||||
_initialRead = false;
|
||||
memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
|
||||
}
|
||||
}
|
||||
|
||||
requestedBytesLeft = numSamples;
|
||||
if (requestedBytesLeft > _streamBytesLeft)
|
||||
requestedBytesLeft = _streamBytesLeft; // not enough bytes left
|
||||
|
||||
// buffering, so that direct decoding of files and such runs way faster
|
||||
while (requestedBytesLeft) {
|
||||
if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) {
|
||||
byteCacheSize = AUDIO_3DO_CACHE_SIZE;
|
||||
} else {
|
||||
byteCacheSize = requestedBytesLeft;
|
||||
}
|
||||
|
||||
requestedBytesLeft -= byteCacheSize;
|
||||
_streamBytesLeft -= byteCacheSize;
|
||||
|
||||
// Fill our byte cache
|
||||
_stream->read(byteCache, byteCacheSize);
|
||||
|
||||
byteCachePtr = byteCache;
|
||||
|
||||
if (!_stereo) {
|
||||
// Mono
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
squareTableOffset = compressedByte + 128;
|
||||
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample1 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample1 = decodedSample;
|
||||
|
||||
buffer[decodedSamplesCount] = decodedSample;
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
} else {
|
||||
// Stereo
|
||||
while (byteCacheSize) {
|
||||
compressedByte = *byteCachePtr++;
|
||||
byteCacheSize--;
|
||||
squareTableOffset = compressedByte + 128;
|
||||
|
||||
if (!(decodedSamplesCount & 1)) {
|
||||
// First channel
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample1 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample1 = decodedSample;
|
||||
} else {
|
||||
// Second channel
|
||||
if (!(compressedByte & 1))
|
||||
_curDecoderData.lastSample2 = 0;
|
||||
|
||||
decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
|
||||
_curDecoderData.lastSample2 = decodedSample;
|
||||
}
|
||||
|
||||
buffer[decodedSamplesCount] = decodedSample;
|
||||
decodedSamplesCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_callerDecoderData) {
|
||||
// copy caller decoder data back
|
||||
memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
|
||||
}
|
||||
|
||||
return decodedSamplesCount;
|
||||
}
|
||||
|
||||
RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) {
|
||||
if (stereo) {
|
||||
if (stream->size() & 1) {
|
||||
warning("make3DO_SDX2Stream(): stereo data is uneven size");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (audioLengthMSecsPtr) {
|
||||
// Caller requires the milliseconds of audio
|
||||
uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample
|
||||
if (stereo) {
|
||||
audioLengthMSecs /= 2;
|
||||
}
|
||||
*audioLengthMSecsPtr = audioLengthMSecs;
|
||||
}
|
||||
|
||||
return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
150
audio/decoders/3do.h
Normal file
150
audio/decoders/3do.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - sherlock (3DO version of Serrated Scalpel)
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_3DO_SDX2_H
|
||||
#define AUDIO_3DO_SDX2_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// amount of bytes to be used within the decoder classes as buffers
|
||||
#define AUDIO_3DO_CACHE_SIZE 1024
|
||||
|
||||
// persistent spaces
|
||||
struct audio_3DO_ADP4_PersistentSpace {
|
||||
int16 lastSample;
|
||||
int16 stepIndex;
|
||||
};
|
||||
|
||||
struct audio_3DO_SDX2_PersistentSpace {
|
||||
int16 lastSample1;
|
||||
int16 lastSample2;
|
||||
};
|
||||
|
||||
class Audio3DO_ADP4_Stream : public RewindableAudioStream {
|
||||
public:
|
||||
Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace);
|
||||
|
||||
protected:
|
||||
const uint16 _sampleRate;
|
||||
const bool _stereo;
|
||||
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream;
|
||||
int32 _streamBytesLeft;
|
||||
|
||||
void reset();
|
||||
bool rewind();
|
||||
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
|
||||
bool isStereo() const { return _stereo; }
|
||||
int getRate() const { return _sampleRate; }
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool _initialRead;
|
||||
audio_3DO_ADP4_PersistentSpace *_callerDecoderData;
|
||||
audio_3DO_ADP4_PersistentSpace _initialDecoderData;
|
||||
audio_3DO_ADP4_PersistentSpace _curDecoderData;
|
||||
|
||||
private:
|
||||
int16 decodeSample(byte compressedNibble);
|
||||
};
|
||||
|
||||
class Audio3DO_SDX2_Stream : public RewindableAudioStream {
|
||||
public:
|
||||
Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr);
|
||||
|
||||
protected:
|
||||
const uint16 _sampleRate;
|
||||
const bool _stereo;
|
||||
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream;
|
||||
int32 _streamBytesLeft;
|
||||
|
||||
void reset();
|
||||
bool rewind();
|
||||
bool endOfData() const { return (_stream->pos() >= _stream->size()); }
|
||||
bool isStereo() const { return _stereo; }
|
||||
int getRate() const { return _sampleRate; }
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
bool _initialRead;
|
||||
audio_3DO_SDX2_PersistentSpace *_callerDecoderData;
|
||||
audio_3DO_SDX2_PersistentSpace _initialDecoderData;
|
||||
audio_3DO_SDX2_PersistentSpace _curDecoderData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
|
||||
* @sampleRate sample rate
|
||||
* @stereo if it's stereo or mono
|
||||
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
|
||||
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
|
||||
* @persistentSpacePtr pointer to the persistent space structure
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
RewindableAudioStream *make3DO_ADP4AudioStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
uint16 sampleRate,
|
||||
bool stereo,
|
||||
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
|
||||
audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL
|
||||
);
|
||||
|
||||
/**
|
||||
* Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the 3DO SDX2 data
|
||||
* @sampleRate sample rate
|
||||
* @stereo if it's stereo or mono
|
||||
* @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds
|
||||
* @disposeAfterUse disposeAfterUse whether to delete the stream after use
|
||||
* @persistentSpacePtr pointer to the persistent space structure
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
RewindableAudioStream *make3DO_SDX2AudioStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
uint16 sampleRate,
|
||||
bool stereo,
|
||||
uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
|
||||
audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL
|
||||
);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
130
audio/decoders/aac.cpp
Normal file
130
audio/decoders/aac.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/aac.h"
|
||||
|
||||
#ifdef USE_FAAD
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/codec.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
#include <neaacdec.h>
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AACDecoder : public Codec {
|
||||
public:
|
||||
AACDecoder(Common::SeekableReadStream *extraData,
|
||||
DisposeAfterUse::Flag disposeExtraData);
|
||||
~AACDecoder();
|
||||
|
||||
AudioStream *decodeFrame(Common::SeekableReadStream &stream) override;
|
||||
|
||||
private:
|
||||
NeAACDecHandle _handle;
|
||||
byte _channels;
|
||||
unsigned long _rate;
|
||||
};
|
||||
|
||||
AACDecoder::AACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
// Open the library
|
||||
_handle = NeAACDecOpen();
|
||||
|
||||
// Configure the library to our needs
|
||||
NeAACDecConfigurationPtr conf = NeAACDecGetCurrentConfiguration(_handle);
|
||||
conf->outputFormat = FAAD_FMT_16BIT; // We only support 16bit audio
|
||||
conf->downMatrix = 1; // Convert from 5.1 to stereo if required
|
||||
NeAACDecSetConfiguration(_handle, conf);
|
||||
|
||||
// Copy the extra data to a buffer
|
||||
extraData->seek(0);
|
||||
byte *extraDataBuf = new byte[extraData->size()];
|
||||
extraData->read(extraDataBuf, extraData->size());
|
||||
|
||||
// Initialize with our extra data
|
||||
// NOTE: This code assumes the extra data is coming from an MPEG-4 file!
|
||||
int err = NeAACDecInit2(_handle, extraDataBuf, extraData->size(), &_rate, &_channels);
|
||||
delete[] extraDataBuf;
|
||||
|
||||
if (err < 0)
|
||||
error("Could not initialize AAC decoder: %s", NeAACDecGetErrorMessage(err));
|
||||
|
||||
if (disposeExtraData == DisposeAfterUse::YES)
|
||||
delete extraData;
|
||||
}
|
||||
|
||||
AACDecoder::~AACDecoder() {
|
||||
NeAACDecClose(_handle);
|
||||
}
|
||||
|
||||
AudioStream *AACDecoder::decodeFrame(Common::SeekableReadStream &stream) {
|
||||
// read everything into a buffer
|
||||
uint32 inBufferPos = 0;
|
||||
uint32 inBufferSize = stream.size();
|
||||
byte *inBuffer = new byte[inBufferSize];
|
||||
stream.read(inBuffer, inBufferSize);
|
||||
|
||||
QueuingAudioStream *audioStream = makeQueuingAudioStream(_rate, _channels == 2);
|
||||
|
||||
// Decode until we have enough samples (or there's no more left)
|
||||
while (inBufferPos < inBufferSize) {
|
||||
NeAACDecFrameInfo frameInfo;
|
||||
void *decodedSamples = NeAACDecDecode(_handle, &frameInfo, inBuffer + inBufferPos, inBufferSize - inBufferPos);
|
||||
|
||||
if (frameInfo.error != 0)
|
||||
error("Failed to decode AAC frame: %s", NeAACDecGetErrorMessage(frameInfo.error));
|
||||
|
||||
byte *buffer = (byte *)malloc(frameInfo.samples * 2);
|
||||
memcpy(buffer, decodedSamples, frameInfo.samples * 2);
|
||||
|
||||
byte flags = FLAG_16BITS;
|
||||
|
||||
if (_channels == 2)
|
||||
flags |= FLAG_STEREO;
|
||||
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
|
||||
audioStream->queueBuffer(buffer, frameInfo.samples * 2, DisposeAfterUse::YES, flags);
|
||||
|
||||
inBufferPos += frameInfo.bytesconsumed;
|
||||
}
|
||||
|
||||
audioStream->finish();
|
||||
return audioStream;
|
||||
}
|
||||
|
||||
// Factory function
|
||||
Codec *makeAACDecoder(Common::SeekableReadStream *extraData, DisposeAfterUse::Flag disposeExtraData) {
|
||||
return new AACDecoder(extraData, disposeExtraData);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FAAD
|
||||
61
audio/decoders/aac.h
Normal file
61
audio/decoders/aac.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - groovie
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_AAC_H
|
||||
#define AUDIO_AAC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef USE_FAAD
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class Codec;
|
||||
|
||||
/**
|
||||
* Create a new Codec for decoding AAC data of an MPEG-4 file in the given stream.
|
||||
*
|
||||
* @note This should *only* be called by our QuickTime/MPEG-4 decoder since it relies
|
||||
* on the MPEG-4 extra data. If you want to decode a file using AAC, go use
|
||||
* makeQuickTimeStream() instead!
|
||||
* @param extraData the SeekableReadStream from which to read the AAC extra data
|
||||
* @param disposeExtraData whether to delete the extra data stream after use
|
||||
* @return a new Codec, or NULL, if an error occurred
|
||||
*/
|
||||
Codec *makeAACDecoder(
|
||||
Common::SeekableReadStream *extraData,
|
||||
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FAAD
|
||||
#endif // #ifndef AUDIO_AAC_H
|
||||
202
audio/decoders/ac3.cpp
Normal file
202
audio/decoders/ac3.cpp
Normal file
@@ -0,0 +1,202 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/ac3.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
extern "C" {
|
||||
#include <a52dec/a52.h>
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AC3Stream : public PacketizedAudioStream {
|
||||
public:
|
||||
AC3Stream(double decibel);
|
||||
~AC3Stream();
|
||||
|
||||
bool init(Common::SeekableReadStream &firstPacket);
|
||||
void deinit();
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override { return _audStream->readBuffer(buffer, numSamples); }
|
||||
bool isStereo() const override { return _audStream->isStereo(); }
|
||||
int getRate() const override { return _audStream->getRate(); }
|
||||
bool endOfData() const override { return _audStream->endOfData(); }
|
||||
bool endOfStream() const override { return _audStream->endOfStream(); }
|
||||
|
||||
// PacketizedAudioStream API
|
||||
void queuePacket(Common::SeekableReadStream *data) override;
|
||||
void finish() override { _audStream->finish(); }
|
||||
|
||||
private:
|
||||
Common::ScopedPtr<QueuingAudioStream> _audStream;
|
||||
a52_state_t *_a52State;
|
||||
uint32 _frameSize;
|
||||
byte _inBuf[4096];
|
||||
byte *_inBufPtr;
|
||||
int _flags;
|
||||
int _sampleRate;
|
||||
double _audioGain;
|
||||
};
|
||||
|
||||
AC3Stream::AC3Stream(double decibel) : _a52State(nullptr), _frameSize(0), _inBufPtr(nullptr), _flags(0), _sampleRate(0) {
|
||||
_audioGain = pow(2, decibel / 6);
|
||||
}
|
||||
|
||||
AC3Stream::~AC3Stream() {
|
||||
deinit();
|
||||
}
|
||||
|
||||
enum {
|
||||
HEADER_SIZE = 7
|
||||
};
|
||||
|
||||
bool AC3Stream::init(Common::SeekableReadStream &firstPacket) {
|
||||
deinit();
|
||||
|
||||
// In theory, I should pass mm_accel() to a52_init(), but I don't know
|
||||
// where that's supposed to be defined.
|
||||
_a52State = a52_init(0);
|
||||
|
||||
// Go through the header to find sync
|
||||
byte buf[HEADER_SIZE];
|
||||
_sampleRate = -1;
|
||||
|
||||
for (uint i = 0; i < firstPacket.size() - sizeof(buf); i++) {
|
||||
int flags, bitRate;
|
||||
firstPacket.seek(i);
|
||||
firstPacket.read(buf, sizeof(buf));
|
||||
|
||||
if (a52_syncinfo(buf, &flags, &_sampleRate, &bitRate) > 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// Ensure we have a valid sample rate
|
||||
if (_sampleRate <= 0) {
|
||||
deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
_audStream.reset(makeQueuingAudioStream(_sampleRate, true));
|
||||
_inBufPtr = _inBuf;
|
||||
_flags = 0;
|
||||
_frameSize = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
void AC3Stream::deinit() {
|
||||
if (!_a52State)
|
||||
return;
|
||||
|
||||
_audStream.reset();
|
||||
a52_free(_a52State);
|
||||
_a52State = nullptr;
|
||||
}
|
||||
|
||||
void AC3Stream::queuePacket(Common::SeekableReadStream *data) {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> packet(data);
|
||||
|
||||
while (packet->pos() < packet->size()) {
|
||||
uint32 leftSize = packet->size() - packet->pos();
|
||||
uint32 len = _inBufPtr - _inBuf;
|
||||
if (_frameSize == 0) {
|
||||
// No header seen: find one
|
||||
len = HEADER_SIZE - len;
|
||||
if (len > leftSize)
|
||||
len = leftSize;
|
||||
packet->read(_inBufPtr, len);
|
||||
leftSize -= len;
|
||||
_inBufPtr += len;
|
||||
if ((_inBufPtr - _inBuf) == HEADER_SIZE) {
|
||||
int sampleRate, bitRate;
|
||||
len = a52_syncinfo(_inBuf, &_flags, &sampleRate, &bitRate);
|
||||
if (len == 0) {
|
||||
memmove(_inBuf, _inBuf + 1, HEADER_SIZE - 1);
|
||||
_inBufPtr--;
|
||||
} else {
|
||||
_frameSize = len;
|
||||
}
|
||||
}
|
||||
} else if (len < _frameSize) {
|
||||
len = _frameSize - len;
|
||||
if (len > leftSize)
|
||||
len = leftSize;
|
||||
|
||||
assert(len < sizeof(_inBuf) - (_inBufPtr - _inBuf));
|
||||
packet->read(_inBufPtr, len);
|
||||
leftSize -= len;
|
||||
_inBufPtr += len;
|
||||
} else {
|
||||
// TODO: Eventually support more than just stereo max
|
||||
int flags = A52_STEREO | A52_ADJUST_LEVEL;
|
||||
sample_t level = 32767 * _audioGain;
|
||||
|
||||
if (a52_frame(_a52State, _inBuf, &flags, &level, 0) != 0)
|
||||
error("Frame fail");
|
||||
|
||||
int16 *outputBuffer = (int16 *)malloc(6 * 256 * 2 * 2);
|
||||
int16 *outputPtr = outputBuffer;
|
||||
int outputLength = 0;
|
||||
for (int i = 0; i < 6; i++) {
|
||||
if (a52_block(_a52State) == 0) {
|
||||
sample_t *samples = a52_samples(_a52State);
|
||||
for (int j = 0; j < 256; j++) {
|
||||
*outputPtr++ = (int16)CLIP<sample_t>(samples[j], -32768, 32767);
|
||||
*outputPtr++ = (int16)CLIP<sample_t>(samples[j + 256], -32768, 32767);
|
||||
}
|
||||
|
||||
outputLength += 1024;
|
||||
}
|
||||
}
|
||||
|
||||
if (outputLength > 0) {
|
||||
flags = FLAG_STEREO | FLAG_16BITS;
|
||||
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
|
||||
_audStream->queueBuffer((byte *)outputBuffer, outputLength, DisposeAfterUse::YES, flags);
|
||||
}
|
||||
|
||||
_inBufPtr = _inBuf;
|
||||
_frameSize = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel) {
|
||||
Common::ScopedPtr<AC3Stream> stream(new AC3Stream(decibel));
|
||||
if (!stream->init(firstPacket))
|
||||
return nullptr;
|
||||
|
||||
return stream.release();
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
50
audio/decoders/ac3.h
Normal file
50
audio/decoders/ac3.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_AC3_H
|
||||
#define AUDIO_DECODERS_AC3_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifdef USE_A52
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
} // End of namespace Common
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class PacketizedAudioStream;
|
||||
|
||||
/**
|
||||
* Create a PacketizedAudioStream that decodes AC-3 sound
|
||||
*
|
||||
* @param firstPacket The stream containing the first packet of data
|
||||
* @return A new PacketizedAudioStream, or NULL on error
|
||||
*/
|
||||
PacketizedAudioStream *makeAC3Stream(Common::SeekableReadStream &firstPacket, double decibel = 0.0);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
619
audio/decoders/adpcm.cpp
Normal file
619
audio/decoders/adpcm.cpp
Normal file
@@ -0,0 +1,619 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/decoders/adpcm.h"
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// Routines to convert 12 bit linear samples to the
|
||||
// Dialogic or Oki ADPCM coding format aka VOX.
|
||||
// See also
|
||||
// <https://web.archive.org/web/20050421220515/http://www.comptek.ru/telephony/tnotes/tt1-13.html>
|
||||
//
|
||||
// IMA ADPCM support is based on
|
||||
// <https://wiki.multimedia.cx/index.php?title=IMA_ADPCM>
|
||||
//
|
||||
// In addition, also MS IMA ADPCM is supported. See
|
||||
// <https://wiki.multimedia.cx/index.php?title=Microsoft_IMA_ADPCM>.
|
||||
//
|
||||
// XA ADPCM support is based on FFmpeg/libav
|
||||
|
||||
ADPCMStream::ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: _stream(stream, disposeAfterUse),
|
||||
_startpos(stream->pos()),
|
||||
_endpos(_startpos + size),
|
||||
_channels(channels),
|
||||
_blockAlign(blockAlign),
|
||||
_rate(rate) {
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void ADPCMStream::reset() {
|
||||
memset(&_status, 0, sizeof(_status));
|
||||
_blockPos[0] = _blockPos[1] = _blockAlign; // To make sure first header is read
|
||||
}
|
||||
|
||||
bool ADPCMStream::rewind() {
|
||||
// TODO: Error checking.
|
||||
reset();
|
||||
_stream->seek(_startpos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
int Oki_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
byte data;
|
||||
|
||||
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
|
||||
if (_decodedSampleCount == 0) {
|
||||
data = _stream->readByte();
|
||||
_decodedSamples[0] = decodeOKI((data >> 4) & 0x0f);
|
||||
_decodedSamples[1] = decodeOKI((data >> 0) & 0x0f);
|
||||
_decodedSampleCount = 2;
|
||||
}
|
||||
|
||||
// (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
|
||||
buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)];
|
||||
_decodedSampleCount--;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
static const int16 okiStepSize[49] = {
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552
|
||||
};
|
||||
|
||||
// Decode Linear to ADPCM
|
||||
int16 Oki_ADPCMStream::decodeOKI(byte code) {
|
||||
int16 diff, E, samp;
|
||||
|
||||
E = (2 * (code & 0x7) + 1) * okiStepSize[_status.ima_ch[0].stepIndex] / 8;
|
||||
diff = (code & 0x08) ? -E : E;
|
||||
samp = _status.ima_ch[0].last + diff;
|
||||
// Clip the values to +/- 2^11 (supposed to be 12 bits)
|
||||
samp = CLIP<int16>(samp, -2048, 2047);
|
||||
|
||||
_status.ima_ch[0].last = samp;
|
||||
_status.ima_ch[0].stepIndex += _stepAdjustTable[code];
|
||||
_status.ima_ch[0].stepIndex = CLIP<int32>(_status.ima_ch[0].stepIndex, 0, ARRAYSIZE(okiStepSize) - 1);
|
||||
|
||||
// * 16 effectively converts 12-bit input to 16-bit output
|
||||
return samp * 16;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
int XA_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
byte *data = new byte[128];
|
||||
|
||||
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
|
||||
if (_decodedSampleCount == 0) {
|
||||
uint32 bytesLeft = _stream->size() - _stream->pos();
|
||||
if (bytesLeft < 128) {
|
||||
_stream->skip(bytesLeft);
|
||||
memset(&buffer[samples], 0, (numSamples - samples) * sizeof(uint16));
|
||||
samples = numSamples;
|
||||
break;
|
||||
}
|
||||
_stream->read(data, 128);
|
||||
decodeXA(data);
|
||||
_decodedSampleIndex = 0;
|
||||
}
|
||||
|
||||
// _decodedSamples acts as a FIFO of depth 2 or 4;
|
||||
buffer[samples] = _decodedSamples[_decodedSampleIndex++];
|
||||
_decodedSampleCount--;
|
||||
}
|
||||
|
||||
delete[] data;
|
||||
return samples;
|
||||
}
|
||||
|
||||
static const int s_xaTable[5][2] = {
|
||||
{ 0, 0 },
|
||||
{ 60, 0 },
|
||||
{ 115, -52 },
|
||||
{ 98, -55 },
|
||||
{ 122, -60 }
|
||||
};
|
||||
|
||||
void XA_ADPCMStream::decodeXA(const byte *src) {
|
||||
int16 *leftChannel = _decodedSamples;
|
||||
int16 *rightChannel = _decodedSamples + 1;
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int shift = 12 - (src[4 + i * 2] & 0xf);
|
||||
int filter = src[4 + i * 2] >> 4;
|
||||
int f0 = s_xaTable[filter][0];
|
||||
int f1 = s_xaTable[filter][1];
|
||||
int16 s_1 = _status.ima_ch[0].sample[0];
|
||||
int16 s_2 = _status.ima_ch[0].sample[1];
|
||||
|
||||
for (int j = 0; j < 28; j++) {
|
||||
byte d = src[16 + i + j * 4];
|
||||
int t = (int8)(d << 4) >> 4;
|
||||
int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
|
||||
s_2 = s_1;
|
||||
s_1 = CLIP<int>(s, -32768, 32767);
|
||||
*leftChannel = s_1;
|
||||
leftChannel += _channels;
|
||||
_decodedSampleCount++;
|
||||
}
|
||||
|
||||
if (_channels == 2) {
|
||||
_status.ima_ch[0].sample[0] = s_1;
|
||||
_status.ima_ch[0].sample[1] = s_2;
|
||||
s_1 = _status.ima_ch[1].sample[0];
|
||||
s_2 = _status.ima_ch[1].sample[1];
|
||||
}
|
||||
|
||||
shift = 12 - (src[5 + i * 2] & 0xf);
|
||||
filter = src[5 + i * 2] >> 4;
|
||||
f0 = s_xaTable[filter][0];
|
||||
f1 = s_xaTable[filter][1];
|
||||
|
||||
for (int j = 0; j < 28; j++) {
|
||||
byte d = src[16 + i + j * 4];
|
||||
int t = (int8)d >> 4;
|
||||
int s = (t << shift) + ((s_1 * f0 + s_2 * f1 + 32) >> 6);
|
||||
s_2 = s_1;
|
||||
s_1 = CLIP<int>(s, -32768, 32767);
|
||||
|
||||
if (_channels == 2) {
|
||||
*rightChannel = s_1;
|
||||
rightChannel += 2;
|
||||
} else {
|
||||
*leftChannel++ = s_1;
|
||||
}
|
||||
_decodedSampleCount++;
|
||||
}
|
||||
|
||||
if (_channels == 2) {
|
||||
_status.ima_ch[1].sample[0] = s_1;
|
||||
_status.ima_ch[1].sample[1] = s_2;
|
||||
} else {
|
||||
_status.ima_ch[0].sample[0] = s_1;
|
||||
_status.ima_ch[0].sample[1] = s_2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
int DVI_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
byte data;
|
||||
|
||||
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
|
||||
if (_decodedSampleCount == 0) {
|
||||
data = _stream->readByte();
|
||||
_decodedSamples[0] = decodeIMA((data >> 4) & 0x0f, 0);
|
||||
_decodedSamples[1] = decodeIMA((data >> 0) & 0x0f, _channels == 2 ? 1 : 0);
|
||||
_decodedSampleCount = 2;
|
||||
}
|
||||
|
||||
// (1 - (count - 1)) ensures that _decodedSamples acts as a FIFO of depth 2
|
||||
buffer[samples] = _decodedSamples[1 - (_decodedSampleCount - 1)];
|
||||
_decodedSampleCount--;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
// Need to write at least one samples per channel
|
||||
assert((numSamples % _channels) == 0);
|
||||
|
||||
// Current sample positions
|
||||
int samples[2] = { 0, 0};
|
||||
|
||||
// Number of samples per channel
|
||||
int chanSamples = numSamples / _channels;
|
||||
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
_stream->seek(_streamPos[i]);
|
||||
|
||||
while ((samples[i] < chanSamples) &&
|
||||
// Last byte read and a new one needed
|
||||
!((_stream->eos() || (_stream->pos() >= _endpos)) && (_chunkPos[i] == 0))) {
|
||||
|
||||
if (_blockPos[i] == _blockAlign) {
|
||||
// 2 byte header per block
|
||||
uint16 temp = _stream->readUint16BE();
|
||||
|
||||
// First 9 bits are the upper bits of the predictor
|
||||
_status.ima_ch[i].last = (int16) (temp & 0xFF80);
|
||||
// Lower 7 bits are the step index
|
||||
_status.ima_ch[i].stepIndex = temp & 0x007F;
|
||||
|
||||
// Clip the step index
|
||||
_status.ima_ch[i].stepIndex = CLIP<int32>(_status.ima_ch[i].stepIndex, 0, 88);
|
||||
|
||||
_blockPos[i] = 2;
|
||||
}
|
||||
|
||||
if (_chunkPos[i] == 0) {
|
||||
// Decode data
|
||||
byte data = _stream->readByte();
|
||||
_buffer[i][0] = decodeIMA(data & 0x0F, i);
|
||||
_buffer[i][1] = decodeIMA(data >> 4, i);
|
||||
}
|
||||
|
||||
// The original is interleaved block-wise, we want it sample-wise
|
||||
buffer[_channels * samples[i] + i] = _buffer[i][_chunkPos[i]];
|
||||
|
||||
if (++_chunkPos[i] > 1) {
|
||||
// We're about to decode the next byte, so advance the block position
|
||||
_chunkPos[i] = 0;
|
||||
_blockPos[i]++;
|
||||
}
|
||||
|
||||
samples[i]++;
|
||||
|
||||
if (_channels == 2)
|
||||
if (_blockPos[i] == _blockAlign)
|
||||
// We're at the end of the block.
|
||||
// Since the channels are interleaved, skip the next block
|
||||
_stream->skip(MIN<uint32>(_blockAlign, _endpos - _stream->pos()));
|
||||
|
||||
_streamPos[i] = _stream->pos();
|
||||
}
|
||||
}
|
||||
|
||||
return samples[0] + samples[1];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
int MSIma_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
// Need to write at least one sample per channel
|
||||
assert((numSamples % _channels) == 0);
|
||||
|
||||
int samples = 0;
|
||||
|
||||
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
// read block header
|
||||
_status.ima_ch[i].last = _stream->readSint16LE();
|
||||
_status.ima_ch[i].stepIndex = _stream->readSint16LE();
|
||||
}
|
||||
|
||||
_blockPos[0] = _channels * 4;
|
||||
}
|
||||
|
||||
// Decode a set of samples
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
// The stream encodes four bytes per channel at a time
|
||||
for (int j = 0; j < 4; j++) {
|
||||
byte data = _stream->readByte();
|
||||
_blockPos[0]++;
|
||||
_buffer[i][j * 2] = decodeIMA(data & 0x0f, i);
|
||||
_buffer[i][j * 2 + 1] = decodeIMA((data >> 4) & 0x0f, i);
|
||||
_samplesLeft[i] += 2;
|
||||
}
|
||||
}
|
||||
|
||||
while (samples < numSamples && _samplesLeft[0] != 0) {
|
||||
for (int i = 0; i < _channels; i++) {
|
||||
buffer[samples + i] = _buffer[i][8 - _samplesLeft[i]];
|
||||
_samplesLeft[i]--;
|
||||
}
|
||||
|
||||
samples += _channels;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
static const int MSADPCMAdaptCoeff1[] = {
|
||||
256, 512, 0, 192, 240, 460, 392
|
||||
};
|
||||
|
||||
static const int MSADPCMAdaptCoeff2[] = {
|
||||
0, -256, 0, 64, 0, -208, -232
|
||||
};
|
||||
|
||||
static const int MSADPCMAdaptationTable[] = {
|
||||
230, 230, 230, 230, 307, 409, 512, 614,
|
||||
768, 614, 512, 409, 307, 230, 230, 230
|
||||
};
|
||||
|
||||
int16 MS_ADPCMStream::decodeMS(ADPCMChannelStatus *c, byte code) {
|
||||
int32 predictor;
|
||||
|
||||
predictor = (((c->sample1) * (c->coeff1)) + ((c->sample2) * (c->coeff2))) / 256;
|
||||
predictor += (signed)((code & 0x08) ? (code - 0x10) : (code)) * c->delta;
|
||||
|
||||
predictor = CLIP<int32>(predictor, -32768, 32767);
|
||||
|
||||
c->sample2 = c->sample1;
|
||||
c->sample1 = predictor;
|
||||
c->delta = (MSADPCMAdaptationTable[(int)code] * c->delta) >> 8;
|
||||
|
||||
if (c->delta < 16)
|
||||
c->delta = 16;
|
||||
|
||||
return (int16)predictor;
|
||||
}
|
||||
|
||||
int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples;
|
||||
byte data;
|
||||
int i;
|
||||
|
||||
for (samples = 0; samples < numSamples && !endOfData(); samples++) {
|
||||
if (_decodedSampleCount == 0) {
|
||||
if (_blockPos[0] == _blockAlign) {
|
||||
// read block header
|
||||
for (i = 0; i < _channels; i++) {
|
||||
_status.ch[i].predictor = CLIP(_stream->readByte(), (byte)0, (byte)6);
|
||||
_status.ch[i].coeff1 = MSADPCMAdaptCoeff1[_status.ch[i].predictor];
|
||||
_status.ch[i].coeff2 = MSADPCMAdaptCoeff2[_status.ch[i].predictor];
|
||||
}
|
||||
|
||||
for (i = 0; i < _channels; i++)
|
||||
_status.ch[i].delta = _stream->readSint16LE();
|
||||
|
||||
for (i = 0; i < _channels; i++)
|
||||
_status.ch[i].sample1 = _stream->readSint16LE();
|
||||
|
||||
for (i = 0; i < _channels; i++)
|
||||
_decodedSamples[_decodedSampleCount++] = _status.ch[i].sample2 = _stream->readSint16LE();
|
||||
|
||||
for (i = 0; i < _channels; i++)
|
||||
_decodedSamples[_decodedSampleCount++] = _status.ch[i].sample1;
|
||||
|
||||
_blockPos[0] = _channels * 7;
|
||||
} else {
|
||||
data = _stream->readByte();
|
||||
_blockPos[0]++;
|
||||
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[0], (data >> 4) & 0x0f);
|
||||
_decodedSamples[_decodedSampleCount++] = decodeMS(&_status.ch[_channels - 1], data & 0x0f);
|
||||
}
|
||||
_decodedSampleIndex = 0;
|
||||
}
|
||||
|
||||
// _decodedSamples acts as a FIFO of depth 2 or 4;
|
||||
buffer[samples] = _decodedSamples[_decodedSampleIndex++];
|
||||
_decodedSampleCount--;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
|
||||
#define DK3_READ_NIBBLE(channelNo) \
|
||||
do { \
|
||||
if (_topNibble) { \
|
||||
_nibble = _lastByte >> 4; \
|
||||
_topNibble = false; \
|
||||
} else { \
|
||||
_lastByte = _stream->readByte(); \
|
||||
_nibble = _lastByte & 0xf; \
|
||||
_topNibble = true; \
|
||||
--blockBytesLeft; \
|
||||
--audioBytesLeft; \
|
||||
} \
|
||||
decodeIMA(_nibble, channelNo); \
|
||||
} while(0)
|
||||
|
||||
int DK3_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
assert((numSamples % 4) == 0);
|
||||
|
||||
const uint32 startOffset = _stream->pos() % _blockAlign;
|
||||
uint32 audioBytesLeft = _endpos - _stream->pos();
|
||||
uint32 blockBytesLeft;
|
||||
if (startOffset != 0) {
|
||||
blockBytesLeft = _blockAlign - startOffset;
|
||||
} else {
|
||||
blockBytesLeft = 0;
|
||||
}
|
||||
|
||||
int samples = 0;
|
||||
while (samples < numSamples && audioBytesLeft) {
|
||||
if (blockBytesLeft == 0) {
|
||||
blockBytesLeft = MIN(_blockAlign, audioBytesLeft);
|
||||
_topNibble = false;
|
||||
|
||||
if (blockBytesLeft < 16) {
|
||||
warning("Truncated DK3 ADPCM block header");
|
||||
break;
|
||||
}
|
||||
|
||||
_stream->skip(2);
|
||||
const uint16 rate = _stream->readUint16LE();
|
||||
assert(rate == getRate());
|
||||
(void)rate;
|
||||
_stream->skip(6);
|
||||
|
||||
// Get predictor for both sum/diff channels
|
||||
_status.ima_ch[0].last = _stream->readSint16LE();
|
||||
_status.ima_ch[1].last = _stream->readSint16LE();
|
||||
|
||||
// Get index for both sum/diff channels
|
||||
_status.ima_ch[0].stepIndex = _stream->readByte();
|
||||
_status.ima_ch[1].stepIndex = _stream->readByte();
|
||||
assert(_status.ima_ch[0].stepIndex < ARRAYSIZE(_imaTable));
|
||||
assert(_status.ima_ch[1].stepIndex < ARRAYSIZE(_imaTable));
|
||||
|
||||
blockBytesLeft -= 16;
|
||||
audioBytesLeft -= 16;
|
||||
}
|
||||
|
||||
DK3_READ_NIBBLE(0);
|
||||
DK3_READ_NIBBLE(1);
|
||||
|
||||
*buffer++ = _status.ima_ch[0].last + _status.ima_ch[1].last;
|
||||
*buffer++ = _status.ima_ch[0].last - _status.ima_ch[1].last;
|
||||
|
||||
DK3_READ_NIBBLE(0);
|
||||
|
||||
*buffer++ = _status.ima_ch[0].last + _status.ima_ch[1].last;
|
||||
*buffer++ = _status.ima_ch[0].last - _status.ima_ch[1].last;
|
||||
|
||||
samples += 4;
|
||||
|
||||
// if the last sample of a block ends on an odd byte, the encoder adds
|
||||
// an extra alignment byte
|
||||
if (!_topNibble && blockBytesLeft == 1) {
|
||||
_stream->skip(1);
|
||||
--blockBytesLeft;
|
||||
--audioBytesLeft;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
#undef DK3_READ_NIBBLE
|
||||
|
||||
#pragma mark -
|
||||
|
||||
|
||||
// This table is used to adjust the step for use on the next sample.
|
||||
// We could half the table, but since the lookup index used is always
|
||||
// a 4-bit nibble, it's more efficient to just keep it as it is.
|
||||
const int16 ADPCMStream::_stepAdjustTable[16] = {
|
||||
-1, -1, -1, -1, 2, 4, 6, 8,
|
||||
-1, -1, -1, -1, 2, 4, 6, 8
|
||||
};
|
||||
|
||||
const int16 Ima_ADPCMStream::_imaTable[89] = {
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
16, 17, 19, 21, 23, 25, 28, 31,
|
||||
34, 37, 41, 45, 50, 55, 60, 66,
|
||||
73, 80, 88, 97, 107, 118, 130, 143,
|
||||
157, 173, 190, 209, 230, 253, 279, 307,
|
||||
337, 371, 408, 449, 494, 544, 598, 658,
|
||||
724, 796, 876, 963, 1060, 1166, 1282, 1411,
|
||||
1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024,
|
||||
3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484,
|
||||
7132, 7845, 8630, 9493,10442,11487,12635,13899,
|
||||
15289,16818,18500,20350,22385,24623,27086,29794,
|
||||
32767
|
||||
};
|
||||
|
||||
int16 Ima_ADPCMStream::decodeIMA(byte code, int channel) {
|
||||
int32 E = (2 * (code & 0x7) + 1) * _imaTable[_status.ima_ch[channel].stepIndex] / 8;
|
||||
int32 diff = (code & 0x08) ? -E : E;
|
||||
int32 samp = CLIP<int32>(_status.ima_ch[channel].last + diff, -32768, 32767);
|
||||
|
||||
_status.ima_ch[channel].last = samp;
|
||||
_status.ima_ch[channel].stepIndex += _stepAdjustTable[code];
|
||||
_status.ima_ch[channel].stepIndex = CLIP<int32>(_status.ima_ch[channel].stepIndex, 0, ARRAYSIZE(_imaTable) - 1);
|
||||
|
||||
return samp;
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, ADPCMType type, int rate, int channels, uint32 blockAlign) {
|
||||
// If size is 0, report the entire size of the stream
|
||||
if (!size)
|
||||
size = stream->size();
|
||||
|
||||
switch (type) {
|
||||
case kADPCMOki:
|
||||
return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMMSIma:
|
||||
return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMMS:
|
||||
return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMDVI:
|
||||
return new DVI_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMApple:
|
||||
return new Apple_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMDK3:
|
||||
return new DK3_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
case kADPCMXA:
|
||||
return new XA_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
|
||||
default:
|
||||
error("Unsupported ADPCM encoding");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class PacketizedADPCMStream : public StatelessPacketizedAudioStream {
|
||||
public:
|
||||
PacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) :
|
||||
StatelessPacketizedAudioStream(rate, channels), _type(type), _blockAlign(blockAlign) {}
|
||||
|
||||
protected:
|
||||
AudioStream *makeStream(Common::SeekableReadStream *data) override;
|
||||
|
||||
private:
|
||||
ADPCMType _type;
|
||||
uint32 _blockAlign;
|
||||
};
|
||||
|
||||
AudioStream *PacketizedADPCMStream::makeStream(Common::SeekableReadStream *data) {
|
||||
return makeADPCMStream(data, DisposeAfterUse::YES, data->size(), _type, getRate(), getChannels(), _blockAlign);
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makePacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) {
|
||||
// Filter out types we can't support (they're not fully stateless)
|
||||
switch (type) {
|
||||
case kADPCMOki:
|
||||
case kADPCMXA:
|
||||
case kADPCMDVI:
|
||||
return nullptr;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return new PacketizedADPCMStream(type, rate, channels, blockAlign);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
108
audio/decoders/adpcm.h
Normal file
108
audio/decoders/adpcm.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - lastexpress
|
||||
* - mohawk
|
||||
* - saga
|
||||
* - sci (DK3 ADPCM for Phantasmagoria 2)
|
||||
* - scumm
|
||||
* - tinsel
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_ADPCM_H
|
||||
#define AUDIO_ADPCM_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class PacketizedAudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
// There are several types of ADPCM encoding, only some are supported here
|
||||
// For all the different encodings, refer to:
|
||||
// https://wiki.multimedia.cx/index.php?title=Category:ADPCM_Audio_Codecs
|
||||
// Usually, if the audio stream we're trying to play has the FourCC header
|
||||
// string intact, it's easy to discern which encoding is used
|
||||
enum ADPCMType {
|
||||
kADPCMOki, // Dialogic/Oki ADPCM (aka VOX)
|
||||
kADPCMMSIma, // Microsoft IMA ADPCM
|
||||
kADPCMMS, // Microsoft ADPCM
|
||||
kADPCMDVI, // Intel DVI IMA ADPCM
|
||||
kADPCMApple, // Apple QuickTime IMA ADPCM
|
||||
kADPCMDK3, // Duck DK3 IMA ADPCM
|
||||
kADPCMXA // XA ADPCM
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes an input stream containing ADPCM compressed sound data and creates
|
||||
* a RewindableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the ADPCM data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @param size how many bytes to read from the stream (0 = all)
|
||||
* @param type the compression type used
|
||||
* @param rate the sampling rate
|
||||
* @param channels the number of channels
|
||||
* @param blockAlign block alignment ???
|
||||
* @return a new RewindableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeADPCMStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse,
|
||||
uint32 size, ADPCMType type,
|
||||
int rate,
|
||||
int channels,
|
||||
uint32 blockAlign = 0);
|
||||
|
||||
/**
|
||||
* Creates a PacketizedAudioStream that will automatically queue
|
||||
* packets as individual AudioStreams like returned by makeADPCMStream.
|
||||
*
|
||||
* Due to the ADPCM types not necessarily supporting stateless
|
||||
* streaming, OKI, XA and DVI are not supported by this function
|
||||
* and will return NULL.
|
||||
*
|
||||
* @param type the compression type used
|
||||
* @param rate the sampling rate
|
||||
* @param channels the number of channels
|
||||
* @param blockAlign block alignment ???
|
||||
* @return The new PacketizedAudioStream or NULL, if the type isn't supported.
|
||||
*/
|
||||
PacketizedAudioStream *makePacketizedADPCMStream(
|
||||
ADPCMType type,
|
||||
int rate,
|
||||
int channels,
|
||||
uint32 blockAlign = 0);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
267
audio/decoders/adpcm_intern.h
Normal file
267
audio/decoders/adpcm_intern.h
Normal file
@@ -0,0 +1,267 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal interfaces to the ADPCM decoders.
|
||||
*
|
||||
* These can be used to make custom ADPCM decoder subclasses,
|
||||
* or to at least share some common data tables between various
|
||||
* ADPCM decoder implementations.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_ADPCM_INTERN_H
|
||||
#define AUDIO_ADPCM_INTERN_H
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class ADPCMStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream;
|
||||
int32 _startpos;
|
||||
const int32 _endpos;
|
||||
const int _channels;
|
||||
const uint32 _blockAlign;
|
||||
uint32 _blockPos[2];
|
||||
const int _rate;
|
||||
|
||||
struct ADPCMStatus {
|
||||
// OKI/IMA
|
||||
struct {
|
||||
int32 last;
|
||||
int32 stepIndex;
|
||||
int16 sample[2];
|
||||
} ima_ch[2];
|
||||
} _status;
|
||||
|
||||
virtual void reset();
|
||||
|
||||
public:
|
||||
ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign);
|
||||
|
||||
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos); }
|
||||
virtual bool isStereo() const { return _channels == 2; }
|
||||
virtual int getRate() const { return _rate; }
|
||||
|
||||
virtual bool rewind();
|
||||
virtual bool seek(const Timestamp &where) { return false; }
|
||||
virtual Timestamp getLength() const { return Timestamp(); }
|
||||
|
||||
/**
|
||||
* This table is used by some ADPCM variants (IMA and OKI) to adjust the
|
||||
* step for use on the next sample.
|
||||
* The first 8 entries are identical to the second 8 entries. Hence, we
|
||||
* could half the table in size. But since the lookup index is always a
|
||||
* 4-bit nibble, it is more efficient to just keep it as it is.
|
||||
*/
|
||||
static const int16 _stepAdjustTable[16];
|
||||
};
|
||||
|
||||
class Oki_ADPCMStream : public ADPCMStream {
|
||||
public:
|
||||
Oki_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; }
|
||||
|
||||
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
protected:
|
||||
int16 decodeOKI(byte);
|
||||
|
||||
private:
|
||||
uint8 _decodedSampleCount;
|
||||
int16 _decodedSamples[2];
|
||||
};
|
||||
|
||||
class XA_ADPCMStream : public ADPCMStream {
|
||||
public:
|
||||
XA_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; }
|
||||
|
||||
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
protected:
|
||||
void decodeXA(const byte *src);
|
||||
|
||||
private:
|
||||
uint8 _decodedSampleCount;
|
||||
uint8 _decodedSampleIndex;
|
||||
int16 _decodedSamples[28 * 2 * 4];
|
||||
};
|
||||
|
||||
class Ima_ADPCMStream : public ADPCMStream {
|
||||
protected:
|
||||
int16 decodeIMA(byte code, int channel = 0); // Default to using the left channel/using one channel
|
||||
|
||||
public:
|
||||
Ima_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {}
|
||||
|
||||
/**
|
||||
* This table is used by decodeIMA.
|
||||
*/
|
||||
static const int16 _imaTable[89];
|
||||
};
|
||||
|
||||
class DVI_ADPCMStream : public Ima_ADPCMStream {
|
||||
public:
|
||||
DVI_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { _decodedSampleCount = 0; }
|
||||
|
||||
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
private:
|
||||
uint8 _decodedSampleCount;
|
||||
int16 _decodedSamples[2];
|
||||
};
|
||||
|
||||
class Apple_ADPCMStream : public Ima_ADPCMStream {
|
||||
protected:
|
||||
// Apple QuickTime IMA ADPCM
|
||||
int32 _streamPos[2];
|
||||
int16 _buffer[2][2];
|
||||
uint8 _chunkPos[2];
|
||||
|
||||
void reset() {
|
||||
Ima_ADPCMStream::reset();
|
||||
_chunkPos[0] = 0;
|
||||
_chunkPos[1] = 0;
|
||||
_streamPos[0] = 0;
|
||||
_streamPos[1] = _blockAlign;
|
||||
}
|
||||
|
||||
public:
|
||||
Apple_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
_chunkPos[0] = 0;
|
||||
_chunkPos[1] = 0;
|
||||
_streamPos[0] = 0;
|
||||
_streamPos[1] = _blockAlign;
|
||||
}
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
};
|
||||
|
||||
class MSIma_ADPCMStream : public Ima_ADPCMStream {
|
||||
public:
|
||||
MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
|
||||
if (blockAlign == 0)
|
||||
error("MSIma_ADPCMStream(): blockAlign isn't specified");
|
||||
|
||||
if (blockAlign % (_channels * 4))
|
||||
error("MSIma_ADPCMStream(): invalid blockAlign");
|
||||
|
||||
_samplesLeft[0] = 0;
|
||||
_samplesLeft[1] = 0;
|
||||
}
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
void reset() {
|
||||
Ima_ADPCMStream::reset();
|
||||
_samplesLeft[0] = 0;
|
||||
_samplesLeft[1] = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int16 _buffer[2][8];
|
||||
int _samplesLeft[2];
|
||||
};
|
||||
|
||||
class MS_ADPCMStream : public ADPCMStream {
|
||||
protected:
|
||||
struct ADPCMChannelStatus {
|
||||
byte predictor;
|
||||
int16 delta;
|
||||
int16 coeff1;
|
||||
int16 coeff2;
|
||||
int16 sample1;
|
||||
int16 sample2;
|
||||
};
|
||||
|
||||
struct {
|
||||
// MS ADPCM
|
||||
ADPCMChannelStatus ch[2];
|
||||
} _status;
|
||||
|
||||
void reset() {
|
||||
ADPCMStream::reset();
|
||||
memset(&_status, 0, sizeof(_status));
|
||||
}
|
||||
|
||||
public:
|
||||
MS_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
if (blockAlign == 0)
|
||||
error("MS_ADPCMStream(): blockAlign isn't specified for MS ADPCM");
|
||||
memset(&_status, 0, sizeof(_status));
|
||||
_decodedSampleCount = 0;
|
||||
_decodedSampleIndex = 0;
|
||||
}
|
||||
|
||||
virtual bool endOfData() const { return (_stream->eos() || _stream->pos() >= _endpos) && (_decodedSampleCount == 0); }
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
protected:
|
||||
int16 decodeMS(ADPCMChannelStatus *c, byte);
|
||||
|
||||
private:
|
||||
uint8 _decodedSampleCount;
|
||||
uint8 _decodedSampleIndex;
|
||||
int16 _decodedSamples[4];
|
||||
};
|
||||
|
||||
// Duck DK3 IMA ADPCM Decoder
|
||||
// Based on FFmpeg's decoder and https://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM
|
||||
|
||||
class DK3_ADPCMStream : public Ima_ADPCMStream {
|
||||
public:
|
||||
DK3_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
|
||||
: Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
|
||||
|
||||
// DK3 only works as a stereo stream
|
||||
assert(channels == 2);
|
||||
}
|
||||
|
||||
virtual int readBuffer(int16 *buffer, const int numSamples);
|
||||
|
||||
private:
|
||||
byte _nibble, _lastByte;
|
||||
bool _topNibble;
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
250
audio/decoders/aiff.cpp
Normal file
250
audio/decoders/aiff.cpp
Normal file
@@ -0,0 +1,250 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code in this file is based on information found at
|
||||
* https://web.archive.org/web/20070409184854/http://www.borg.com/~jglatt/tech/aiff.htm
|
||||
*
|
||||
* Also partially based on libav's aiffdec.c
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/aiff.h"
|
||||
#include "audio/decoders/adpcm.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/3do.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
uint32 readExtended(Common::SeekableReadStream &stream) {
|
||||
// The sample rate is stored as an "80 bit IEEE Standard 754 floating
|
||||
// point number (Standard Apple Numeric Environment [SANE] data type
|
||||
// Extended).
|
||||
|
||||
byte buf[10];
|
||||
uint32 mantissa;
|
||||
uint32 last = 0;
|
||||
byte exp;
|
||||
|
||||
stream.read(buf, 10);
|
||||
mantissa = READ_BE_UINT32(buf + 2);
|
||||
exp = 30 - buf[1];
|
||||
|
||||
while (exp--) {
|
||||
last = mantissa;
|
||||
mantissa >>= 1;
|
||||
}
|
||||
|
||||
if (last & 0x00000001)
|
||||
mantissa++;
|
||||
|
||||
return mantissa;
|
||||
}
|
||||
|
||||
// AIFF versions
|
||||
static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F');
|
||||
static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
|
||||
|
||||
// Codecs
|
||||
static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
|
||||
|
||||
AIFFHeader *AIFFHeader::readAIFFHeader(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) {
|
||||
warning("makeAIFFStream: No 'FORM' header");
|
||||
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->readUint32BE(); // file size
|
||||
|
||||
uint32 version = stream->readUint32BE();
|
||||
|
||||
if (version != kVersionAIFF && version != kVersionAIFC) {
|
||||
warning("makeAIFFStream: No 'AIFF' or 'AIFC' header");
|
||||
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// From here on, we only care about the COMM and SSND chunks, which are
|
||||
// the only required chunks.
|
||||
|
||||
bool foundCOMM = false;
|
||||
bool foundSSND = false;
|
||||
|
||||
AIFFHeader *aiffHeader = new AIFFHeader;
|
||||
aiffHeader->_codec = kCodecPCM; // AIFF Default;
|
||||
|
||||
while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) {
|
||||
uint32 tag = stream->readUint32BE();
|
||||
uint32 length = stream->readUint32BE();
|
||||
uint32 pos = stream->pos();
|
||||
|
||||
if (stream->eos() || stream->err())
|
||||
break;
|
||||
|
||||
switch (tag) {
|
||||
case MKTAG('C', 'O', 'M', 'M'):
|
||||
foundCOMM = true;
|
||||
aiffHeader->_channels = stream->readUint16BE();
|
||||
aiffHeader->_frameCount = stream->readUint32BE();
|
||||
aiffHeader->_bitsPerSample = stream->readUint16BE();
|
||||
aiffHeader->_rate = readExtended(*stream);
|
||||
|
||||
if (version == kVersionAIFC)
|
||||
aiffHeader->_codec = stream->readUint32BE();
|
||||
break;
|
||||
case MKTAG('S', 'S', 'N', 'D'):
|
||||
foundSSND = true;
|
||||
/* uint32 offset = */ stream->readUint32BE();
|
||||
/* uint32 blockAlign = */ stream->readUint32BE();
|
||||
delete aiffHeader->_dataStream;
|
||||
aiffHeader->_dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse);
|
||||
break;
|
||||
case MKTAG('F', 'V', 'E', 'R'):
|
||||
switch (stream->readUint32BE()) {
|
||||
case 0:
|
||||
version = kVersionAIFF;
|
||||
break;
|
||||
case 0xA2805140:
|
||||
version = kVersionAIFC;
|
||||
break;
|
||||
default:
|
||||
warning("Unknown AIFF version chunk version");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MKTAG('w', 'a', 'v', 'e'):
|
||||
warning("Found unhandled AIFF-C extra data chunk");
|
||||
|
||||
if (!aiffHeader->_dataStream && disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
delete aiffHeader->_dataStream;
|
||||
delete aiffHeader;
|
||||
return nullptr;
|
||||
default:
|
||||
debug(1, "Skipping AIFF '%s' chunk", tag2str(tag));
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 seekPos = pos + length;
|
||||
if (seekPos < (uint32)stream->size()) {
|
||||
seekPos += (length & 1); // ensure we're word-aligned
|
||||
}
|
||||
stream->seek(seekPos);
|
||||
}
|
||||
|
||||
if (!foundCOMM) {
|
||||
warning("makeAIFFStream: Could not find 'COMM' chunk");
|
||||
|
||||
if (!aiffHeader->_dataStream && disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
delete aiffHeader;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!foundSSND) {
|
||||
warning("makeAIFFStream: Could not find 'SSND' chunk");
|
||||
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
|
||||
delete aiffHeader;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return aiffHeader;
|
||||
}
|
||||
|
||||
RewindableAudioStream *AIFFHeader::makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
// We only implement a subset of the AIFF standard.
|
||||
|
||||
if (_channels < 1 || _channels > 2) {
|
||||
warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", _channels);
|
||||
delete _dataStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Seek to the start of _dataStream, required for at least FileStream
|
||||
_dataStream->seek(0);
|
||||
|
||||
switch (_codec) {
|
||||
case kCodecPCM:
|
||||
case MKTAG('t', 'w', 'o', 's'):
|
||||
case MKTAG('s', 'o', 'w', 't'): {
|
||||
// PCM samples are always signed.
|
||||
byte rawFlags = 0;
|
||||
if (_bitsPerSample == 16)
|
||||
rawFlags |= Audio::FLAG_16BITS;
|
||||
if (_channels == 2)
|
||||
rawFlags |= Audio::FLAG_STEREO;
|
||||
if (_codec == MKTAG('s', 'o', 'w', 't'))
|
||||
rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
|
||||
|
||||
return makeRawStream(_dataStream, _rate, rawFlags, disposeAfterUse);
|
||||
}
|
||||
case MKTAG('i', 'm', 'a', '4'):
|
||||
// Apple QuickTime IMA ADPCM
|
||||
return makeADPCMStream(_dataStream, disposeAfterUse, 0, kADPCMApple, _rate, _channels, 34);
|
||||
case MKTAG('Q', 'D', 'M', '2'):
|
||||
// TODO: Need to figure out how to integrate this
|
||||
// (But hopefully never needed)
|
||||
warning("Unhandled AIFF-C QDM2 compression");
|
||||
break;
|
||||
case MKTAG('A', 'D', 'P', '4'):
|
||||
// ADP4 on 3DO
|
||||
return make3DO_ADP4AudioStream(_dataStream, _rate, _channels == 2, NULL, disposeAfterUse);
|
||||
case MKTAG('S', 'D', 'X', '2'):
|
||||
// SDX2 on 3DO
|
||||
return make3DO_SDX2AudioStream(_dataStream, _rate, _channels == 2, NULL, disposeAfterUse);
|
||||
default:
|
||||
warning("Unhandled AIFF-C compression tag '%s'", tag2str(_codec));
|
||||
}
|
||||
|
||||
delete _dataStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
AIFFHeader *aiffHeader = AIFFHeader::readAIFFHeader(stream, disposeAfterUse);
|
||||
if (aiffHeader == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto res = aiffHeader->makeAIFFStream(stream, disposeAfterUse);
|
||||
delete aiffHeader;
|
||||
return res;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
77
audio/decoders/aiff.h
Normal file
77
audio/decoders/aiff.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - bbvs
|
||||
* - pegasus
|
||||
* - saga
|
||||
* - sci
|
||||
* - sword1
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_AIFF_H
|
||||
#define AUDIO_AIFF_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class RewindableAudioStream;
|
||||
|
||||
class AIFFHeader {
|
||||
public:
|
||||
static AIFFHeader *readAIFFHeader(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
uint32 getFrameCount() const { return _frameCount; }
|
||||
uint32 getFrameRate() const { return _rate; }
|
||||
|
||||
private:
|
||||
uint16 _channels = 0;
|
||||
uint32 _frameCount = 0;
|
||||
uint16 _bitsPerSample = 0;
|
||||
uint32 _rate = 0;
|
||||
uint32 _codec = 0;
|
||||
Common::SeekableReadStream *_dataStream = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* Try to load an AIFF from the given seekable stream and create an AudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the AIFF data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
RewindableAudioStream *makeAIFFStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
185
audio/decoders/apc.cpp
Normal file
185
audio/decoders/apc.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/decoders/adpcm_intern.h"
|
||||
#include "audio/decoders/apc.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class APCStreamImpl : public APCStream {
|
||||
public:
|
||||
// These parameters are completely optional and only used in HNM videos to make sure data is consistent
|
||||
APCStreamImpl(uint32 sampleRate = uint(-1), byte stereo = byte(-1));
|
||||
|
||||
bool init(Common::SeekableReadStream &header) override;
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override { return _audStream->readBuffer(buffer, numSamples); }
|
||||
bool isStereo() const override { return _audStream->isStereo(); }
|
||||
int getRate() const override { return _audStream->getRate(); }
|
||||
bool endOfData() const override { return _audStream->endOfData(); }
|
||||
bool endOfStream() const override { return _audStream->endOfStream(); }
|
||||
|
||||
// PacketizedAudioStream API
|
||||
void queuePacket(Common::SeekableReadStream *data) override;
|
||||
void finish() override { _audStream->finish(); }
|
||||
|
||||
private:
|
||||
struct Status {
|
||||
int32 last;
|
||||
uint32 stepIndex;
|
||||
int16 step;
|
||||
};
|
||||
|
||||
FORCEINLINE static int16 decodeIMA(byte code, Status *status);
|
||||
|
||||
Common::ScopedPtr<QueuingAudioStream> _audStream;
|
||||
byte _stereo;
|
||||
uint32 _sampleRate;
|
||||
|
||||
Status _status[2];
|
||||
};
|
||||
|
||||
APCStreamImpl::APCStreamImpl(uint32 sampleRate, byte stereo) :
|
||||
_sampleRate(sampleRate), _stereo(stereo) {
|
||||
memset(_status, 0, sizeof(_status));
|
||||
if (_sampleRate != uint32(-1) && _stereo != byte(-1)) {
|
||||
_audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo));
|
||||
}
|
||||
}
|
||||
|
||||
bool APCStreamImpl::init(Common::SeekableReadStream &header) {
|
||||
// Header size
|
||||
if (header.size() < 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read magic and version at once
|
||||
byte magicVersion[12];
|
||||
if (header.read(magicVersion, sizeof(magicVersion)) != sizeof(magicVersion)) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(magicVersion, "CRYO_APC1.20", sizeof(magicVersion))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//uint32 num_samples = header.readUint32LE();
|
||||
header.skip(4);
|
||||
|
||||
uint32 samplerate = header.readUint32LE();
|
||||
if (_sampleRate != uint32(-1) && _sampleRate != samplerate) {
|
||||
error("Samplerate mismatch");
|
||||
return false;
|
||||
}
|
||||
_sampleRate = samplerate;
|
||||
|
||||
uint32 leftSample = header.readSint32LE();
|
||||
uint32 rightSample = header.readSint32LE();
|
||||
|
||||
uint32 audioFlags = header.readUint32LE();
|
||||
byte stereo = (audioFlags & 1);
|
||||
if (_stereo != byte(-1) && _stereo != stereo) {
|
||||
error("Channels mismatch");
|
||||
return false;
|
||||
|
||||
}
|
||||
_stereo = stereo;
|
||||
|
||||
_status[0].last = leftSample;
|
||||
_status[1].last = rightSample;
|
||||
_status[0].stepIndex = _status[1].stepIndex = 0;
|
||||
_status[0].step = _status[1].step = Ima_ADPCMStream::_imaTable[0];
|
||||
|
||||
if (!_audStream) {
|
||||
_audStream.reset(makeQueuingAudioStream(_sampleRate, _stereo));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void APCStreamImpl::queuePacket(Common::SeekableReadStream *data) {
|
||||
Common::ScopedPtr<Common::SeekableReadStream> packet(data);
|
||||
|
||||
uint32 size = packet->size() - packet->pos();
|
||||
if (size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// From 4-bits samples to 16-bits
|
||||
int16 *outputBuffer = (int16 *)malloc(size * 4);
|
||||
int16 *outputPtr = outputBuffer;
|
||||
|
||||
int channelOffset = (_stereo ? 1 : 0);
|
||||
|
||||
for (uint32 counter = size; counter > 0; counter--) {
|
||||
byte word = packet->readByte();
|
||||
*(outputPtr++) = decodeIMA((word >> 4) & 0x0f, _status);
|
||||
*(outputPtr++) = decodeIMA((word >> 0) & 0x0f, _status + channelOffset);
|
||||
}
|
||||
|
||||
byte flags = FLAG_16BITS;
|
||||
if (_stereo) {
|
||||
flags |= FLAG_STEREO;
|
||||
}
|
||||
#ifdef SCUMM_LITTLE_ENDIAN
|
||||
flags |= Audio::FLAG_LITTLE_ENDIAN;
|
||||
#endif
|
||||
_audStream->queueBuffer((byte *)outputBuffer, size * 4, DisposeAfterUse::YES, flags);
|
||||
}
|
||||
|
||||
int16 APCStreamImpl::decodeIMA(byte code, Status *status) {
|
||||
int32 E = (2 * (code & 0x7) + 1) * status->step / 8;
|
||||
int32 diff = (code & 0x08) ? -E : E;
|
||||
// In Cryo APC data is only truncated and not CLIPed as expected
|
||||
int16 samp = (status->last + diff);
|
||||
|
||||
int32 index = status->stepIndex + Ima_ADPCMStream::_stepAdjustTable[code];
|
||||
index = CLIP<int32>(index, 0, ARRAYSIZE(Ima_ADPCMStream::_imaTable) - 1);
|
||||
|
||||
status->last = samp;
|
||||
status->stepIndex = index;
|
||||
status->step = Ima_ADPCMStream::_imaTable[index];
|
||||
|
||||
return samp;
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makeAPCStream(Common::SeekableReadStream &header) {
|
||||
Common::ScopedPtr<APCStream> stream(new APCStreamImpl());
|
||||
if (!stream->init(header)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return stream.release();
|
||||
}
|
||||
|
||||
APCStream *makeAPCStream(uint sampleRate, bool stereo) {
|
||||
assert((sampleRate % 11025) == 0);
|
||||
return new APCStreamImpl(sampleRate, stereo ? 1 : 0);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
61
audio/decoders/apc.h
Normal file
61
audio/decoders/apc.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_APC_H
|
||||
#define AUDIO_DECODERS_APC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
} // End of namespace Common
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class APCStream : public PacketizedAudioStream {
|
||||
public:
|
||||
virtual bool init(Common::SeekableReadStream &header) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a PacketizedAudioStream that decodes Cryo APC sound from stream
|
||||
*
|
||||
* @param header The stream containing the header
|
||||
* queuePacket must be called after
|
||||
* @return A new PacketizedAudioStream, or nullptr on error
|
||||
*/
|
||||
PacketizedAudioStream *makeAPCStream(Common::SeekableReadStream &header);
|
||||
|
||||
/**
|
||||
* Create a PacketizedAudioStream that decodes Cryo APC sound using predefined settings
|
||||
* This is used by HNM6 video decoder and shouldn't be called elsewhere.
|
||||
*
|
||||
* @param sampleRate The sample rate of the stream
|
||||
* @param stereo Whether the stream will be stereo
|
||||
* @return A new APCStream
|
||||
*/
|
||||
APCStream *makeAPCStream(uint sampleRate, bool stereo);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
|
||||
460
audio/decoders/asf.cpp
Normal file
460
audio/decoders/asf.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on xoreos' ASF code which is in turn
|
||||
// Largely based on the ASF implementation found in FFmpeg.
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/asf.h"
|
||||
#include "audio/decoders/wma.h"
|
||||
#include "audio/decoders/wave_types.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class ASFGUID {
|
||||
public:
|
||||
ASFGUID(Common::SeekableReadStream &stream) {
|
||||
stream.read(id, 16);
|
||||
}
|
||||
|
||||
bool operator==(const byte gid[16]) const {
|
||||
return !memcmp(gid, id, 16);
|
||||
}
|
||||
|
||||
bool operator!=(const byte gid[16]) const {
|
||||
return memcmp(gid, id, 16);
|
||||
}
|
||||
|
||||
Common::String toString() const {
|
||||
return Common::String::format("%02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x",
|
||||
id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7], id[8], id[9], id[10], id[11], id[12], id[13], id[14], id[15]);
|
||||
}
|
||||
|
||||
private:
|
||||
byte id[16];
|
||||
};
|
||||
|
||||
// GUID's that we need
|
||||
static const byte s_asfHeader[16] = { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C };
|
||||
static const byte s_asfFileHeader[16] = { 0xA1, 0xDC, 0xAB, 0x8C, 0x47, 0xA9, 0xCF, 0x11, 0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
|
||||
static const byte s_asfHead1[16] = { 0xb5, 0x03, 0xbf, 0x5f, 0x2E, 0xA9, 0xCF, 0x11, 0x8e, 0xe3, 0x00, 0xc0, 0x0c, 0x20, 0x53, 0x65 };
|
||||
static const byte s_asfComment[16] = { 0x33, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
|
||||
static const byte s_asfStreamHeader[16] = { 0x91, 0x07, 0xDC, 0xB7, 0xB7, 0xA9, 0xCF, 0x11, 0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65 };
|
||||
static const byte s_asfCodecComment[16] = { 0x40, 0x52, 0xD1, 0x86, 0x1D, 0x31, 0xD0, 0x11, 0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6 };
|
||||
static const byte s_asfDataHeader[16] = { 0x36, 0x26, 0xb2, 0x75, 0x8E, 0x66, 0xCF, 0x11, 0xa6, 0xd9, 0x00, 0xaa, 0x00, 0x62, 0xce, 0x6c };
|
||||
static const byte s_asfAudioStream[16] = { 0x40, 0x9E, 0x69, 0xF8, 0x4D, 0x5B, 0xCF, 0x11, 0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B };
|
||||
static const byte s_asfExtendedHeader[16] = { 0x40, 0xA4, 0xD0, 0xD2, 0x07, 0xE3, 0xD2, 0x11, 0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50 };
|
||||
static const byte s_asfStreamBitRate[16] = { 0xce, 0x75, 0xf8, 0x7b, 0x8d, 0x46, 0xd1, 0x11, 0x8d, 0x82, 0x00, 0x60, 0x97, 0xc9, 0xa2, 0xb2 };
|
||||
|
||||
class ASFStream : public SeekableAudioStream {
|
||||
public:
|
||||
ASFStream(Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
~ASFStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool endOfData() const override;
|
||||
bool isStereo() const override { return _channels == 2; }
|
||||
int getRate() const override { return _sampleRate; }
|
||||
Timestamp getLength() const override { return Audio::Timestamp(_duration / 10000, _sampleRate); }
|
||||
bool seek(const Timestamp &where) override;
|
||||
bool rewind() override;
|
||||
|
||||
private:
|
||||
// Packet data
|
||||
struct Packet {
|
||||
Packet();
|
||||
~Packet();
|
||||
|
||||
byte flags;
|
||||
byte segmentType;
|
||||
uint16 packetSize;
|
||||
uint32 sendTime;
|
||||
uint16 duration;
|
||||
|
||||
struct Segment {
|
||||
byte streamID;
|
||||
byte sequenceNumber;
|
||||
bool isKeyframe;
|
||||
Common::Array<Common::SeekableReadStream *> data;
|
||||
};
|
||||
|
||||
Common::Array<Segment> segments;
|
||||
};
|
||||
|
||||
Common::SeekableReadStream *_stream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
|
||||
void parseStreamHeader();
|
||||
void parseFileHeader();
|
||||
Packet *readPacket();
|
||||
Codec *createCodec();
|
||||
AudioStream *createAudioStream();
|
||||
|
||||
uint32 _rewindPos;
|
||||
uint64 _curPacket;
|
||||
Packet *_lastPacket;
|
||||
Codec *_codec;
|
||||
AudioStream *_curAudioStream;
|
||||
byte _curSequenceNumber;
|
||||
|
||||
// Header object variables
|
||||
uint64 _packetCount;
|
||||
uint64 _duration;
|
||||
uint32 _minPacketSize, _maxPacketSize;
|
||||
|
||||
// Stream object variables
|
||||
uint16 _streamID;
|
||||
uint16 _compression;
|
||||
uint16 _channels;
|
||||
int _sampleRate;
|
||||
uint32 _bitRate;
|
||||
uint16 _blockAlign;
|
||||
uint16 _bitsPerCodedSample;
|
||||
Common::SeekableReadStream *_extraData;
|
||||
};
|
||||
|
||||
ASFStream::Packet::Packet() {
|
||||
}
|
||||
|
||||
ASFStream::Packet::~Packet() {
|
||||
for (uint32 i = 0; i < segments.size(); i++)
|
||||
for (uint32 j = 0; j < segments[i].data.size(); j++)
|
||||
delete segments[i].data[j];
|
||||
}
|
||||
|
||||
ASFStream::ASFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) : _stream(stream), _disposeAfterUse(disposeAfterUse) {
|
||||
_extraData = nullptr;
|
||||
_lastPacket = nullptr;
|
||||
_curPacket = 0;
|
||||
_codec = nullptr;
|
||||
_curAudioStream = nullptr;
|
||||
_curSequenceNumber = 1; // They always start at one
|
||||
|
||||
ASFGUID guid = ASFGUID(*_stream);
|
||||
if (guid != s_asfHeader)
|
||||
error("ASFStream: Missing asf header");
|
||||
|
||||
_stream->readUint64LE();
|
||||
_stream->readUint32LE();
|
||||
_stream->readByte();
|
||||
_stream->readByte();
|
||||
|
||||
for (;;) {
|
||||
uint64 startPos = _stream->pos();
|
||||
guid = ASFGUID(*_stream);
|
||||
uint64 size = _stream->readUint64LE();
|
||||
|
||||
if (_stream->eos())
|
||||
error("ASFStream: Unexpected eos");
|
||||
|
||||
// TODO: Parse each chunk
|
||||
if (guid == s_asfFileHeader) {
|
||||
parseFileHeader();
|
||||
} else if (guid == s_asfHead1) {
|
||||
// Should be safe to ignore
|
||||
} else if (guid == s_asfComment) {
|
||||
// Ignored
|
||||
} else if (guid == s_asfStreamHeader) {
|
||||
parseStreamHeader();
|
||||
} else if (guid == s_asfCodecComment) {
|
||||
// TODO
|
||||
} else if (guid == s_asfDataHeader) {
|
||||
// Done parsing the header
|
||||
break;
|
||||
} else if (guid == s_asfExtendedHeader) {
|
||||
// TODO
|
||||
} else if (guid == s_asfStreamBitRate) {
|
||||
// Ignored
|
||||
} else
|
||||
warning("Found unknown ASF GUID: %s", guid.toString().c_str());
|
||||
|
||||
_stream->seek(startPos + size);
|
||||
}
|
||||
|
||||
// Skip to the beginning of the packets
|
||||
_stream->skip(26);
|
||||
_rewindPos = _stream->pos();
|
||||
}
|
||||
|
||||
ASFStream::~ASFStream() {
|
||||
if (_disposeAfterUse)
|
||||
delete _stream;
|
||||
|
||||
delete _lastPacket;
|
||||
delete _curAudioStream;
|
||||
delete _codec;
|
||||
}
|
||||
|
||||
void ASFStream::parseFileHeader() {
|
||||
_stream->skip(16); // skip client GUID
|
||||
/* uint64 fileSize = */ _stream->readUint64LE();
|
||||
/* uint64 creationTime = */ _stream->readUint64LE();
|
||||
_packetCount = _stream->readUint64LE();
|
||||
/* uint64 endTimestamp = */ _stream->readUint64LE();
|
||||
_duration = _stream->readUint64LE();
|
||||
/* uint32 startTimestamp = */ _stream->readUint32LE();
|
||||
/* uint32 unknown = */ _stream->readUint32LE();
|
||||
/* uint32 flags = */ _stream->readUint32LE();
|
||||
_minPacketSize = _stream->readUint32LE();
|
||||
_maxPacketSize = _stream->readUint32LE();
|
||||
/* uint32 uncFrameSize = */ _stream->readUint32LE();
|
||||
|
||||
// We only know how to support packets of one length
|
||||
if (_minPacketSize != _maxPacketSize)
|
||||
error("ASFStream::parseFileHeader(): Mismatched packet sizes: Min = %d, Max = %d", _minPacketSize, _maxPacketSize);
|
||||
}
|
||||
|
||||
void ASFStream::parseStreamHeader() {
|
||||
ASFGUID guid = ASFGUID(*_stream);
|
||||
|
||||
if (guid != s_asfAudioStream)
|
||||
error("ASFStream::parseStreamHeader(): Found non-audio stream");
|
||||
|
||||
_stream->skip(16); // skip a guid
|
||||
_stream->readUint64LE(); // total size
|
||||
uint32 typeSpecificSize = _stream->readUint32LE();
|
||||
_stream->readUint32LE();
|
||||
_streamID = _stream->readUint16LE();
|
||||
_stream->readUint32LE();
|
||||
|
||||
// Parse the wave header
|
||||
_compression = _stream->readUint16LE();
|
||||
_channels = _stream->readUint16LE();
|
||||
_sampleRate = _stream->readUint32LE();
|
||||
_bitRate = _stream->readUint32LE() * 8;
|
||||
_blockAlign = _stream->readUint16LE();
|
||||
_bitsPerCodedSample = (typeSpecificSize == 14) ? 8 : _stream->readUint16LE();
|
||||
|
||||
if (typeSpecificSize >= 18) {
|
||||
uint32 cbSize = _stream->readUint16LE();
|
||||
cbSize = MIN<int>(cbSize, typeSpecificSize - 18);
|
||||
_extraData = _stream->readStream(cbSize);
|
||||
}
|
||||
|
||||
_codec = createCodec();
|
||||
}
|
||||
|
||||
bool ASFStream::seek(const Timestamp &where) {
|
||||
if (where == 0) {
|
||||
return rewind();
|
||||
}
|
||||
|
||||
// Seeking is not supported
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ASFStream::rewind() {
|
||||
// Seek back to the start of the packets
|
||||
_stream->seek(_rewindPos);
|
||||
|
||||
// Reset our packet counter
|
||||
_curPacket = 0;
|
||||
delete _lastPacket; _lastPacket = nullptr;
|
||||
|
||||
// Delete a stream if we have one
|
||||
delete _curAudioStream; _curAudioStream = nullptr;
|
||||
|
||||
// Reset this too
|
||||
_curSequenceNumber = 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ASFStream::Packet *ASFStream::readPacket() {
|
||||
if (_curPacket == _packetCount)
|
||||
error("ASFStream::readPacket(): Reading too many packets");
|
||||
|
||||
uint32 packetStartPos = _stream->pos();
|
||||
|
||||
// Read a single ASF packet
|
||||
if (_stream->readByte() != 0x82)
|
||||
error("ASFStream::readPacket(): Missing packet header");
|
||||
|
||||
if (_stream->readUint16LE() != 0)
|
||||
error("ASFStream::readPacket(): Unknown is not zero");
|
||||
|
||||
Packet *packet = new Packet();
|
||||
packet->flags = _stream->readByte();
|
||||
packet->segmentType = _stream->readByte();
|
||||
packet->packetSize = (packet->flags & 0x40) ? _stream->readUint16LE() : 0;
|
||||
|
||||
uint16 paddingSize = 0;
|
||||
if (packet->flags & 0x10)
|
||||
paddingSize = _stream->readUint16LE();
|
||||
else if (packet->flags & 0x08)
|
||||
paddingSize = _stream->readByte();
|
||||
|
||||
packet->sendTime = _stream->readUint32LE();
|
||||
packet->duration = _stream->readUint16LE();
|
||||
|
||||
byte segmentCount = (packet->flags & 0x01) ? _stream->readByte() : 1;
|
||||
packet->segments.resize(segmentCount & 0x3F);
|
||||
|
||||
for (uint32 i = 0; i < packet->segments.size(); i++) {
|
||||
Packet::Segment &segment = packet->segments[i];
|
||||
|
||||
segment.streamID = _stream->readByte();
|
||||
segment.sequenceNumber = _stream->readByte();
|
||||
segment.isKeyframe = (segment.streamID & 0x80) != 0;
|
||||
segment.streamID &= 0x7F;
|
||||
|
||||
uint32 fragmentOffset = 0;
|
||||
if (packet->segmentType == 0x55)
|
||||
fragmentOffset = _stream->readByte();
|
||||
else if (packet->segmentType == 0x59)
|
||||
fragmentOffset = _stream->readUint16LE();
|
||||
else if (packet->segmentType == 0x5D)
|
||||
fragmentOffset = _stream->readUint32LE();
|
||||
else
|
||||
error("ASFStream::readPacket(): Unknown packet segment type 0x%02x", packet->segmentType);
|
||||
|
||||
byte flags = _stream->readByte();
|
||||
if (flags == 1) {
|
||||
//uint32 objectStartTime = fragmentOffset; // reused purpose
|
||||
_stream->readByte(); // unknown
|
||||
|
||||
uint32 dataLength = (packet->segments.size() == 1) ? (_maxPacketSize - (_stream->pos() - packetStartPos) - paddingSize) : _stream->readUint16LE();
|
||||
uint32 startObjectPos = _stream->pos();
|
||||
|
||||
while ((uint32)_stream->pos() < dataLength + startObjectPos)
|
||||
segment.data.push_back(_stream->readStream(_stream->readByte()));
|
||||
} else if (flags == 8) {
|
||||
/* uint32 objectLength = */ _stream->readUint32LE();
|
||||
/* uint32 objectStartTime = */ _stream->readUint32LE();
|
||||
|
||||
uint32 dataLength = 0;
|
||||
if (packet->segments.size() == 1)
|
||||
dataLength = _maxPacketSize - (_stream->pos() - packetStartPos) - fragmentOffset - paddingSize;
|
||||
else if (segmentCount & 0x40)
|
||||
dataLength = _stream->readByte();
|
||||
else
|
||||
dataLength = _stream->readUint16LE();
|
||||
|
||||
_stream->skip(fragmentOffset);
|
||||
segment.data.push_back(_stream->readStream(dataLength));
|
||||
} else
|
||||
error("ASFStream::readPacket(): Unknown packet flags 0x%02x", flags);
|
||||
}
|
||||
|
||||
// Skip any padding
|
||||
_stream->skip(paddingSize);
|
||||
|
||||
// We just read a packet
|
||||
_curPacket++;
|
||||
|
||||
if ((uint32)_stream->pos() != packetStartPos + _maxPacketSize)
|
||||
error("ASFStream::readPacket(): Mismatching packet pos: %d (should be %d)", (int)_stream->pos(), _maxPacketSize + packetStartPos);
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
Codec *ASFStream::createCodec() {
|
||||
switch (_compression) {
|
||||
case kWaveFormatWMAv2:
|
||||
return new WMACodec(2, _sampleRate, _channels, _bitRate, _blockAlign, _extraData);
|
||||
default:
|
||||
error("ASFStream::createAudioStream(): Unknown compression 0x%04x", _compression);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AudioStream *ASFStream::createAudioStream() {
|
||||
delete _lastPacket;
|
||||
_lastPacket = readPacket();
|
||||
|
||||
// TODO
|
||||
if (_lastPacket->segments.size() != 1)
|
||||
error("ASFStream::createAudioStream(): Only single segment packets supported");
|
||||
|
||||
Packet::Segment &segment = _lastPacket->segments[0];
|
||||
|
||||
// We should only have one stream in a ASF audio file
|
||||
if (segment.streamID != _streamID)
|
||||
error("ASFStream::createAudioStream(): Packet stream ID mismatch");
|
||||
|
||||
// TODO
|
||||
if (segment.sequenceNumber != _curSequenceNumber)
|
||||
error("ASFStream::createAudioStream(): Only one sequence number per packet supported");
|
||||
|
||||
// This can overflow and needs to overflow!
|
||||
_curSequenceNumber++;
|
||||
|
||||
// TODO
|
||||
if (segment.data.size() != 1)
|
||||
error("ASFStream::createAudioStream(): Packet grouping not supported");
|
||||
|
||||
Common::SeekableReadStream *stream = segment.data[0];
|
||||
if (_codec)
|
||||
return _codec->decodeFrame(*stream);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ASFStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesDecoded = 0;
|
||||
|
||||
for (;;) {
|
||||
if (_curAudioStream) {
|
||||
samplesDecoded += _curAudioStream->readBuffer(buffer + samplesDecoded, numSamples - samplesDecoded);
|
||||
|
||||
if (_curAudioStream->endOfData()) {
|
||||
delete _curAudioStream;
|
||||
_curAudioStream = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (samplesDecoded == numSamples || endOfData())
|
||||
break;
|
||||
|
||||
if (!_curAudioStream) {
|
||||
_curAudioStream = createAudioStream();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return samplesDecoded;
|
||||
}
|
||||
|
||||
bool ASFStream::endOfData() const {
|
||||
return _curPacket == _packetCount && !_curAudioStream;
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeASFStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
SeekableAudioStream *s = new ASFStream(stream, disposeAfterUse);
|
||||
|
||||
if (s && s->endOfData()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
45
audio/decoders/asf.h
Normal file
45
audio/decoders/asf.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on xoreos' ASF code
|
||||
|
||||
#ifndef AUDIO_DECODERS_ASF_H
|
||||
#define AUDIO_DECODERS_ASF_H
|
||||
|
||||
namespace Audio {
|
||||
|
||||
/**
|
||||
* Try to load a ASF from the given seekable stream and create a RewindableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream The SeekableReadStream from which to read the ASF data.
|
||||
* @param disposeAfterUse Whether to delete the stream after use.
|
||||
*
|
||||
* @return A new SeekableAudioStream, or 0, if an error occurred.
|
||||
*/
|
||||
|
||||
SeekableAudioStream *makeASFStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // AUDIO_DECODERS_ASF_H
|
||||
50
audio/decoders/codec.h
Normal file
50
audio/decoders/codec.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_CODEC_H
|
||||
#define AUDIO_DECODERS_CODEC_H
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
|
||||
/**
|
||||
* @deprecated The old method of handling audio codecs that rely
|
||||
* on the state remaining the same between calls. This should
|
||||
* only be used for old code.
|
||||
*
|
||||
* DEPRECATED; USE PacketizedAudioStream INSTEAD!
|
||||
*/
|
||||
class Codec {
|
||||
public:
|
||||
Codec() {}
|
||||
virtual ~Codec() {}
|
||||
|
||||
virtual AudioStream *decodeFrame(Common::SeekableReadStream &data) = 0;
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
643
audio/decoders/flac.cpp
Normal file
643
audio/decoders/flac.cpp
Normal file
@@ -0,0 +1,643 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Disable symbol overrides for FILE as that is used in FLAC headers
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
|
||||
|
||||
#include "audio/decoders/flac.h"
|
||||
|
||||
#ifdef USE_FLAC
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#define FLAC__NO_DLL // that MS-magic gave me headaches - just link the library you like
|
||||
#include <FLAC/export.h>
|
||||
#include <FLAC/stream_decoder.h>
|
||||
|
||||
namespace Audio {
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- FLAC stream ---
|
||||
#pragma mark -
|
||||
|
||||
static const uint MAX_OUTPUT_CHANNELS = 2;
|
||||
|
||||
|
||||
class FLACStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::SeekableReadStream *_inStream;
|
||||
bool _disposeAfterUse;
|
||||
|
||||
::FLAC__StreamDecoder *_decoder;
|
||||
|
||||
/** Header of the stream */
|
||||
FLAC__StreamMetadata_StreamInfo _streaminfo;
|
||||
|
||||
/** index + 1(!) of the last sample to be played */
|
||||
FLAC__uint64 _lastSample;
|
||||
|
||||
/** total play time */
|
||||
Timestamp _length;
|
||||
|
||||
/** true if the last sample was decoded from the FLAC-API - there might still be data in the buffer */
|
||||
bool _lastSampleWritten;
|
||||
|
||||
typedef int16 SampleType;
|
||||
enum { BUFTYPE_BITS = 16 };
|
||||
|
||||
enum {
|
||||
// Maximal buffer size. According to the FLAC format specification, the block size is
|
||||
// a 16 bit value (in fact it seems the maximal block size is 32768, but we play it safe).
|
||||
BUFFER_SIZE = 65536
|
||||
};
|
||||
|
||||
struct {
|
||||
SampleType bufData[BUFFER_SIZE];
|
||||
SampleType *bufReadPos;
|
||||
uint bufFill;
|
||||
} _sampleCache;
|
||||
|
||||
SampleType *_outBuffer;
|
||||
uint _requestedSamples;
|
||||
|
||||
typedef void (*PFCONVERTBUFFERS)(SampleType*, const FLAC__int32*[], uint, const uint, const uint8);
|
||||
PFCONVERTBUFFERS _methodConvertBuffers;
|
||||
|
||||
|
||||
public:
|
||||
FLACStream(Common::SeekableReadStream *inStream, bool dispose);
|
||||
virtual ~FLACStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool isStereo() const override { return _streaminfo.channels >= 2; }
|
||||
int getRate() const override { return _streaminfo.sample_rate; }
|
||||
bool endOfData() const override {
|
||||
// End of data is reached if there either is no valid stream data available,
|
||||
// or if we reached the last sample and completely emptied the sample cache.
|
||||
return _streaminfo.channels == 0 || (_lastSampleWritten && _sampleCache.bufFill == 0);
|
||||
}
|
||||
|
||||
bool seek(const Timestamp &where) override;
|
||||
Timestamp getLength() const override { return _length; }
|
||||
|
||||
bool isStreamDecoderReady() const { return getStreamDecoderState() == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC; }
|
||||
protected:
|
||||
uint getChannels() const { return MIN<uint>(_streaminfo.channels, MAX_OUTPUT_CHANNELS); }
|
||||
|
||||
bool allocateBuffer(uint minSamples);
|
||||
|
||||
inline FLAC__StreamDecoderState getStreamDecoderState() const;
|
||||
|
||||
inline bool processSingleBlock();
|
||||
inline bool processUntilEndOfMetadata();
|
||||
bool seekAbsolute(FLAC__uint64 sample);
|
||||
|
||||
inline ::FLAC__StreamDecoderReadStatus callbackRead(FLAC__byte buffer[], size_t *bytes);
|
||||
inline ::FLAC__StreamDecoderSeekStatus callbackSeek(FLAC__uint64 absoluteByteOffset);
|
||||
inline ::FLAC__StreamDecoderTellStatus callbackTell(FLAC__uint64 *absoluteByteOffset);
|
||||
inline ::FLAC__StreamDecoderLengthStatus callbackLength(FLAC__uint64 *streamLength);
|
||||
inline bool callbackEOF();
|
||||
inline ::FLAC__StreamDecoderWriteStatus callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
||||
inline void callbackMetadata(const ::FLAC__StreamMetadata *metadata);
|
||||
inline void callbackError(::FLAC__StreamDecoderErrorStatus status);
|
||||
|
||||
private:
|
||||
static ::FLAC__StreamDecoderReadStatus callWrapRead(const ::FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData);
|
||||
static ::FLAC__StreamDecoderSeekStatus callWrapSeek(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData);
|
||||
static ::FLAC__StreamDecoderTellStatus callWrapTell(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData);
|
||||
static ::FLAC__StreamDecoderLengthStatus callWrapLength(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData);
|
||||
static FLAC__bool callWrapEOF(const ::FLAC__StreamDecoder *decoder, void *clientData);
|
||||
static ::FLAC__StreamDecoderWriteStatus callWrapWrite(const ::FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData);
|
||||
static void callWrapMetadata(const ::FLAC__StreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData);
|
||||
static void callWrapError(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData);
|
||||
|
||||
void setBestConvertBufferMethod();
|
||||
static void convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
static void convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits);
|
||||
};
|
||||
|
||||
FLACStream::FLACStream(Common::SeekableReadStream *inStream, bool dispose)
|
||||
: _decoder(::FLAC__stream_decoder_new()),
|
||||
_inStream(inStream),
|
||||
_disposeAfterUse(dispose),
|
||||
_length(0, 1000), _lastSample(0),
|
||||
_outBuffer(nullptr), _requestedSamples(0), _lastSampleWritten(false),
|
||||
_methodConvertBuffers(&FLACStream::convertBuffersGeneric)
|
||||
{
|
||||
assert(_inStream);
|
||||
memset(&_streaminfo, 0, sizeof(_streaminfo));
|
||||
|
||||
_sampleCache.bufReadPos = nullptr;
|
||||
_sampleCache.bufFill = 0;
|
||||
|
||||
_methodConvertBuffers = &FLACStream::convertBuffersGeneric;
|
||||
|
||||
bool success;
|
||||
success = (::FLAC__stream_decoder_init_stream(
|
||||
_decoder,
|
||||
&FLACStream::callWrapRead,
|
||||
&FLACStream::callWrapSeek,
|
||||
&FLACStream::callWrapTell,
|
||||
&FLACStream::callWrapLength,
|
||||
&FLACStream::callWrapEOF,
|
||||
&FLACStream::callWrapWrite,
|
||||
&FLACStream::callWrapMetadata,
|
||||
&FLACStream::callWrapError,
|
||||
(void *)this
|
||||
) == FLAC__STREAM_DECODER_INIT_STATUS_OK);
|
||||
if (success) {
|
||||
if (processUntilEndOfMetadata() && _streaminfo.channels > 0) {
|
||||
_lastSample = _streaminfo.total_samples + 1;
|
||||
_length = Timestamp(0, _lastSample - 1, getRate());
|
||||
return; // no error occurred
|
||||
}
|
||||
}
|
||||
|
||||
warning("FLACStream: could not create audio stream");
|
||||
}
|
||||
|
||||
FLACStream::~FLACStream() {
|
||||
if (_decoder != nullptr) {
|
||||
(void) ::FLAC__stream_decoder_finish(_decoder);
|
||||
::FLAC__stream_decoder_delete(_decoder);
|
||||
}
|
||||
if (_disposeAfterUse)
|
||||
delete _inStream;
|
||||
}
|
||||
|
||||
inline FLAC__StreamDecoderState FLACStream::getStreamDecoderState() const {
|
||||
assert(_decoder != nullptr);
|
||||
return ::FLAC__stream_decoder_get_state(_decoder);
|
||||
}
|
||||
|
||||
inline bool FLACStream::processSingleBlock() {
|
||||
assert(_decoder != nullptr);
|
||||
return 0 != ::FLAC__stream_decoder_process_single(_decoder);
|
||||
}
|
||||
|
||||
inline bool FLACStream::processUntilEndOfMetadata() {
|
||||
assert(_decoder != nullptr);
|
||||
return 0 != ::FLAC__stream_decoder_process_until_end_of_metadata(_decoder);
|
||||
}
|
||||
|
||||
bool FLACStream::seekAbsolute(FLAC__uint64 sample) {
|
||||
assert(_decoder != nullptr);
|
||||
const bool result = (0 != ::FLAC__stream_decoder_seek_absolute(_decoder, sample));
|
||||
if (result) {
|
||||
_lastSampleWritten = (_lastSample != 0 && sample >= _lastSample); // only set if we are SURE
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool FLACStream::seek(const Timestamp &where) {
|
||||
_sampleCache.bufFill = 0;
|
||||
_sampleCache.bufReadPos = nullptr;
|
||||
// FLAC uses the sample pair number, thus we always use "false" for the isStereo parameter
|
||||
// of the convertTimeToStreamPos helper.
|
||||
return seekAbsolute((FLAC__uint64)convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
|
||||
}
|
||||
|
||||
int FLACStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
const uint numChannels = getChannels();
|
||||
|
||||
if (numChannels == 0) {
|
||||
warning("FLACStream: Stream not successfully initialized, can't playback");
|
||||
return -1; // streaminfo wasn't read!
|
||||
}
|
||||
|
||||
assert(numSamples % numChannels == 0); // must be multiple of channels!
|
||||
assert(buffer != nullptr);
|
||||
assert(_outBuffer == nullptr);
|
||||
assert(_requestedSamples == 0);
|
||||
|
||||
_outBuffer = buffer;
|
||||
_requestedSamples = numSamples;
|
||||
|
||||
// If there is still data in our buffer from the last time around,
|
||||
// copy that first.
|
||||
if (_sampleCache.bufFill > 0) {
|
||||
assert(_sampleCache.bufReadPos >= _sampleCache.bufData);
|
||||
assert(_sampleCache.bufFill % numChannels == 0);
|
||||
|
||||
const uint copySamples = MIN((uint)numSamples, _sampleCache.bufFill);
|
||||
memcpy(buffer, _sampleCache.bufReadPos, copySamples*sizeof(buffer[0]));
|
||||
|
||||
_outBuffer = buffer + copySamples;
|
||||
_requestedSamples = numSamples - copySamples;
|
||||
_sampleCache.bufReadPos += copySamples;
|
||||
_sampleCache.bufFill -= copySamples;
|
||||
}
|
||||
|
||||
bool decoderOk = true;
|
||||
|
||||
FLAC__StreamDecoderState state = getStreamDecoderState();
|
||||
|
||||
// Keep poking FLAC to process more samples until we completely satisfied the request
|
||||
// respectively until we run out of data.
|
||||
while (!_lastSampleWritten && _requestedSamples > 0 && state == FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC) {
|
||||
assert(_sampleCache.bufFill == 0);
|
||||
assert(_requestedSamples % numChannels == 0);
|
||||
processSingleBlock();
|
||||
state = getStreamDecoderState();
|
||||
|
||||
if (state == FLAC__STREAM_DECODER_END_OF_STREAM)
|
||||
_lastSampleWritten = true;
|
||||
}
|
||||
|
||||
// Error handling
|
||||
switch (state) {
|
||||
case FLAC__STREAM_DECODER_END_OF_STREAM:
|
||||
_lastSampleWritten = true;
|
||||
break;
|
||||
case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
|
||||
break;
|
||||
default:
|
||||
decoderOk = false;
|
||||
warning("FLACStream: An error occurred while decoding. DecoderState is: %d", getStreamDecoderState());
|
||||
}
|
||||
|
||||
// Compute how many samples we actually produced
|
||||
const int samples = (int)(_outBuffer - buffer);
|
||||
assert(samples % numChannels == 0);
|
||||
|
||||
_outBuffer = nullptr; // basically unnecessary, only for the purpose of the asserts
|
||||
_requestedSamples = 0; // basically unnecessary, only for the purpose of the asserts
|
||||
|
||||
return decoderOk ? samples : -1;
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderReadStatus FLACStream::callbackRead(FLAC__byte buffer[], size_t *bytes) {
|
||||
if (*bytes == 0) {
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_ABORT; /* abort to avoid a deadlock */
|
||||
}
|
||||
|
||||
const uint32 bytesRead = _inStream->read(buffer, *bytes);
|
||||
|
||||
if (bytesRead == 0) {
|
||||
return _inStream->eos() ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_ABORT;
|
||||
}
|
||||
|
||||
*bytes = static_cast<uint>(bytesRead);
|
||||
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
void FLACStream::setBestConvertBufferMethod() {
|
||||
PFCONVERTBUFFERS tempMethod = &FLACStream::convertBuffersGeneric;
|
||||
|
||||
const uint numChannels = getChannels();
|
||||
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||
|
||||
assert(numChannels >= 1);
|
||||
assert(numBits >= 4 && numBits <=32);
|
||||
|
||||
if (numChannels == 1) {
|
||||
if (numBits == 8)
|
||||
tempMethod = &FLACStream::convertBuffersMono8Bit;
|
||||
if (numBits == BUFTYPE_BITS)
|
||||
tempMethod = &FLACStream::convertBuffersMonoNS;
|
||||
} else if (numChannels == 2) {
|
||||
if (numBits == 8)
|
||||
tempMethod = &FLACStream::convertBuffersStereo8Bit;
|
||||
if (numBits == BUFTYPE_BITS)
|
||||
tempMethod = &FLACStream::convertBuffersStereoNS;
|
||||
} /* else ... */
|
||||
|
||||
_methodConvertBuffers = tempMethod;
|
||||
}
|
||||
|
||||
// 1 channel, no scaling
|
||||
void FLACStream::convertBuffersMonoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 1);
|
||||
assert(numBits == BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||
|
||||
while (numSamples >= 4) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel1[1]);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[2]);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel1[3]);
|
||||
bufDestination += 4;
|
||||
inChannel1 += 4;
|
||||
numSamples -= 4;
|
||||
}
|
||||
|
||||
for (; numSamples > 0; --numSamples) {
|
||||
*bufDestination++ = static_cast<SampleType>(*inChannel1++);
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 1 channel, scaling from 8Bit
|
||||
void FLACStream::convertBuffersMono8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 1);
|
||||
assert(numBits == 8);
|
||||
assert(8 < BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0];
|
||||
|
||||
while (numSamples >= 4) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[2]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel1[3]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 4;
|
||||
inChannel1 += 4;
|
||||
numSamples -= 4;
|
||||
}
|
||||
|
||||
for (; numSamples > 0; --numSamples) {
|
||||
*bufDestination++ = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 2 channels, no scaling
|
||||
void FLACStream::convertBuffersStereoNS(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 2);
|
||||
assert(numBits == BUFTYPE_BITS);
|
||||
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||
|
||||
while (numSamples >= 2*2) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel2[0]);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[1]);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel2[1]);
|
||||
bufDestination += 2 * 2;
|
||||
inChannel1 += 2;
|
||||
inChannel2 += 2;
|
||||
numSamples -= 2 * 2;
|
||||
}
|
||||
|
||||
while (numSamples > 0) {
|
||||
bufDestination[0] = static_cast<SampleType>(*inChannel1++);
|
||||
bufDestination[1] = static_cast<SampleType>(*inChannel2++);
|
||||
bufDestination += 2;
|
||||
numSamples -= 2;
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
inChannels[1] = inChannel2;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// 2 channels, scaling from 8Bit
|
||||
void FLACStream::convertBuffersStereo8Bit(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numChannels == 2);
|
||||
assert(numBits == 8);
|
||||
assert(numSamples % 2 == 0); // must be integral multiply of channels
|
||||
assert(8 < BUFTYPE_BITS);
|
||||
|
||||
FLAC__int32 const* inChannel1 = inChannels[0]; // Left Channel
|
||||
FLAC__int32 const* inChannel2 = inChannels[1]; // Right Channel
|
||||
|
||||
while (numSamples >= 2*2) {
|
||||
bufDestination[0] = static_cast<SampleType>(inChannel1[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(inChannel2[0]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[2] = static_cast<SampleType>(inChannel1[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[3] = static_cast<SampleType>(inChannel2[1]) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 2 * 2;
|
||||
inChannel1 += 2;
|
||||
inChannel2 += 2;
|
||||
numSamples -= 2 * 2;
|
||||
}
|
||||
|
||||
while (numSamples > 0) {
|
||||
bufDestination[0] = static_cast<SampleType>(*inChannel1++) << (BUFTYPE_BITS - 8);
|
||||
bufDestination[1] = static_cast<SampleType>(*inChannel2++) << (BUFTYPE_BITS - 8);
|
||||
bufDestination += 2;
|
||||
numSamples -= 2;
|
||||
}
|
||||
|
||||
inChannels[0] = inChannel1;
|
||||
inChannels[1] = inChannel2;
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
// all Purpose-conversion - slowest of em all
|
||||
void FLACStream::convertBuffersGeneric(SampleType* bufDestination, const FLAC__int32 *inChannels[], uint numSamples, const uint numChannels, const uint8 numBits) {
|
||||
assert(numSamples % numChannels == 0); // must be integral multiply of channels
|
||||
|
||||
if (numBits < BUFTYPE_BITS) {
|
||||
const uint8 kPower = (uint8)(BUFTYPE_BITS - numBits);
|
||||
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++)) << kPower;
|
||||
}
|
||||
} else if (numBits > BUFTYPE_BITS) {
|
||||
const uint8 kPower = (uint8)(numBits - BUFTYPE_BITS);
|
||||
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++) >> kPower);
|
||||
}
|
||||
} else {
|
||||
for (; numSamples > 0; numSamples -= numChannels) {
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
*bufDestination++ = static_cast<SampleType>(*(inChannels[i]++));
|
||||
}
|
||||
}
|
||||
|
||||
assert(numSamples == 0); // dint copy too many samples
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderWriteStatus FLACStream::callbackWrite(const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[]) {
|
||||
assert(frame->header.channels == _streaminfo.channels);
|
||||
assert(frame->header.sample_rate == _streaminfo.sample_rate);
|
||||
assert(frame->header.bits_per_sample == _streaminfo.bits_per_sample);
|
||||
assert(frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER || _streaminfo.min_blocksize == _streaminfo.max_blocksize);
|
||||
|
||||
// We require that either the sample cache is empty, or that no samples were requested
|
||||
assert(_sampleCache.bufFill == 0 || _requestedSamples == 0);
|
||||
|
||||
uint numSamples = frame->header.blocksize;
|
||||
const uint numChannels = getChannels();
|
||||
const uint8 numBits = (uint8)_streaminfo.bits_per_sample;
|
||||
|
||||
assert(_requestedSamples % numChannels == 0); // must be integral multiply of channels
|
||||
|
||||
const FLAC__uint64 firstSampleNumber = (frame->header.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER) ?
|
||||
frame->header.number.sample_number : (static_cast<FLAC__uint64>(frame->header.number.frame_number)) * _streaminfo.max_blocksize;
|
||||
|
||||
// Check whether we are about to reach beyond the last sample we are supposed to play.
|
||||
if (_lastSample != 0 && firstSampleNumber + numSamples >= _lastSample) {
|
||||
numSamples = (uint)(firstSampleNumber >= _lastSample ? 0 : _lastSample - firstSampleNumber);
|
||||
_lastSampleWritten = true;
|
||||
}
|
||||
|
||||
// The value in _requestedSamples counts raw samples, so if there are more than one
|
||||
// channel, we have to multiply the number of available sample "pairs" by numChannels
|
||||
numSamples *= numChannels;
|
||||
|
||||
const FLAC__int32 *inChannels[MAX_OUTPUT_CHANNELS];
|
||||
for (uint i = 0; i < numChannels; ++i)
|
||||
inChannels[i] = buffer[i];
|
||||
|
||||
// write the incoming samples directly into the buffer provided to us by the mixer
|
||||
if (_requestedSamples > 0) {
|
||||
assert(_requestedSamples % numChannels == 0);
|
||||
assert(_outBuffer != nullptr);
|
||||
|
||||
// Copy & convert the available samples (limited both by how many we have available, and
|
||||
// by how many are actually needed).
|
||||
const uint copySamples = MIN(_requestedSamples, numSamples);
|
||||
(*_methodConvertBuffers)(_outBuffer, inChannels, copySamples, numChannels, numBits);
|
||||
|
||||
_requestedSamples -= copySamples;
|
||||
numSamples -= copySamples;
|
||||
_outBuffer += copySamples;
|
||||
}
|
||||
|
||||
// Write all remaining samples (i.e. those which didn't fit into the mixer buffer)
|
||||
// into the sample cache.
|
||||
if (_sampleCache.bufFill == 0)
|
||||
_sampleCache.bufReadPos = _sampleCache.bufData;
|
||||
const uint cacheSpace = (_sampleCache.bufData + BUFFER_SIZE) - (_sampleCache.bufReadPos + _sampleCache.bufFill);
|
||||
assert(numSamples <= cacheSpace);
|
||||
(void)cacheSpace;
|
||||
(*_methodConvertBuffers)(_sampleCache.bufReadPos + _sampleCache.bufFill, inChannels, numSamples, numChannels, numBits);
|
||||
|
||||
_sampleCache.bufFill += numSamples;
|
||||
|
||||
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderSeekStatus FLACStream::callbackSeek(FLAC__uint64 absoluteByteOffset) {
|
||||
_inStream->seek(absoluteByteOffset, SEEK_SET);
|
||||
const bool result = (absoluteByteOffset == (FLAC__uint64)_inStream->pos());
|
||||
|
||||
return result ? FLAC__STREAM_DECODER_SEEK_STATUS_OK : FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderTellStatus FLACStream::callbackTell(FLAC__uint64 *absoluteByteOffset) {
|
||||
*absoluteByteOffset = static_cast<FLAC__uint64>(_inStream->pos());
|
||||
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
|
||||
}
|
||||
|
||||
inline ::FLAC__StreamDecoderLengthStatus FLACStream::callbackLength(FLAC__uint64 *streamLength) {
|
||||
*streamLength = static_cast<FLAC__uint64>(_inStream->size());
|
||||
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
|
||||
}
|
||||
|
||||
inline bool FLACStream::callbackEOF() {
|
||||
return _inStream->eos();
|
||||
}
|
||||
|
||||
|
||||
inline void FLACStream::callbackMetadata(const ::FLAC__StreamMetadata *metadata) {
|
||||
assert(_decoder != nullptr);
|
||||
assert(metadata->type == FLAC__METADATA_TYPE_STREAMINFO); // others arent really interesting
|
||||
|
||||
_streaminfo = metadata->data.stream_info;
|
||||
setBestConvertBufferMethod(); // should be set after getting stream-information. FLAC always parses the info first
|
||||
}
|
||||
inline void FLACStream::callbackError(::FLAC__StreamDecoderErrorStatus status) {
|
||||
// some of these are non-critical-Errors
|
||||
debug(1, "FLACStream: An error occurred while decoding. DecoderStateError is: %d", status);
|
||||
}
|
||||
|
||||
/* Static Callback Wrappers */
|
||||
::FLAC__StreamDecoderReadStatus FLACStream::callWrapRead(const ::FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackRead(buffer, bytes);
|
||||
}
|
||||
|
||||
::FLAC__StreamDecoderSeekStatus FLACStream::callWrapSeek(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 absoluteByteOffset, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackSeek(absoluteByteOffset);
|
||||
}
|
||||
|
||||
::FLAC__StreamDecoderTellStatus FLACStream::callWrapTell(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *absoluteByteOffset, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackTell(absoluteByteOffset);
|
||||
}
|
||||
|
||||
::FLAC__StreamDecoderLengthStatus FLACStream::callWrapLength(const ::FLAC__StreamDecoder *decoder, FLAC__uint64 *streamLength, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackLength(streamLength);
|
||||
}
|
||||
|
||||
FLAC__bool FLACStream::callWrapEOF(const ::FLAC__StreamDecoder *decoder, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackEOF();
|
||||
}
|
||||
|
||||
::FLAC__StreamDecoderWriteStatus FLACStream::callWrapWrite(const ::FLAC__StreamDecoder *decoder, const ::FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
return instance->callbackWrite(frame, buffer);
|
||||
}
|
||||
|
||||
void FLACStream::callWrapMetadata(const ::FLAC__StreamDecoder *decoder, const ::FLAC__StreamMetadata *metadata, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
instance->callbackMetadata(metadata);
|
||||
}
|
||||
|
||||
void FLACStream::callWrapError(const ::FLAC__StreamDecoder *decoder, ::FLAC__StreamDecoderErrorStatus status, void *clientData) {
|
||||
FLACStream *instance = (FLACStream *)clientData;
|
||||
assert(nullptr != instance);
|
||||
instance->callbackError(status);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- FLAC factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeFLACStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
SeekableAudioStream *s = new FLACStream(stream, disposeAfterUse);
|
||||
if (s && s->endOfData()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FLAC
|
||||
69
audio/decoders/flac.h
Normal file
69
audio/decoders/flac.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - draci
|
||||
* - kyra
|
||||
* - queen
|
||||
* - saga
|
||||
* - sci
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - touche
|
||||
* - tucker
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_FLAC_H
|
||||
#define AUDIO_FLAC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef USE_FLAC
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Create a new SeekableAudioStream from the FLAC data in the given stream.
|
||||
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the FLAC data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeFLACStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_FLAC
|
||||
#endif // #ifndef AUDIO_FLAC_H
|
||||
127
audio/decoders/g711.cpp
Normal file
127
audio/decoders/g711.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/g711.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/util.h"
|
||||
|
||||
/* from g711.c by SUN microsystems (unrestricted use) */
|
||||
|
||||
#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
|
||||
#define QUANT_MASK (0xf) /* Quantization field mask. */
|
||||
#define SEG_SHIFT (4) /* Left shift for segment number. */
|
||||
#define SEG_MASK (0x70) /* Segment field mask. */
|
||||
#define BIAS (0x84) /* Bias for linear code. */
|
||||
|
||||
namespace Audio {
|
||||
|
||||
/**
|
||||
* Logarithmic PCM (G.711)
|
||||
* https://en.wikipedia.org/wiki/G.711
|
||||
* https://wiki.multimedia.cx/index.php/PCM#Logarithmic_PCM
|
||||
*/
|
||||
class G711AudioStream : public SeekableAudioStream {
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream;
|
||||
const int _rate;
|
||||
const int _channels;
|
||||
|
||||
protected:
|
||||
virtual int16 decodeSample(uint8 val) = 0;
|
||||
|
||||
public:
|
||||
G711AudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
|
||||
_stream(stream, disposeAfterUse),
|
||||
_rate(rate),
|
||||
_channels(channels) {
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
int samples;
|
||||
|
||||
for (samples = 0; samples < numSamples; samples++) {
|
||||
uint8 val = _stream->readByte();
|
||||
if (endOfData())
|
||||
break;
|
||||
buffer[samples] = decodeSample(val);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool isStereo() const override { return (_channels == 2); }
|
||||
int getRate() const override { return _rate; }
|
||||
bool endOfData() const override { return _stream->eos(); }
|
||||
bool seek(const Timestamp &where) override {
|
||||
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
|
||||
return _stream->seek(seekSample, SEEK_SET);
|
||||
}
|
||||
Timestamp getLength() const override {
|
||||
return Timestamp(0, _stream->size() / _channels, _rate);
|
||||
}
|
||||
};
|
||||
|
||||
class G711ALawStream : public G711AudioStream {
|
||||
int16 decodeSample(uint8 val) override {
|
||||
val ^= 0x55;
|
||||
|
||||
int t = val & QUANT_MASK;
|
||||
int seg = ((unsigned)val & SEG_MASK) >> SEG_SHIFT;
|
||||
if (seg)
|
||||
t = (t + t + 1 + 32) << (seg + 2);
|
||||
else
|
||||
t = (t + t + 1) << 3;
|
||||
|
||||
return (val & SIGN_BIT) ? t : -t;
|
||||
}
|
||||
|
||||
public:
|
||||
G711ALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
|
||||
G711AudioStream(stream, disposeAfterUse, rate, channels) {
|
||||
}
|
||||
};
|
||||
|
||||
SeekableAudioStream *makeALawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) {
|
||||
return new G711ALawStream(stream, disposeAfterUse, rate, channels);
|
||||
}
|
||||
|
||||
class G711MuLawStream : public G711AudioStream {
|
||||
int16 decodeSample(uint8 val) override {
|
||||
val = ~val;
|
||||
|
||||
int t = ((val & QUANT_MASK) << 3) + BIAS;
|
||||
t <<= ((unsigned)val & SEG_MASK) >> SEG_SHIFT;
|
||||
|
||||
return (val & SIGN_BIT) ? (BIAS - t) : (t - BIAS);
|
||||
}
|
||||
|
||||
public:
|
||||
G711MuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) :
|
||||
G711AudioStream(stream, disposeAfterUse, rate, channels) {
|
||||
}
|
||||
};
|
||||
|
||||
SeekableAudioStream *makeMuLawStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, int rate, int channels) {
|
||||
return new G711MuLawStream(stream, disposeAfterUse, rate, channels);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
76
audio/decoders/g711.h
Normal file
76
audio/decoders/g711.h
Normal file
@@ -0,0 +1,76 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - trecision
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_G711_H
|
||||
#define AUDIO_G711_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Takes an input stream containing G711 A-law compressed sound data and creates
|
||||
* a SeekableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the PCM data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @param rate the sampling rate
|
||||
* @param channels the number of channels
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeALawStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse,
|
||||
int rate,
|
||||
int channels);
|
||||
|
||||
/**
|
||||
* Takes an input stream containing G711 μ-law compressed sound data and creates
|
||||
* a SeekableAudioStream from that.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the PCM data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @param rate the sampling rate
|
||||
* @param channels the number of channels
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeMuLawStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse,
|
||||
int rate,
|
||||
int channels);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
131
audio/decoders/iff_sound.cpp
Normal file
131
audio/decoders/iff_sound.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/iff_sound.h"
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "common/formats/iff_container.h"
|
||||
#include "common/func.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
struct Voice8Header {
|
||||
uint32 oneShotHiSamples;
|
||||
uint32 repeatHiSamples;
|
||||
uint32 samplesPerHiCycle;
|
||||
uint16 samplesPerSec;
|
||||
byte octaves;
|
||||
byte compression;
|
||||
uint32 volume;
|
||||
|
||||
Voice8Header() {
|
||||
memset(this, 0, sizeof(Voice8Header));
|
||||
}
|
||||
|
||||
void load(Common::ReadStream &stream);
|
||||
};
|
||||
|
||||
void Voice8Header::load(Common::ReadStream &stream) {
|
||||
oneShotHiSamples = stream.readUint32BE();
|
||||
repeatHiSamples = stream.readUint32BE();
|
||||
samplesPerHiCycle = stream.readUint32BE();
|
||||
samplesPerSec = stream.readUint16BE();
|
||||
octaves = stream.readByte();
|
||||
compression = stream.readByte();
|
||||
volume = stream.readUint32BE();
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct A8SVXLoader {
|
||||
Voice8Header _header;
|
||||
int8 *_data;
|
||||
uint32 _dataSize;
|
||||
|
||||
void load(Common::ReadStream &input) {
|
||||
Common::IFFParser parser(&input);
|
||||
Common::Functor1Mem< Common::IFFChunk&, bool, A8SVXLoader > c(this, &A8SVXLoader::callback);
|
||||
parser.parse(c);
|
||||
}
|
||||
|
||||
bool callback(Common::IFFChunk &chunk) {
|
||||
switch (chunk._type) {
|
||||
case ID_VHDR:
|
||||
_header.load(*chunk._stream);
|
||||
break;
|
||||
|
||||
case ID_BODY:
|
||||
_dataSize = chunk._size;
|
||||
_data = (int8 *)malloc(_dataSize);
|
||||
assert(_data);
|
||||
loadData(chunk._stream);
|
||||
return true;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void loadData(Common::ReadStream *stream) {
|
||||
switch (_header.compression) {
|
||||
case 0:
|
||||
stream->read(_data, _dataSize);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
// implement other formats here
|
||||
error("compressed IFF audio is not supported");
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
AudioStream *make8SVXStream(Common::ReadStream &input, bool loop) {
|
||||
A8SVXLoader loader;
|
||||
loader.load(input);
|
||||
|
||||
SeekableAudioStream *stream = Audio::makeRawStream((byte *)loader._data, loader._dataSize, loader._header.samplesPerSec, 0);
|
||||
|
||||
uint32 loopStart = 0, loopEnd = 0;
|
||||
if (loop) {
|
||||
// the standard way to loop 8SVX audio implies use of the oneShotHiSamples and
|
||||
// repeatHiSamples fields
|
||||
loopStart = 0;
|
||||
loopEnd = loader._header.oneShotHiSamples + loader._header.repeatHiSamples;
|
||||
|
||||
if (loopStart != loopEnd) {
|
||||
return new SubLoopingAudioStream(stream, 0,
|
||||
Timestamp(0, loopStart, loader._header.samplesPerSec),
|
||||
Timestamp(0, loopEnd, loader._header.samplesPerSec));
|
||||
}
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
}
|
||||
43
audio/decoders/iff_sound.h
Normal file
43
audio/decoders/iff_sound.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - parallaction
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_IFF_H
|
||||
#define AUDIO_IFF_H
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
|
||||
AudioStream *make8SVXStream(Common::ReadStream &stream, bool loop);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
107
audio/decoders/mac_snd.cpp
Normal file
107
audio/decoders/mac_snd.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* The code in this file is based on information found at
|
||||
* https://developer.apple.com/library/archive/documentation/mac/Sound/Sound-60.html#HEADING60-15
|
||||
*
|
||||
* We implement both type 1 and type 2 snd resources, but only those that are sampled
|
||||
*/
|
||||
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
#include "audio/decoders/mac_snd.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
SeekableAudioStream *makeMacSndStream(Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
|
||||
uint16 sndType = stream->readUint16BE();
|
||||
|
||||
if (sndType == 1) {
|
||||
// "normal" snd resources
|
||||
if (stream->readUint16BE() != 1) {
|
||||
warning("makeMacSndStream(): Unsupported data type count");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (stream->readUint16BE() != 5) {
|
||||
// 5 == sampled
|
||||
warning("makeMacSndStream(): Unsupported data type");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->readUint32BE(); // initialization option
|
||||
} else if (sndType == 2) {
|
||||
// old HyperCard snd resources
|
||||
stream->readUint16BE(); // reference count (unused)
|
||||
} else {
|
||||
warning("makeMacSndStream(): Unknown format type %d", sndType);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// We really should never get this as long as we have sampled data only
|
||||
if (stream->readUint16BE() != 1) {
|
||||
warning("makeMacSndStream(): Unsupported command count");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16 command = stream->readUint16BE();
|
||||
|
||||
// 0x8050 - soundCmd (with dataOffsetFlag set): install a sampled sound as a voice
|
||||
// 0x8051 - bufferCmd (with dataOffsetFlag set): play a sample sound
|
||||
if (command != 0x8050 && command != 0x8051) {
|
||||
warning("makeMacSndStream(): Unsupported command %04x", command);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->readUint16BE(); // 0
|
||||
uint32 soundHeaderOffset = stream->readUint32BE();
|
||||
|
||||
stream->seek(soundHeaderOffset);
|
||||
|
||||
uint32 soundDataOffset = stream->readUint32BE();
|
||||
uint32 size = stream->readUint32BE();
|
||||
uint16 rate = stream->readUint32BE() >> 16; // Really fixed point, but we only support integer rates
|
||||
stream->readUint32BE(); // loop start
|
||||
stream->readUint32BE(); // loop end
|
||||
byte encoding = stream->readByte();
|
||||
stream->readByte(); // base frequency
|
||||
|
||||
if (encoding != 0) {
|
||||
// 0 == PCM
|
||||
warning("makeMacSndStream(): Unsupported compression %d", encoding);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
stream->skip(soundDataOffset);
|
||||
|
||||
Common::SeekableReadStream *dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + size, disposeAfterUse);
|
||||
|
||||
// Since we allocated our own stream for the data, we must specify DisposeAfterUse::YES.
|
||||
return makeRawStream(dataStream, rate, Audio::FLAG_UNSIGNED);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
57
audio/decoders/mac_snd.h
Normal file
57
audio/decoders/mac_snd.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - saga
|
||||
* - sci
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_MAC_SND_H
|
||||
#define AUDIO_MAC_SND_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Try to load a Mac snd resource from the given seekable stream and create a SeekableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the snd data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeMacSndStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
563
audio/decoders/mp3.cpp
Normal file
563
audio/decoders/mp3.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/mp3.h"
|
||||
|
||||
#ifdef USE_MAD
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/mutex.h"
|
||||
#include "common/ptr.h"
|
||||
#include "common/queue.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/substream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include <mad.h>
|
||||
|
||||
#if defined(__PSP__)
|
||||
#include "backends/platform/psp/mp3.h"
|
||||
#endif
|
||||
namespace Audio {
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- MP3 (MAD) stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
class BaseMP3Stream : public virtual AudioStream {
|
||||
public:
|
||||
BaseMP3Stream();
|
||||
virtual ~BaseMP3Stream();
|
||||
|
||||
bool endOfData() const override { return _state == MP3_STATE_EOS; }
|
||||
bool isStereo() const override { return _channels == 2; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
protected:
|
||||
void decodeMP3Data(Common::ReadStream &stream);
|
||||
void readMP3Data(Common::ReadStream &stream);
|
||||
|
||||
void initStream(Common::ReadStream &stream);
|
||||
void readHeader(Common::ReadStream &stream);
|
||||
void deinitStream();
|
||||
|
||||
int fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples);
|
||||
|
||||
enum State {
|
||||
MP3_STATE_INIT, // Need to init the decoder
|
||||
MP3_STATE_READY, // ready for processing data
|
||||
MP3_STATE_EOS // end of data reached (may need to loop)
|
||||
};
|
||||
|
||||
uint _posInFrame;
|
||||
State _state;
|
||||
|
||||
mad_timer_t _curTime;
|
||||
|
||||
mad_stream _stream;
|
||||
mad_frame _frame;
|
||||
mad_synth _synth;
|
||||
|
||||
uint _channels;
|
||||
uint _rate;
|
||||
|
||||
enum {
|
||||
BUFFER_SIZE = 5 * 8192
|
||||
};
|
||||
|
||||
// This buffer contains a slab of input data
|
||||
byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD];
|
||||
};
|
||||
|
||||
class MP3Stream : private BaseMP3Stream, public SeekableAudioStream {
|
||||
public:
|
||||
MP3Stream(Common::SeekableReadStream *inStream,
|
||||
DisposeAfterUse::Flag dispose);
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
bool seek(const Timestamp &where) override;
|
||||
Timestamp getLength() const override { return _length; }
|
||||
|
||||
protected:
|
||||
Common::ScopedPtr<Common::SeekableReadStream> _inStream;
|
||||
|
||||
Timestamp _length;
|
||||
|
||||
private:
|
||||
static Common::SeekableReadStream *skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose);
|
||||
};
|
||||
|
||||
class PacketizedMP3Stream : private BaseMP3Stream, public PacketizedAudioStream {
|
||||
public:
|
||||
PacketizedMP3Stream(Common::SeekableReadStream &firstPacket);
|
||||
PacketizedMP3Stream(uint channels, uint rate);
|
||||
~PacketizedMP3Stream();
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
bool endOfData() const override;
|
||||
bool endOfStream() const override;
|
||||
|
||||
// PacketizedAudioStream API
|
||||
void queuePacket(Common::SeekableReadStream *packet) override;
|
||||
void finish() override;
|
||||
|
||||
private:
|
||||
Common::Mutex _mutex;
|
||||
Common::Queue<Common::SeekableReadStream *> _queue;
|
||||
bool _finished;
|
||||
};
|
||||
|
||||
|
||||
BaseMP3Stream::BaseMP3Stream() :
|
||||
_posInFrame(0),
|
||||
_state(MP3_STATE_INIT),
|
||||
_curTime(mad_timer_zero) {
|
||||
|
||||
// The MAD_BUFFER_GUARD must always contain zeros (the reason
|
||||
// for this is that the Layer III Huffman decoder of libMAD
|
||||
// may read a few bytes beyond the end of the input buffer).
|
||||
memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD);
|
||||
}
|
||||
|
||||
BaseMP3Stream::~BaseMP3Stream() {
|
||||
deinitStream();
|
||||
}
|
||||
|
||||
void BaseMP3Stream::decodeMP3Data(Common::ReadStream &stream) {
|
||||
do {
|
||||
if (_state == MP3_STATE_INIT)
|
||||
initStream(stream);
|
||||
|
||||
if (_state == MP3_STATE_EOS)
|
||||
return;
|
||||
|
||||
// If necessary, load more data into the stream decoder
|
||||
if (_stream.error == MAD_ERROR_BUFLEN)
|
||||
readMP3Data(stream);
|
||||
|
||||
while (_state == MP3_STATE_READY) {
|
||||
_stream.error = MAD_ERROR_NONE;
|
||||
|
||||
// Decode the next frame
|
||||
if (mad_frame_decode(&_frame, &_stream) == -1) {
|
||||
if (_stream.error == MAD_ERROR_BUFLEN) {
|
||||
break; // Read more data
|
||||
} else if (MAD_RECOVERABLE(_stream.error)) {
|
||||
// Note: we will occasionally see MAD_ERROR_BADDATAPTR errors here.
|
||||
// These are normal and expected (caused by our frame skipping (i.e. "seeking")
|
||||
// code above).
|
||||
debug(6, "MP3Stream: Recoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
continue;
|
||||
} else {
|
||||
warning("MP3Stream: Unrecoverable error in mad_frame_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up the total playback time so far
|
||||
mad_timer_add(&_curTime, _frame.header.duration);
|
||||
// Synthesize PCM data
|
||||
mad_synth_frame(&_synth, &_frame);
|
||||
_posInFrame = 0;
|
||||
break;
|
||||
}
|
||||
} while (_state != MP3_STATE_EOS && _stream.error == MAD_ERROR_BUFLEN);
|
||||
|
||||
if (_stream.error != MAD_ERROR_NONE)
|
||||
_state = MP3_STATE_EOS;
|
||||
}
|
||||
|
||||
void BaseMP3Stream::readMP3Data(Common::ReadStream &stream) {
|
||||
uint32 remaining = 0;
|
||||
|
||||
// Give up immediately if we already used up all data in the stream
|
||||
if (stream.eos()) {
|
||||
_state = MP3_STATE_EOS;
|
||||
return;
|
||||
}
|
||||
|
||||
if (_stream.next_frame) {
|
||||
// If there is still data in the MAD stream, we need to preserve it.
|
||||
// Note that we use memmove, as we are reusing the same buffer,
|
||||
// and hence the data regions we copy from and to may overlap.
|
||||
remaining = _stream.bufend - _stream.next_frame;
|
||||
assert(remaining < BUFFER_SIZE); // Paranoia check
|
||||
memmove(_buf, _stream.next_frame, remaining);
|
||||
}
|
||||
|
||||
// Try to read the next block
|
||||
uint32 size = stream.read(_buf + remaining, BUFFER_SIZE - remaining);
|
||||
if (size <= 0) {
|
||||
_state = MP3_STATE_EOS;
|
||||
return;
|
||||
}
|
||||
|
||||
// Feed the data we just read into the stream decoder
|
||||
_stream.error = MAD_ERROR_NONE;
|
||||
mad_stream_buffer(&_stream, _buf, size + remaining);
|
||||
}
|
||||
|
||||
void BaseMP3Stream::initStream(Common::ReadStream &stream) {
|
||||
if (_state != MP3_STATE_INIT)
|
||||
deinitStream();
|
||||
|
||||
// Init MAD
|
||||
mad_stream_init(&_stream);
|
||||
mad_frame_init(&_frame);
|
||||
mad_synth_init(&_synth);
|
||||
|
||||
// Reset the stream data
|
||||
_curTime = mad_timer_zero;
|
||||
_posInFrame = 0;
|
||||
|
||||
// Update state
|
||||
_state = MP3_STATE_READY;
|
||||
|
||||
// Read the first few sample bytes
|
||||
readMP3Data(stream);
|
||||
}
|
||||
|
||||
void BaseMP3Stream::readHeader(Common::ReadStream &stream) {
|
||||
if (_state != MP3_STATE_READY)
|
||||
return;
|
||||
|
||||
// If necessary, load more data into the stream decoder
|
||||
if (_stream.error == MAD_ERROR_BUFLEN)
|
||||
readMP3Data(stream);
|
||||
|
||||
while (_state != MP3_STATE_EOS) {
|
||||
_stream.error = MAD_ERROR_NONE;
|
||||
|
||||
// Decode the next header. Note: mad_frame_decode would do this for us, too.
|
||||
// However, for seeking we don't want to decode the full frame (else it would
|
||||
// be far too slow). Hence we perform this explicitly in a separate step.
|
||||
if (mad_header_decode(&_frame.header, &_stream) == -1) {
|
||||
if (_stream.error == MAD_ERROR_BUFLEN) {
|
||||
readMP3Data(stream); // Read more data
|
||||
continue;
|
||||
} else if (MAD_RECOVERABLE(_stream.error)) {
|
||||
debug(6, "MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
continue;
|
||||
} else {
|
||||
warning("MP3Stream: Unrecoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Sum up the total playback time so far
|
||||
mad_timer_add(&_curTime, _frame.header.duration);
|
||||
break;
|
||||
}
|
||||
|
||||
if (_stream.error != MAD_ERROR_NONE)
|
||||
_state = MP3_STATE_EOS;
|
||||
}
|
||||
|
||||
void BaseMP3Stream::deinitStream() {
|
||||
if (_state == MP3_STATE_INIT)
|
||||
return;
|
||||
|
||||
// Deinit MAD
|
||||
mad_synth_finish(&_synth);
|
||||
mad_frame_finish(&_frame);
|
||||
mad_stream_finish(&_stream);
|
||||
|
||||
_state = MP3_STATE_EOS;
|
||||
}
|
||||
|
||||
static inline int scaleSample(mad_fixed_t sample) {
|
||||
// round
|
||||
sample += (1L << (MAD_F_FRACBITS - 16));
|
||||
|
||||
// clip
|
||||
if (sample > MAD_F_ONE - 1)
|
||||
sample = MAD_F_ONE - 1;
|
||||
else if (sample < -MAD_F_ONE)
|
||||
sample = -MAD_F_ONE;
|
||||
|
||||
// quantize and scale to not saturate when mixing a lot of channels
|
||||
return sample >> (MAD_F_FRACBITS + 1 - 16);
|
||||
}
|
||||
|
||||
int BaseMP3Stream::fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
// Keep going as long as we have input available
|
||||
while (samples < numSamples && _state != MP3_STATE_EOS) {
|
||||
const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header));
|
||||
while (samples < len) {
|
||||
*buffer++ = (int16)scaleSample(_synth.pcm.samples[0][_posInFrame]);
|
||||
samples++;
|
||||
if (MAD_NCHANNELS(&_frame.header) == 2) {
|
||||
*buffer++ = (int16)scaleSample(_synth.pcm.samples[1][_posInFrame]);
|
||||
samples++;
|
||||
}
|
||||
_posInFrame++;
|
||||
}
|
||||
if (_posInFrame >= _synth.pcm.length) {
|
||||
// We used up all PCM data in the current frame -- read & decode more
|
||||
decodeMP3Data(stream);
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||
BaseMP3Stream(),
|
||||
_inStream(skipID3(inStream, dispose)),
|
||||
_length(0, 1000) {
|
||||
|
||||
// Initialize the stream with some data and set the channels and rate
|
||||
// variables
|
||||
decodeMP3Data(*_inStream);
|
||||
_channels = MAD_NCHANNELS(&_frame.header);
|
||||
_rate = _frame.header.samplerate;
|
||||
|
||||
// Calculate the length of the stream
|
||||
while (_state != MP3_STATE_EOS)
|
||||
readHeader(*_inStream);
|
||||
|
||||
// To rule out any invalid sample rate to be encountered here, say in case the
|
||||
// MP3 stream is invalid, we just check the MAD error code here.
|
||||
// We need to assure this, since else we might trigger an assertion in Timestamp
|
||||
// (When getRate() returns 0 or a negative number to be precise).
|
||||
// Note that we allow "MAD_ERROR_BUFLEN" as error code here, since according
|
||||
// to mad.h it is also set on EOF.
|
||||
if ((_stream.error == MAD_ERROR_NONE || _stream.error == MAD_ERROR_BUFLEN) && getRate() > 0)
|
||||
_length = Timestamp(mad_timer_count(_curTime, MAD_UNITS_MILLISECONDS), getRate());
|
||||
|
||||
deinitStream();
|
||||
|
||||
// Reinit stream
|
||||
_state = MP3_STATE_INIT;
|
||||
_inStream->seek(0);
|
||||
|
||||
// Decode the first chunk of data to set up the stream again.
|
||||
decodeMP3Data(*_inStream);
|
||||
}
|
||||
|
||||
int MP3Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
return fillBuffer(*_inStream, buffer, numSamples);
|
||||
}
|
||||
|
||||
bool MP3Stream::seek(const Timestamp &where) {
|
||||
if (where == _length) {
|
||||
_state = MP3_STATE_EOS;
|
||||
return true;
|
||||
} else if (where > _length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint32 time = where.msecs();
|
||||
|
||||
mad_timer_t destination;
|
||||
mad_timer_set(&destination, time / 1000, time % 1000, 1000);
|
||||
|
||||
if (_state != MP3_STATE_READY || mad_timer_compare(destination, _curTime) < 0) {
|
||||
_inStream->seek(0);
|
||||
initStream(*_inStream);
|
||||
}
|
||||
|
||||
while (mad_timer_compare(destination, _curTime) > 0 && _state != MP3_STATE_EOS)
|
||||
readHeader(*_inStream);
|
||||
|
||||
decodeMP3Data(*_inStream);
|
||||
|
||||
return (_state != MP3_STATE_EOS);
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *MP3Stream::skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose) {
|
||||
// Skip ID3 TAG if any
|
||||
// ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those.
|
||||
// ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'.
|
||||
// The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer.
|
||||
// See https://id3.org/id3v2.4.0-structure for details.
|
||||
char data[10];
|
||||
stream->read(data, sizeof(data));
|
||||
|
||||
uint32 offset = 0;
|
||||
if (!stream->eos() && data[0] == 'I' && data[1] == 'D' && data[2] == '3') {
|
||||
uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6]));
|
||||
// This size does not include an optional 10 bytes footer. Check if it is present.
|
||||
if (data[5] & 0x10)
|
||||
size += 10;
|
||||
|
||||
// Add in the 10 bytes we read in
|
||||
size += sizeof(data);
|
||||
debug(0, "Skipping ID3 TAG (%d bytes)", size);
|
||||
offset = size;
|
||||
}
|
||||
|
||||
return new Common::SeekableSubReadStream(stream, offset, stream->size(), dispose);
|
||||
}
|
||||
|
||||
PacketizedMP3Stream::PacketizedMP3Stream(Common::SeekableReadStream &firstPacket) :
|
||||
BaseMP3Stream(),
|
||||
_finished(false) {
|
||||
|
||||
// Load some data to get the channels/rate
|
||||
_queue.push(&firstPacket);
|
||||
decodeMP3Data(firstPacket);
|
||||
_channels = MAD_NCHANNELS(&_frame.header);
|
||||
_rate = _frame.header.samplerate;
|
||||
|
||||
// Clear everything
|
||||
deinitStream();
|
||||
_state = MP3_STATE_INIT;
|
||||
_queue.clear();
|
||||
}
|
||||
|
||||
PacketizedMP3Stream::PacketizedMP3Stream(uint channels, uint rate) :
|
||||
BaseMP3Stream(),
|
||||
_finished(false) {
|
||||
_channels = channels;
|
||||
_rate = rate;
|
||||
}
|
||||
|
||||
PacketizedMP3Stream::~PacketizedMP3Stream() {
|
||||
Common::StackLock lock(_mutex);
|
||||
while (!_queue.empty()) {
|
||||
delete _queue.front();
|
||||
_queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
int PacketizedMP3Stream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
|
||||
Common::StackLock lock(_mutex);
|
||||
while (samples < numSamples) {
|
||||
// Empty? Bail out for now, and mark the stream as ended
|
||||
if (_queue.empty()) {
|
||||
// EOS state is only valid once a packet has been received at least
|
||||
// once
|
||||
if (_state == MP3_STATE_READY)
|
||||
_state = MP3_STATE_EOS;
|
||||
return samples;
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *packet = _queue.front();
|
||||
|
||||
if (_state == MP3_STATE_INIT) {
|
||||
// Initialize everything
|
||||
decodeMP3Data(*packet);
|
||||
} else if (_state == MP3_STATE_EOS) {
|
||||
// Reset the end-of-stream setting
|
||||
_state = MP3_STATE_READY;
|
||||
}
|
||||
|
||||
samples += fillBuffer(*packet, buffer + samples, numSamples - samples);
|
||||
|
||||
// If the stream is done, kill it
|
||||
if (packet->pos() >= packet->size()) {
|
||||
_queue.pop();
|
||||
delete packet;
|
||||
}
|
||||
}
|
||||
|
||||
// This will happen if the audio runs out just as the last sample is
|
||||
// decoded. But there may still be more audio queued up.
|
||||
if (_state == MP3_STATE_EOS && !_queue.empty()) {
|
||||
_state = MP3_STATE_READY;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool PacketizedMP3Stream::endOfData() const {
|
||||
Common::StackLock lock(_mutex);
|
||||
return BaseMP3Stream::endOfData();
|
||||
}
|
||||
|
||||
bool PacketizedMP3Stream::endOfStream() const {
|
||||
Common::StackLock lock(_mutex);
|
||||
|
||||
if (!endOfData())
|
||||
return false;
|
||||
|
||||
if (!_queue.empty())
|
||||
return false;
|
||||
|
||||
return _finished;
|
||||
}
|
||||
|
||||
void PacketizedMP3Stream::queuePacket(Common::SeekableReadStream *packet) {
|
||||
Common::StackLock lock(_mutex);
|
||||
assert(!_finished);
|
||||
_queue.push(packet);
|
||||
|
||||
// If the audio had finished (buffer underrun?), there is more to
|
||||
// decode now.
|
||||
if (_state == MP3_STATE_EOS) {
|
||||
_state = MP3_STATE_READY;
|
||||
}
|
||||
}
|
||||
|
||||
void PacketizedMP3Stream::finish() {
|
||||
Common::StackLock lock(_mutex);
|
||||
_finished = true;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- MP3 factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeMP3Stream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
|
||||
#if defined(__PSP__)
|
||||
SeekableAudioStream *s = 0;
|
||||
|
||||
if (Mp3PspStream::isOkToCreateStream())
|
||||
s = new Mp3PspStream(stream, disposeAfterUse);
|
||||
|
||||
if (!s) // go to regular MAD mp3 stream if ME fails
|
||||
s = new MP3Stream(stream, disposeAfterUse);
|
||||
#else
|
||||
SeekableAudioStream *s = new MP3Stream(stream, disposeAfterUse);
|
||||
#endif
|
||||
if (s && s->endOfData()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makePacketizedMP3Stream(Common::SeekableReadStream &firstPacket) {
|
||||
return new PacketizedMP3Stream(firstPacket);
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makePacketizedMP3Stream(uint channels, uint rate) {
|
||||
return new PacketizedMP3Stream(channels, rate);
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MAD
|
||||
93
audio/decoders/mp3.h
Normal file
93
audio/decoders/mp3.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - draci
|
||||
* - glk
|
||||
* - kyra
|
||||
* - mohawk
|
||||
* - queen
|
||||
* - saga
|
||||
* - sci
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - titanic
|
||||
* - touche
|
||||
* - tucker
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_MP3_H
|
||||
#define AUDIO_MP3_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef USE_MAD
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class PacketizedAudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Create a new SeekableAudioStream from the MP3 data in the given stream.
|
||||
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the MP3 data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeMP3Stream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
/**
|
||||
* Create a new PacketizedAudioStream from the first packet in the given
|
||||
* stream. It does not own the packet and must be queued again later.
|
||||
*
|
||||
* @param firstPacket the SeekableReadStream from which to read the MP3 data
|
||||
* @return a new PacketizedAudioStream
|
||||
*/
|
||||
PacketizedAudioStream *makePacketizedMP3Stream(
|
||||
Common::SeekableReadStream &firstPacket);
|
||||
|
||||
/**
|
||||
* Create a new PacketizedAudioStream for a given number of channels
|
||||
* and sample rate.
|
||||
*
|
||||
* @param firstPacket the SeekableReadStream from which to read the MP3 data
|
||||
* @return a new PacketizedAudioStream
|
||||
*/
|
||||
PacketizedAudioStream *makePacketizedMP3Stream(
|
||||
uint channels, uint rate);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MAD
|
||||
#endif // #ifndef AUDIO_MP3_H
|
||||
338
audio/decoders/mpc.cpp
Normal file
338
audio/decoders/mpc.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
|
||||
#include "audio/decoders/mpc.h"
|
||||
|
||||
#ifdef USE_MPCDEC
|
||||
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
#include <mpcdec/mpcdec.h>
|
||||
#else
|
||||
#include <mpc/mpcdec.h>
|
||||
#endif
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// These are wrapper functions to allow using a SeekableReadStream object to
|
||||
// provide data to the mpc_reader object.
|
||||
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
static mpc_int32_t read_stream(void *data, void *ptr, mpc_int32_t size) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
|
||||
#else
|
||||
static mpc_int32_t read_stream(mpc_reader *p_reader, void *ptr, mpc_int32_t size) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
|
||||
#endif
|
||||
|
||||
return stream->read(ptr, size);
|
||||
}
|
||||
|
||||
/// Seeks to byte position offset.
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
static mpc_bool_t seek_stream(void *data, mpc_int32_t offset) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
|
||||
#else
|
||||
static mpc_bool_t seek_stream(mpc_reader *p_reader, mpc_int32_t offset) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
|
||||
#endif
|
||||
|
||||
return stream->seek(offset);
|
||||
}
|
||||
|
||||
/// Returns the current byte offset in the stream.
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
static mpc_int32_t tell_stream(void *data) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
|
||||
#else
|
||||
static mpc_int32_t tell_stream(mpc_reader *p_reader) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
|
||||
#endif
|
||||
|
||||
return stream->pos();
|
||||
}
|
||||
|
||||
/// Returns the total length of the source stream, in bytes.
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
static mpc_int32_t get_size_stream(void *data) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)data;
|
||||
#else
|
||||
static mpc_int32_t get_size_stream(mpc_reader *p_reader) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)p_reader->data;
|
||||
#endif
|
||||
|
||||
return stream->size();
|
||||
}
|
||||
|
||||
/// True if the stream is a seekable stream.
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
static mpc_bool_t canseek_stream(void *p_reader) {
|
||||
return TRUE;
|
||||
}
|
||||
#else
|
||||
static mpc_bool_t canseek_stream(mpc_reader *p_reader) {
|
||||
return MPC_TRUE;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Musepack stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
class MPCStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _inStream;
|
||||
|
||||
bool _isStereo;
|
||||
int _rate;
|
||||
|
||||
Timestamp _length;
|
||||
|
||||
mpc_reader _reader;
|
||||
mpc_streaminfo _si;
|
||||
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
mpc_decoder _decoder;
|
||||
#else
|
||||
mpc_demux *_demux;
|
||||
#endif
|
||||
|
||||
MPC_SAMPLE_FORMAT _bufferDec[MPC_DECODER_BUFFER_LENGTH];
|
||||
uint16 _buffer[MPC_DECODER_BUFFER_LENGTH];
|
||||
const uint16 *_bufferEnd;
|
||||
const uint16 *_pos;
|
||||
|
||||
public:
|
||||
// startTime / duration are in milliseconds
|
||||
MPCStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
|
||||
~MPCStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool endOfData() const override { return _pos >= _bufferEnd; }
|
||||
bool isStereo() const override { return _isStereo; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
bool seek(const Timestamp &where) override;
|
||||
Timestamp getLength() const override { return _length; }
|
||||
protected:
|
||||
bool refill();
|
||||
};
|
||||
|
||||
MPCStream::MPCStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||
_inStream(inStream, dispose),
|
||||
_length(0, 1000),
|
||||
_bufferEnd(ARRAYEND(_buffer)) {
|
||||
|
||||
_pos = _bufferEnd; // This will return endOfBuffer() if we're not properly inited
|
||||
|
||||
_reader.read = read_stream;
|
||||
_reader.seek = seek_stream;
|
||||
_reader.tell = tell_stream;
|
||||
_reader.get_size = get_size_stream;
|
||||
_reader.canseek = canseek_stream;
|
||||
_reader.data = (void *)inStream;
|
||||
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
mpc_streaminfo_init(&_si);
|
||||
if (mpc_streaminfo_read(&_si, &_reader) < 0) {
|
||||
warning("Cannot read musepack stream info");
|
||||
return;
|
||||
}
|
||||
mpc_decoder_setup(&_decoder, &_reader);
|
||||
mpc_decoder_scale_output (&_decoder, 1.0);
|
||||
if (!mpc_decoder_initialize(&_decoder, &_si)) {
|
||||
warning("Cannot initialize musepack decoder");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
_demux = mpc_demux_init(&_reader);
|
||||
|
||||
if (!_demux) {
|
||||
warning("Cannot init musepack demuxer");
|
||||
return;
|
||||
}
|
||||
|
||||
mpc_demux_get_info(_demux, &_si);
|
||||
#endif
|
||||
|
||||
_isStereo = _si.channels >= 2;
|
||||
_rate = _si.sample_freq;
|
||||
_length = Timestamp(uint32(mpc_streaminfo_get_length(&_si) * 1000.0), getRate());
|
||||
|
||||
int time = (int)mpc_streaminfo_get_length(&_si);
|
||||
int minutes = time / 60;
|
||||
int seconds = time % 60;
|
||||
|
||||
debug(9, "stream version %d", _si.stream_version);
|
||||
debug(9, "encoder: %s", _si.encoder);
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
debug(9, "profile: %s (q=%d)", _si.profile_name, _si.profile);
|
||||
#else
|
||||
debug(9, "profile: %s (q=%0.2f)", _si.profile_name, _si.profile - 5);
|
||||
debug(9, "PNS: %s", _si.pns == 0xFF ? "unknow" : _si.pns ? "on" : "off");
|
||||
#endif
|
||||
debug(9, "mid/side stereo: %s", _si.ms ? "on" : "off");
|
||||
debug(9, "gapless: %s", _si.is_true_gapless ? "on" : "off");
|
||||
debug(9, "average bitrate: %6.1f kbps", _si.average_bitrate * 1.e-3);
|
||||
debug(9, "samplerate: %d Hz", _si.sample_freq);
|
||||
debug(9, "channels: %d", _si.channels);
|
||||
debug(9, "length: %d:%.2d (%u samples)", minutes, seconds, (mpc_uint32_t)mpc_streaminfo_get_length_samples(&_si));
|
||||
debug(9, "file size: %d Bytes", _si.total_file_length);
|
||||
debug(9, "track peak: %2.2f dB", _si.peak_title / 256.f);
|
||||
debug(9, "track gain: %2.2f dB / %2.2f dB", _si.gain_title / 256.f, _si.gain_title == 0 ? 0 : 64.82f - _si.gain_title / 256.f);
|
||||
debug(9, "album peak: %2.2f dB", _si.peak_album / 256.f);
|
||||
debug(9, "album gain: %2.2f dB / %2.2f dB", _si.gain_album / 256.f, _si.gain_album == 0 ? 0 : 64.82f - _si.gain_album / 256.f);
|
||||
|
||||
if (!refill())
|
||||
return;
|
||||
}
|
||||
|
||||
MPCStream::~MPCStream() {
|
||||
#ifndef USE_MPCDEC_OLD_API
|
||||
mpc_demux_exit(_demux);
|
||||
#endif
|
||||
}
|
||||
|
||||
int MPCStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && _pos < _bufferEnd) {
|
||||
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
|
||||
memcpy(buffer, _pos, len * 2);
|
||||
buffer += len;
|
||||
_pos += len;
|
||||
samples += len;
|
||||
if (_pos >= _bufferEnd) {
|
||||
if (!refill())
|
||||
break;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool MPCStream::seek(const Timestamp &where) {
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
bool res = (mpc_decoder_seek_seconds(&_decoder, (double)where.msecs() / 1000.0) == TRUE);
|
||||
#else
|
||||
bool res = (mpc_demux_seek_second(_demux, (double)where.msecs() / 1000.0) == MPC_STATUS_OK);
|
||||
#endif
|
||||
if (!res) {
|
||||
warning("Error seeking in musepack stream");
|
||||
_pos = _bufferEnd;
|
||||
return false;
|
||||
}
|
||||
|
||||
return refill();
|
||||
}
|
||||
|
||||
bool MPCStream::refill() {
|
||||
bool result;
|
||||
uint32 samples;
|
||||
|
||||
#ifdef USE_MPCDEC_OLD_API
|
||||
uint32 vbr_update_acc, vbr_update_bits;
|
||||
samples = mpc_decoder_decode(&_decoder, _bufferDec, &vbr_update_acc, &vbr_update_bits);
|
||||
|
||||
if (samples == 0) { // End of stream
|
||||
_pos = _buffer;
|
||||
_bufferEnd = _buffer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (samples == -1u) { // Corruptd stream
|
||||
result = false;
|
||||
samples = 0;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
#else
|
||||
mpc_frame_info frame;
|
||||
frame.buffer = _bufferDec;
|
||||
|
||||
result = (mpc_demux_decode(_demux, &frame) == MPC_STATUS_OK);
|
||||
|
||||
if (frame.bits == -1) { // End of stream
|
||||
_pos = _buffer;
|
||||
_bufferEnd = _buffer;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
samples = frame.samples;
|
||||
#endif
|
||||
|
||||
if (!result) {
|
||||
// Possibly recoverable, just warn about it
|
||||
warning("Corrupted data in musepack file");
|
||||
}
|
||||
|
||||
#ifdef MPC_FIXED_POINT
|
||||
for(int i = 0; i < MPC_DECODER_BUFFER_LENGTH; i++) {
|
||||
int tmp = _bufferDec[i] >> MPC_FIXED_POINT_FRACTPART;
|
||||
if (tmp > ((1 << 15) - 1)) tmp = ((1 << 15) - 1);
|
||||
if (tmp < -(1 << 15)) tmp = -(1 << 15);
|
||||
_buffer[i] = tmp;
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < MPC_DECODER_BUFFER_LENGTH; i++) {
|
||||
int tmp = nearbyintf(_bufferDec[i] * (1 << 15));
|
||||
if (tmp > ((1 << 15) - 1))
|
||||
tmp = ((1 << 15) - 1);
|
||||
if (tmp < -(1 << 15))
|
||||
tmp = -(1 << 15);
|
||||
_buffer[i] = (uint16)tmp;
|
||||
}
|
||||
#endif
|
||||
|
||||
_pos = _buffer;
|
||||
_bufferEnd = &_buffer[samples * _si.channels];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Ogg Vorbis factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeMPCStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
SeekableAudioStream *s = new MPCStream(stream, disposeAfterUse);
|
||||
if (s && s->endOfData()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MPCDEC
|
||||
59
audio/decoders/mpc.h
Normal file
59
audio/decoders/mpc.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - qdengine
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_MPC_H
|
||||
#define AUDIO_MPC_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef USE_MPCDEC
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
|
||||
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeMPCStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_MPCDEC
|
||||
#endif // #ifndef AUDIO_MPC_H
|
||||
2611
audio/decoders/qdm2.cpp
Normal file
2611
audio/decoders/qdm2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
53
audio/decoders/qdm2.h
Normal file
53
audio/decoders/qdm2.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef AUDIO_QDM2_H
|
||||
#define AUDIO_QDM2_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
#ifdef USE_QDM2
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class Codec;
|
||||
|
||||
/**
|
||||
* Create a new Codec from the QDM2 data in the given stream.
|
||||
*
|
||||
* @param extraData the QuickTime extra data stream
|
||||
* @param disposeExtraData the QuickTime extra data stream
|
||||
* @return a new Codec, or NULL, if an error occurred
|
||||
*/
|
||||
Codec *makeQDM2Decoder(Common::SeekableReadStream *extraData,
|
||||
DisposeAfterUse::Flag disposeExtraData = DisposeAfterUse::NO);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // USE_QDM2
|
||||
#endif // AUDIO_QDM2_H
|
||||
527
audio/decoders/qdm2data.h
Normal file
527
audio/decoders/qdm2data.h
Normal file
@@ -0,0 +1,527 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_QDM2DATA_H
|
||||
#define AUDIO_QDM2DATA_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
/// VLC TABLES
|
||||
|
||||
// values in this table range from -1..23; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_level_huffcodes[24] = {
|
||||
0x037c, 0x0004, 0x003c, 0x004c, 0x003a, 0x002c, 0x001c, 0x001a,
|
||||
0x0024, 0x0014, 0x0001, 0x0002, 0x0000, 0x0003, 0x0007, 0x0005,
|
||||
0x0006, 0x0008, 0x0009, 0x000a, 0x000c, 0x00fc, 0x007c, 0x017c
|
||||
};
|
||||
|
||||
static const byte vlc_tab_level_huffbits[24] = {
|
||||
10, 6, 7, 7, 6, 6, 6, 6, 6, 5, 4, 4, 4, 3, 3, 3, 3, 4, 4, 5, 7, 8, 9, 10
|
||||
};
|
||||
|
||||
// values in this table range from -1..36; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_diff_huffcodes[37] = {
|
||||
0x1c57, 0x0004, 0x0000, 0x0001, 0x0003, 0x0002, 0x000f, 0x000e,
|
||||
0x0007, 0x0016, 0x0037, 0x0027, 0x0026, 0x0066, 0x0006, 0x0097,
|
||||
0x0046, 0x01c6, 0x0017, 0x0786, 0x0086, 0x0257, 0x00d7, 0x0357,
|
||||
0x00c6, 0x0386, 0x0186, 0x0000, 0x0157, 0x0c57, 0x0057, 0x0000,
|
||||
0x0b86, 0x0000, 0x1457, 0x0000, 0x0457
|
||||
};
|
||||
|
||||
static const byte vlc_tab_diff_huffbits[37] = {
|
||||
13, 3, 3, 2, 3, 3, 4, 4, 6, 5, 6, 6, 7, 7, 8, 8,
|
||||
8, 9, 8, 11, 9, 10, 8, 10, 9, 12, 10, 0, 10, 13, 11, 0,
|
||||
12, 0, 13, 0, 13
|
||||
};
|
||||
|
||||
// values in this table range from -1..5; adjust retrieved value by -1
|
||||
static const byte vlc_tab_run_huffcodes[6] = {
|
||||
0x1f, 0x00, 0x01, 0x03, 0x07, 0x0f
|
||||
};
|
||||
|
||||
static const byte vlc_tab_run_huffbits[6] = {
|
||||
5, 1, 2, 3, 4, 5
|
||||
};
|
||||
|
||||
// values in this table range from -1..19; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_tone_level_idx_hi1_huffcodes[20] = {
|
||||
0x5714, 0x000c, 0x0002, 0x0001, 0x0000, 0x0004, 0x0034, 0x0054,
|
||||
0x0094, 0x0014, 0x0114, 0x0214, 0x0314, 0x0614, 0x0e14, 0x0f14,
|
||||
0x2714, 0x0714, 0x1714, 0x3714
|
||||
};
|
||||
|
||||
static const byte vlc_tab_tone_level_idx_hi1_huffbits[20] = {
|
||||
15, 4, 2, 1, 3, 5, 6, 7, 8, 10, 10, 11, 11, 12, 12, 12, 14, 14, 15, 14
|
||||
};
|
||||
|
||||
// values in this table range from -1..23; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_tone_level_idx_mid_huffcodes[24] = {
|
||||
0x0fea, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
|
||||
0x0000, 0x0000, 0x0000, 0x0000, 0x03ea, 0x00ea, 0x002a, 0x001a,
|
||||
0x0006, 0x0001, 0x0000, 0x0002, 0x000a, 0x006a, 0x01ea, 0x07ea
|
||||
};
|
||||
|
||||
static const byte vlc_tab_tone_level_idx_mid_huffbits[24] = {
|
||||
12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 9, 7, 5, 3, 1, 2, 4, 6, 8, 10, 12
|
||||
};
|
||||
|
||||
// values in this table range from -1..23; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_tone_level_idx_hi2_huffcodes[24] = {
|
||||
0x0664, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0064, 0x00e4,
|
||||
0x00a4, 0x0068, 0x0004, 0x0008, 0x0014, 0x0018, 0x0000, 0x0001,
|
||||
0x0002, 0x0003, 0x000c, 0x0028, 0x0024, 0x0164, 0x0000, 0x0264
|
||||
};
|
||||
|
||||
static const byte vlc_tab_tone_level_idx_hi2_huffbits[24] = {
|
||||
11, 0, 0, 0, 0, 0, 10, 8, 8, 7, 6, 6, 5, 5, 4, 2, 2, 2, 4, 7, 8, 9, 0, 11
|
||||
};
|
||||
|
||||
// values in this table range from -1..8; adjust retrieved value by -1
|
||||
static const byte vlc_tab_type30_huffcodes[9] = {
|
||||
0x3c, 0x06, 0x00, 0x01, 0x03, 0x02, 0x04, 0x0c, 0x1c
|
||||
};
|
||||
|
||||
static const byte vlc_tab_type30_huffbits[9] = {
|
||||
6, 3, 3, 2, 2, 3, 4, 5, 6
|
||||
};
|
||||
|
||||
// values in this table range from -1..9; adjust retrieved value by -1
|
||||
static const byte vlc_tab_type34_huffcodes[10] = {
|
||||
0x18, 0x00, 0x01, 0x04, 0x05, 0x07, 0x03, 0x02, 0x06, 0x08
|
||||
};
|
||||
|
||||
static const byte vlc_tab_type34_huffbits[10] = {
|
||||
5, 4, 3, 3, 3, 3, 3, 3, 3, 5
|
||||
};
|
||||
|
||||
// values in this table range from -1..22; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_fft_tone_offset_0_huffcodes[23] = {
|
||||
0x038e, 0x0001, 0x0000, 0x0022, 0x000a, 0x0006, 0x0012, 0x0002,
|
||||
0x001e, 0x003e, 0x0056, 0x0016, 0x000e, 0x0032, 0x0072, 0x0042,
|
||||
0x008e, 0x004e, 0x00f2, 0x002e, 0x0036, 0x00c2, 0x018e
|
||||
};
|
||||
|
||||
static const byte vlc_tab_fft_tone_offset_0_huffbits[23] = {
|
||||
10, 1, 2, 6, 4, 5, 6, 7, 6, 6, 7, 7, 8, 7, 8, 8, 9, 7, 8, 6, 6, 8, 10
|
||||
};
|
||||
|
||||
// values in this table range from -1..27; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_fft_tone_offset_1_huffcodes[28] = {
|
||||
0x07a4, 0x0001, 0x0020, 0x0012, 0x001c, 0x0008, 0x0006, 0x0010,
|
||||
0x0000, 0x0014, 0x0004, 0x0032, 0x0070, 0x000c, 0x0002, 0x003a,
|
||||
0x001a, 0x002c, 0x002a, 0x0022, 0x0024, 0x000a, 0x0064, 0x0030,
|
||||
0x0062, 0x00a4, 0x01a4, 0x03a4
|
||||
};
|
||||
|
||||
static const byte vlc_tab_fft_tone_offset_1_huffbits[28] = {
|
||||
11, 1, 6, 6, 5, 4, 3, 6, 6, 5, 6, 6, 7, 6, 6, 6,
|
||||
6, 6, 6, 7, 8, 6, 7, 7, 7, 9, 10, 11
|
||||
};
|
||||
|
||||
// values in this table range from -1..31; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_fft_tone_offset_2_huffcodes[32] = {
|
||||
0x1760, 0x0001, 0x0000, 0x0082, 0x000c, 0x0006, 0x0003, 0x0007,
|
||||
0x0008, 0x0004, 0x0010, 0x0012, 0x0022, 0x001a, 0x0000, 0x0020,
|
||||
0x000a, 0x0040, 0x004a, 0x006a, 0x002a, 0x0042, 0x0002, 0x0060,
|
||||
0x00aa, 0x00e0, 0x00c2, 0x01c2, 0x0160, 0x0360, 0x0760, 0x0f60
|
||||
};
|
||||
|
||||
static const byte vlc_tab_fft_tone_offset_2_huffbits[32] = {
|
||||
13, 2, 0, 8, 4, 3, 3, 3, 4, 4, 5, 5, 6, 5, 7, 7,
|
||||
7, 7, 7, 7, 8, 8, 8, 9, 8, 8, 9, 9, 10, 11, 13, 12
|
||||
};
|
||||
|
||||
// values in this table range from -1..34; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_fft_tone_offset_3_huffcodes[35] = {
|
||||
0x33ea, 0x0005, 0x0000, 0x000c, 0x0000, 0x0006, 0x0003, 0x0008,
|
||||
0x0002, 0x0001, 0x0004, 0x0007, 0x001a, 0x000f, 0x001c, 0x002c,
|
||||
0x000a, 0x001d, 0x002d, 0x002a, 0x000d, 0x004c, 0x008c, 0x006a,
|
||||
0x00cd, 0x004d, 0x00ea, 0x020c, 0x030c, 0x010c, 0x01ea, 0x07ea,
|
||||
0x0bea, 0x03ea, 0x13ea
|
||||
};
|
||||
|
||||
static const byte vlc_tab_fft_tone_offset_3_huffbits[35] = {
|
||||
14, 4, 0, 10, 4, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6,
|
||||
6, 5, 6, 7, 7, 7, 8, 8, 8, 8, 9, 10, 10, 10, 10, 11,
|
||||
12, 13, 14
|
||||
};
|
||||
|
||||
// values in this table range from -1..37; adjust retrieved value by -1
|
||||
static const uint16 vlc_tab_fft_tone_offset_4_huffcodes[38] = {
|
||||
0x5282, 0x0016, 0x0000, 0x0136, 0x0004, 0x0000, 0x0007, 0x000a,
|
||||
0x000e, 0x0003, 0x0001, 0x000d, 0x0006, 0x0009, 0x0012, 0x0005,
|
||||
0x0025, 0x0022, 0x0015, 0x0002, 0x0076, 0x0035, 0x0042, 0x00c2,
|
||||
0x0182, 0x00b6, 0x0036, 0x03c2, 0x0482, 0x01c2, 0x0682, 0x0882,
|
||||
0x0a82, 0x0082, 0x0282, 0x1282, 0x3282, 0x2282
|
||||
};
|
||||
|
||||
static const byte vlc_tab_fft_tone_offset_4_huffbits[38] = {
|
||||
15, 6, 0, 9, 3, 3, 3, 4, 4, 3, 4, 4, 5, 4, 5, 6,
|
||||
6, 6, 6, 8, 7, 6, 8, 9, 9, 8, 9, 10, 11, 10, 11, 12,
|
||||
12, 12, 14, 15, 14, 14
|
||||
};
|
||||
|
||||
/// FFT TABLES
|
||||
|
||||
// values in this table range from -1..27; adjust retrieved value by -1
|
||||
static const uint16 fft_level_exp_alt_huffcodes[28] = {
|
||||
0x1ec6, 0x0006, 0x00c2, 0x0142, 0x0242, 0x0246, 0x00c6, 0x0046,
|
||||
0x0042, 0x0146, 0x00a2, 0x0062, 0x0026, 0x0016, 0x000e, 0x0005,
|
||||
0x0004, 0x0003, 0x0000, 0x0001, 0x000a, 0x0012, 0x0002, 0x0022,
|
||||
0x01c6, 0x02c6, 0x06c6, 0x0ec6
|
||||
};
|
||||
|
||||
static const byte fft_level_exp_alt_huffbits[28] = {
|
||||
13, 7, 8, 9, 10, 10, 10, 10, 10, 9, 8, 7, 6, 5, 4, 3,
|
||||
3, 2, 3, 3, 4, 5, 7, 8, 9, 11, 12, 13
|
||||
};
|
||||
|
||||
// values in this table range from -1..19; adjust retrieved value by -1
|
||||
static const uint16 fft_level_exp_huffcodes[20] = {
|
||||
0x0f24, 0x0001, 0x0002, 0x0000, 0x0006, 0x0005, 0x0007, 0x000c,
|
||||
0x000b, 0x0014, 0x0013, 0x0004, 0x0003, 0x0023, 0x0064, 0x00a4,
|
||||
0x0024, 0x0124, 0x0324, 0x0724
|
||||
};
|
||||
|
||||
static const byte fft_level_exp_huffbits[20] = {
|
||||
12, 3, 3, 3, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 8, 9, 10, 11, 12
|
||||
};
|
||||
|
||||
// values in this table range from -1..6; adjust retrieved value by -1
|
||||
static const byte fft_stereo_exp_huffcodes[7] = {
|
||||
0x3e, 0x01, 0x00, 0x02, 0x06, 0x0e, 0x1e
|
||||
};
|
||||
|
||||
static const byte fft_stereo_exp_huffbits[7] = {
|
||||
6, 1, 2, 3, 4, 5, 6
|
||||
};
|
||||
|
||||
// values in this table range from -1..8; adjust retrieved value by -1
|
||||
static const byte fft_stereo_phase_huffcodes[9] = {
|
||||
0x35, 0x02, 0x00, 0x01, 0x0d, 0x15, 0x05, 0x09, 0x03
|
||||
};
|
||||
|
||||
static const byte fft_stereo_phase_huffbits[9] = {
|
||||
6, 2, 2, 4, 4, 6, 5, 4, 2
|
||||
};
|
||||
|
||||
static const int fft_cutoff_index_table[4][2] = {
|
||||
{ 1, 2 }, {-1, 0 }, {-1,-2 }, { 0, 0 }
|
||||
};
|
||||
|
||||
static const int16 fft_level_index_table[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
|
||||
};
|
||||
|
||||
static const byte last_coeff[3] = {
|
||||
4, 7, 10
|
||||
};
|
||||
|
||||
static const byte coeff_per_sb_for_avg[3][30] = {
|
||||
{ 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
|
||||
{ 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 },
|
||||
{ 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9 }
|
||||
};
|
||||
|
||||
static const uint32 dequant_table[3][10][30] = {
|
||||
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 256, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 51, 102, 154, 205, 256, 238, 219, 201, 183, 165, 146, 128, 110, 91, 73, 55, 37, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 18, 37, 55, 73, 91, 110, 128, 146, 165, 183, 201, 219, 238, 256, 228, 199, 171, 142, 114, 85, 57, 28 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 85, 171, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 85, 171, 256, 219, 183, 146, 110, 73, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 73, 110, 146, 183, 219, 256, 228, 199, 171, 142, 114, 85, 57, 28, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 57, 85, 114, 142, 171, 199, 228, 256, 213, 171, 128, 85, 43 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } },
|
||||
{ { 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 256, 256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 256, 171, 85, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 85, 171, 256, 192, 128, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 128, 192, 256, 205, 154, 102, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 51, 102, 154, 205, 256, 213, 171, 128, 85, 43, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 43, 85, 128, 171, 213, 256, 213, 171, 128, 85, 43 } }
|
||||
};
|
||||
|
||||
static const byte coeff_per_sb_for_dequant[3][30] = {
|
||||
{ 0, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
|
||||
{ 0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6 },
|
||||
{ 0, 1, 2, 3, 4, 4, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9 }
|
||||
};
|
||||
|
||||
// first index is subband, 2nd index is 0, 1 or 3 (2 is unused)
|
||||
static const int8 tone_level_idx_offset_table[30][4] = {
|
||||
{ -50, -50, 0, -50 },
|
||||
{ -50, -50, 0, -50 },
|
||||
{ -50, -9, 0, -19 },
|
||||
{ -16, -6, 0, -12 },
|
||||
{ -11, -4, 0, -8 },
|
||||
{ -8, -3, 0, -6 },
|
||||
{ -7, -3, 0, -5 },
|
||||
{ -6, -2, 0, -4 },
|
||||
{ -5, -2, 0, -3 },
|
||||
{ -4, -1, 0, -3 },
|
||||
{ -4, -1, 0, -2 },
|
||||
{ -3, -1, 0, -2 },
|
||||
{ -3, -1, 0, -2 },
|
||||
{ -3, -1, 0, -2 },
|
||||
{ -2, -1, 0, -1 },
|
||||
{ -2, -1, 0, -1 },
|
||||
{ -2, -1, 0, -1 },
|
||||
{ -2, 0, 0, -1 },
|
||||
{ -2, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, -1 },
|
||||
{ -1, 0, 0, 0 },
|
||||
{ -1, 0, 0, 0 },
|
||||
{ -1, 0, 0, 0 },
|
||||
{ -1, 0, 0, 0 }
|
||||
};
|
||||
|
||||
/* all my samples have 1st index 0 or 1 */
|
||||
/* second index is subband, only indexes 0-29 seem to be used */
|
||||
static const int8 coding_method_table[5][30] = {
|
||||
{ 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
|
||||
},
|
||||
{ 34, 30, 24, 24, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
|
||||
},
|
||||
{ 34, 30, 30, 30, 24, 24, 16, 16, 16, 16, 16, 16, 10, 10, 10,
|
||||
10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10
|
||||
},
|
||||
{ 34, 34, 30, 30, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16,
|
||||
16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 10, 10, 10, 10
|
||||
},
|
||||
{ 34, 34, 30, 30, 30, 30, 30, 30, 24, 24, 24, 24, 24, 24, 24,
|
||||
24, 24, 24, 24, 24, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16
|
||||
},
|
||||
};
|
||||
|
||||
static const int vlc_stage3_values[60] = {
|
||||
0, 1, 2, 3, 4, 6, 8, 10, 12, 16, 20, 24,
|
||||
28, 36, 44, 52, 60, 76, 92, 108, 124, 156, 188, 220,
|
||||
252, 316, 380, 444, 508, 636, 764, 892, 1020, 1276, 1532, 1788,
|
||||
2044, 2556, 3068, 3580, 4092, 5116, 6140, 7164, 8188, 10236, 12284, 14332,
|
||||
16380, 20476, 24572, 28668, 32764, 40956, 49148, 57340, 65532, 81916, 98300,114684
|
||||
};
|
||||
|
||||
static const float fft_tone_sample_table[4][16][5] = {
|
||||
{ { .0100000000f,-.0037037037f,-.0020000000f,-.0069444444f,-.0018416207f },
|
||||
{ .0416666667f, .0000000000f, .0000000000f,-.0208333333f,-.0123456791f },
|
||||
{ .1250000000f, .0558035709f, .0330687836f,-.0164473690f,-.0097465888f },
|
||||
{ .1562500000f, .0625000000f, .0370370370f,-.0062500000f,-.0037037037f },
|
||||
{ .1996007860f, .0781250000f, .0462962948f, .0022727272f, .0013468013f },
|
||||
{ .2000000000f, .0625000000f, .0370370373f, .0208333333f, .0074074073f },
|
||||
{ .2127659619f, .0555555556f, .0329218097f, .0208333333f, .0123456791f },
|
||||
{ .2173913121f, .0473484844f, .0280583613f, .0347222239f, .0205761325f },
|
||||
{ .2173913121f, .0347222239f, .0205761325f, .0473484844f, .0280583613f },
|
||||
{ .2127659619f, .0208333333f, .0123456791f, .0555555556f, .0329218097f },
|
||||
{ .2000000000f, .0208333333f, .0074074073f, .0625000000f, .0370370370f },
|
||||
{ .1996007860f, .0022727272f, .0013468013f, .0781250000f, .0462962948f },
|
||||
{ .1562500000f,-.0062500000f,-.0037037037f, .0625000000f, .0370370370f },
|
||||
{ .1250000000f,-.0164473690f,-.0097465888f, .0558035709f, .0330687836f },
|
||||
{ .0416666667f,-.0208333333f,-.0123456791f, .0000000000f, .0000000000f },
|
||||
{ .0100000000f,-.0069444444f,-.0018416207f,-.0037037037f,-.0020000000f } },
|
||||
|
||||
{ { .0050000000f,-.0200000000f, .0125000000f,-.3030303030f, .0020000000f },
|
||||
{ .1041666642f, .0400000000f,-.0250000000f, .0333333333f,-.0200000000f },
|
||||
{ .1250000000f, .0100000000f, .0142857144f,-.0500000007f,-.0200000000f },
|
||||
{ .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f },
|
||||
{ .1562500000f,-.0006250000f,-.00049382716f,-.000625000f,-.00049382716f },
|
||||
{ .1250000000f,-.0500000000f,-.0200000000f, .0100000000f, .0142857144f },
|
||||
{ .1041666667f, .0333333333f,-.0200000000f, .0400000000f,-.0250000000f },
|
||||
{ .0050000000f,-.3030303030f, .0020000001f,-.0200000000f, .0125000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } },
|
||||
|
||||
{ { .1428571492f, .1250000000f,-.0285714287f,-.0357142873f, .0208333333f },
|
||||
{ .1818181818f, .0588235296f, .0333333333f, .0212765951f, .0100000000f },
|
||||
{ .1818181818f, .0212765951f, .0100000000f, .0588235296f, .0333333333f },
|
||||
{ .1428571492f,-.0357142873f, .0208333333f, .1250000000f,-.0285714287f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } },
|
||||
|
||||
{ { .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f },
|
||||
{ .0000000000f, .0000000000f, .0000000000f, .0000000000f, .0000000000f } }
|
||||
};
|
||||
|
||||
static const float fft_tone_level_table[2][64] = { {
|
||||
// pow ~ (i > 46) ? 0 : (((((i & 1) ? 431 : 304) << (i >> 1))) / 1024.0);
|
||||
0.17677669f, 0.42677650f, 0.60355347f, 0.85355347f,
|
||||
1.20710683f, 1.68359375f, 2.37500000f, 3.36718750f,
|
||||
4.75000000f, 6.73437500f, 9.50000000f, 13.4687500f,
|
||||
19.0000000f, 26.9375000f, 38.0000000f, 53.8750000f,
|
||||
76.0000000f, 107.750000f, 152.000000f, 215.500000f,
|
||||
304.000000f, 431.000000f, 608.000000f, 862.000000f,
|
||||
1216.00000f, 1724.00000f, 2432.00000f, 3448.00000f,
|
||||
4864.00000f, 6896.00000f, 9728.00000f, 13792.0000f,
|
||||
19456.0000f, 27584.0000f, 38912.0000f, 55168.0000f,
|
||||
77824.0000f, 110336.000f, 155648.000f, 220672.000f,
|
||||
311296.000f, 441344.000f, 622592.000f, 882688.000f,
|
||||
1245184.00f, 1765376.00f, 2490368.00f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
}, {
|
||||
// pow = (i > 45) ? 0 : ((((i & 1) ? 431 : 304) << (i >> 1)) / 512.0);
|
||||
0.59375000f, 0.84179688f, 1.18750000f, 1.68359375f,
|
||||
2.37500000f, 3.36718750f, 4.75000000f, 6.73437500f,
|
||||
9.50000000f, 13.4687500f, 19.0000000f, 26.9375000f,
|
||||
38.0000000f, 53.8750000f, 76.0000000f, 107.750000f,
|
||||
152.000000f, 215.500000f, 304.000000f, 431.000000f,
|
||||
608.000000f, 862.000000f, 1216.00000f, 1724.00000f,
|
||||
2432.00000f, 3448.00000f, 4864.00000f, 6896.00000f,
|
||||
9728.00000f, 13792.0000f, 19456.0000f, 27584.0000f,
|
||||
38912.0000f, 55168.0000f, 77824.0000f, 110336.000f,
|
||||
155648.000f, 220672.000f, 311296.000f, 441344.000f,
|
||||
622592.000f, 882688.000f, 1245184.00f, 1765376.00f,
|
||||
2490368.00f, 3530752.00f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f,
|
||||
0.00000000f, 0.00000000f, 0.00000000f, 0.00000000f
|
||||
} };
|
||||
|
||||
static const float fft_tone_envelope_table[4][31] = {
|
||||
{ .009607375f, .038060248f, .084265202f, .146446645f, .222214907f, .308658302f,
|
||||
.402454883f, .500000060f, .597545207f, .691341758f, .777785182f, .853553414f,
|
||||
.915734828f, .961939812f, .990392685f, 1.00000000f, .990392625f, .961939752f,
|
||||
.915734768f, .853553295f, .777785063f, .691341639f, .597545087f, .500000000f,
|
||||
.402454853f, .308658272f, .222214878f, .146446615f, .084265172f, .038060218f,
|
||||
.009607345f },
|
||||
{ .038060248f, .146446645f, .308658302f, .500000060f, .691341758f, .853553414f,
|
||||
.961939812f, 1.00000000f, .961939752f, .853553295f, .691341639f, .500000000f,
|
||||
.308658272f, .146446615f, .038060218f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f },
|
||||
{ .146446645f, .500000060f, .853553414f, 1.00000000f, .853553295f, .500000000f,
|
||||
.146446615f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f },
|
||||
{ .500000060f, 1.00000000f, .500000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f, .000000000f, .000000000f, .000000000f, .000000000f, .000000000f,
|
||||
.000000000f }
|
||||
};
|
||||
|
||||
static const float sb_noise_attenuation[32] = {
|
||||
0.0f, 0.0f, 0.3f, 0.4f, 0.5f, 0.7f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
|
||||
};
|
||||
|
||||
static const byte fft_subpackets[32] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0
|
||||
};
|
||||
|
||||
// first index is joined_stereo, second index is 0 or 2 (1 is unused)
|
||||
static const float dequant_1bit[2][3] = {
|
||||
{-0.920000f, 0.000000f, 0.920000f },
|
||||
{-0.890000f, 0.000000f, 0.890000f }
|
||||
};
|
||||
|
||||
static const float type30_dequant[8] = {
|
||||
-1.0f,-0.625f,-0.291666656732559f,0.0f,
|
||||
0.25f,0.5f,0.75f,1.0f,
|
||||
};
|
||||
|
||||
static const float type34_delta[10] = { // FIXME: covers 8 entries..
|
||||
-1.0f,-0.60947573184967f,-0.333333343267441f,-0.138071194291115f,0.0f,
|
||||
0.138071194291115f,0.333333343267441f,0.60947573184967f,1.0f,0.0f,
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
712
audio/decoders/quicktime.cpp
Normal file
712
audio/decoders/quicktime.cpp
Normal file
@@ -0,0 +1,712 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/util.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
#include "audio/decoders/codec.h"
|
||||
#include "audio/decoders/quicktime.h"
|
||||
#include "audio/decoders/quicktime_intern.h"
|
||||
|
||||
// Codecs
|
||||
#include "audio/decoders/aac.h"
|
||||
#include "audio/decoders/adpcm.h"
|
||||
#include "audio/decoders/qdm2.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/g711.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
/**
|
||||
* An AudioStream wrapper that forces audio to be played in mono.
|
||||
* It currently just ignores the right channel if stereo.
|
||||
*/
|
||||
class ForcedMonoAudioStream : public AudioStream {
|
||||
public:
|
||||
ForcedMonoAudioStream(AudioStream *parentStream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES) :
|
||||
_parentStream(parentStream), _disposeAfterUse(disposeAfterUse) {}
|
||||
|
||||
~ForcedMonoAudioStream() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _parentStream;
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
if (!_parentStream->isStereo())
|
||||
return _parentStream->readBuffer(buffer, numSamples);
|
||||
|
||||
int16 temp[2];
|
||||
int samples = 0;
|
||||
|
||||
while (samples < numSamples && !endOfData()) {
|
||||
_parentStream->readBuffer(temp, 2);
|
||||
*buffer++ = temp[0];
|
||||
samples++;
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool endOfData() const override { return _parentStream->endOfData(); }
|
||||
bool isStereo() const override { return false; }
|
||||
int getRate() const override { return _parentStream->getRate(); }
|
||||
|
||||
private:
|
||||
AudioStream *_parentStream;
|
||||
DisposeAfterUse::Flag _disposeAfterUse;
|
||||
};
|
||||
|
||||
QuickTimeAudioDecoder::QuickTimeAudioDecoder() : Common::QuickTimeParser() {
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::~QuickTimeAudioDecoder() {
|
||||
for (uint32 i = 0; i < _audioTracks.size(); i++)
|
||||
delete _audioTracks[i];
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::loadAudioFile(const Common::Path &filename) {
|
||||
if (!Common::QuickTimeParser::parseFile(filename))
|
||||
return false;
|
||||
|
||||
init();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::loadAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) {
|
||||
if (!Common::QuickTimeParser::parseStream(stream, disposeFileHandle))
|
||||
return false;
|
||||
|
||||
init();
|
||||
return true;
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::init() {
|
||||
Common::QuickTimeParser::init();
|
||||
|
||||
// Initialize all the audio streams
|
||||
// But ignore any streams we don't support
|
||||
for (uint32 i = 0; i < _tracks.size(); i++)
|
||||
if (_tracks[i]->codecType == CODEC_TYPE_AUDIO && ((AudioSampleDesc *)_tracks[i]->sampleDescs[0])->isAudioCodecSupported())
|
||||
_audioTracks.push_back(new QuickTimeAudioTrack(this, _tracks[i]));
|
||||
}
|
||||
|
||||
Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
|
||||
if (track->codecType == CODEC_TYPE_AUDIO) {
|
||||
debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format));
|
||||
|
||||
AudioSampleDesc *entry = new AudioSampleDesc(track, format);
|
||||
|
||||
uint16 stsdVersion = _fd->readUint16BE();
|
||||
_fd->readUint16BE(); // revision level
|
||||
_fd->readUint32BE(); // vendor
|
||||
|
||||
entry->_channels = _fd->readUint16BE(); // channel count
|
||||
entry->_bitsPerSample = _fd->readUint16BE(); // sample size
|
||||
|
||||
_fd->readUint16BE(); // compression id = 0
|
||||
_fd->readUint16BE(); // packet size = 0
|
||||
|
||||
entry->_sampleRate = (_fd->readUint32BE() >> 16);
|
||||
|
||||
debug(0, "stsd version =%d", stsdVersion);
|
||||
if (stsdVersion == 0) {
|
||||
// Not used, except in special cases. See below.
|
||||
entry->_samplesPerFrame = entry->_bytesPerFrame = 0;
|
||||
} else if (stsdVersion == 1) {
|
||||
// Read QT version 1 fields. In version 0 these dont exist.
|
||||
entry->_samplesPerFrame = _fd->readUint32BE();
|
||||
debug(0, "stsd samples_per_frame =%d",entry->_samplesPerFrame);
|
||||
_fd->readUint32BE(); // bytes per packet
|
||||
entry->_bytesPerFrame = _fd->readUint32BE();
|
||||
debug(0, "stsd bytes_per_frame =%d", entry->_bytesPerFrame);
|
||||
_fd->readUint32BE(); // bytes per sample
|
||||
} else {
|
||||
warning("Unsupported QuickTime STSD audio version %d", stsdVersion);
|
||||
delete entry;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Version 0 files don't have some variables set, so we'll do that here
|
||||
if (format == MKTAG('i', 'm', 'a', '4')) {
|
||||
entry->_samplesPerFrame = 64;
|
||||
entry->_bytesPerFrame = 34 * entry->_channels;
|
||||
}
|
||||
|
||||
if (entry->_sampleRate == 0 && track->timeScale > 1)
|
||||
entry->_sampleRate = track->timeScale;
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::QuickTimeAudioTrack::QuickTimeAudioTrack(QuickTimeAudioDecoder *decoder, Common::QuickTimeParser::Track *parentTrack) {
|
||||
_decoder = decoder;
|
||||
_parentTrack = parentTrack;
|
||||
_queue = createStream();
|
||||
_samplesQueued = 0;
|
||||
|
||||
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
|
||||
|
||||
if (entry->getCodecTag() == MKTAG('r', 'a', 'w', ' ') || entry->getCodecTag() == MKTAG('t', 'w', 'o', 's'))
|
||||
_parentTrack->sampleSize = (entry->_bitsPerSample / 8) * entry->_channels;
|
||||
|
||||
// Initialize our edit parser too
|
||||
_curEdit = 0;
|
||||
enterNewEdit(Timestamp());
|
||||
|
||||
// If the edit doesn't start on a nice boundary, set us up to skip some samples
|
||||
Timestamp editStartTime(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale);
|
||||
Timestamp trackPosition = getCurrentTrackTime();
|
||||
if (_parentTrack->editList[_curEdit].mediaTime != -1 && trackPosition != editStartTime)
|
||||
_skipSamples = editStartTime.convertToFramerate(getRate()) - trackPosition;
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::QuickTimeAudioTrack::~QuickTimeAudioTrack() {
|
||||
delete _queue;
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &length) {
|
||||
if (allDataRead() || (length.totalNumberOfFrames() != 0 && Timestamp(0, _samplesQueued, getRate()) >= length))
|
||||
return;
|
||||
|
||||
do {
|
||||
Timestamp nextEditTime(0, _parentTrack->editList[_curEdit].timeOffset + _parentTrack->editList[_curEdit].trackDuration, _decoder->_timeScale);
|
||||
|
||||
if (_parentTrack->editList[_curEdit].mediaTime == -1) {
|
||||
// We've got an empty edit, so fill it with silence
|
||||
Timestamp editLength(0, _parentTrack->editList[_curEdit].trackDuration, _decoder->_timeScale);
|
||||
|
||||
// If we seek into the middle of an empty edit, we need to adjust
|
||||
if (_skipSamples != Timestamp()) {
|
||||
editLength = editLength - _skipSamples;
|
||||
_skipSamples = Timestamp();
|
||||
}
|
||||
|
||||
queueStream(makeLimitingAudioStream(makeSilentAudioStream(getRate(), isStereo()), editLength), editLength);
|
||||
_curEdit++;
|
||||
enterNewEdit(nextEditTime);
|
||||
} else {
|
||||
// Normal audio
|
||||
AudioStream *stream = readAudioChunk(_curChunk);
|
||||
Timestamp chunkLength = getChunkLength(_curChunk, _skipAACPrimer);
|
||||
_skipAACPrimer = false;
|
||||
_curChunk++;
|
||||
|
||||
// If we have any samples that we need to skip (ie. we seek'ed into
|
||||
// the middle of a chunk), skip them here.
|
||||
if (_skipSamples != Timestamp()) {
|
||||
if (_skipSamples > chunkLength) {
|
||||
// If the amount we need to skip is greater than the size
|
||||
// of the chunk, just skip it altogether.
|
||||
_curMediaPos = _curMediaPos + chunkLength;
|
||||
_skipSamples = _skipSamples - chunkLength;
|
||||
delete stream;
|
||||
continue;
|
||||
}
|
||||
|
||||
skipSamples(_skipSamples, stream);
|
||||
_curMediaPos = _curMediaPos + _skipSamples;
|
||||
chunkLength = chunkLength - _skipSamples;
|
||||
_skipSamples = Timestamp();
|
||||
}
|
||||
|
||||
// Calculate our overall position within the media
|
||||
Timestamp trackPosition = getCurrentTrackTime() + chunkLength;
|
||||
|
||||
// If we have reached the end of this edit (or have no more media to read),
|
||||
// we move on to the next edit
|
||||
if (trackPosition >= nextEditTime || _curChunk >= _parentTrack->chunkCount) {
|
||||
chunkLength = nextEditTime.convertToFramerate(getRate()) - getCurrentTrackTime();
|
||||
stream = makeLimitingAudioStream(stream, chunkLength);
|
||||
_curEdit++;
|
||||
enterNewEdit(nextEditTime);
|
||||
|
||||
// Next time around, we'll know how much to skip
|
||||
trackPosition = getCurrentTrackTime();
|
||||
if (!allDataRead() && _parentTrack->editList[_curEdit].mediaTime != -1 && nextEditTime != trackPosition)
|
||||
_skipSamples = nextEditTime.convertToFramerate(getRate()) - trackPosition;
|
||||
} else {
|
||||
_curMediaPos = _curMediaPos + chunkLength.convertToFramerate(_curMediaPos.framerate());
|
||||
}
|
||||
|
||||
queueStream(stream, chunkLength);
|
||||
}
|
||||
} while (!allDataRead() && Timestamp(0, _samplesQueued, getRate()) < length);
|
||||
}
|
||||
|
||||
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getCurrentTrackTime() const {
|
||||
if (allDataRead())
|
||||
return getLength().convertToFramerate(getRate());
|
||||
|
||||
return Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale).convertToFramerate(getRate())
|
||||
+ _curMediaPos - Timestamp(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale).convertToFramerate(getRate());
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueRemainingAudio() {
|
||||
queueAudio(getLength());
|
||||
}
|
||||
|
||||
int QuickTimeAudioDecoder::QuickTimeAudioTrack::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesRead = _queue->readBuffer(buffer, numSamples);
|
||||
_samplesQueued -= samplesRead / (isStereo() ? 2 : 1);
|
||||
return samplesRead;
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::allDataRead() const {
|
||||
return _curEdit == _parentTrack->editList.size();
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::endOfData() const {
|
||||
return allDataRead() && _queue->endOfData();
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::seek(const Timestamp &where) {
|
||||
// Recreate the queue
|
||||
delete _queue;
|
||||
_queue = createStream();
|
||||
_samplesQueued = 0;
|
||||
|
||||
if (where >= getLength()) {
|
||||
// We're done
|
||||
_curEdit = _parentTrack->editList.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find where we are in the stream
|
||||
findEdit(where);
|
||||
|
||||
// Now queue up some audio and skip whatever we need to skip
|
||||
Timestamp samplesToSkip = where.convertToFramerate(getRate()) - getCurrentTrackTime();
|
||||
queueAudio();
|
||||
skipSamples(samplesToSkip, _queue);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getLength() const {
|
||||
return Timestamp(0, _parentTrack->duration, _decoder->_timeScale);
|
||||
}
|
||||
|
||||
QueuingAudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::createStream() const {
|
||||
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
|
||||
return makeQueuingAudioStream(entry->_sampleRate, entry->_channels == 2);
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::QuickTimeAudioTrack::isOldDemuxing() const {
|
||||
return _parentTrack->timeToSampleCount == 1 && _parentTrack->timeToSample[0].duration == 1;
|
||||
}
|
||||
|
||||
AudioStream *QuickTimeAudioDecoder::QuickTimeAudioTrack::readAudioChunk(uint chunk) {
|
||||
AudioSampleDesc *entry = (AudioSampleDesc *)_parentTrack->sampleDescs[0];
|
||||
Common::MemoryWriteStreamDynamic *wStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::NO);
|
||||
|
||||
_decoder->_fd->seek(_parentTrack->chunkOffsets[chunk]);
|
||||
|
||||
// First, we have to get the sample count
|
||||
uint32 sampleCount = getAudioChunkSampleCount(chunk);
|
||||
assert(sampleCount != 0);
|
||||
|
||||
if (isOldDemuxing()) {
|
||||
// Old-style audio demuxing
|
||||
|
||||
// Then calculate the right sizes
|
||||
while (sampleCount > 0) {
|
||||
uint32 samples = 0, size = 0;
|
||||
|
||||
if (entry->_samplesPerFrame >= 160) {
|
||||
samples = entry->_samplesPerFrame;
|
||||
size = entry->_bytesPerFrame;
|
||||
} else if (entry->_samplesPerFrame > 1) {
|
||||
samples = MIN<uint32>((1024 / entry->_samplesPerFrame) * entry->_samplesPerFrame, sampleCount);
|
||||
size = (samples / entry->_samplesPerFrame) * entry->_bytesPerFrame;
|
||||
} else {
|
||||
samples = MIN<uint32>(1024, sampleCount);
|
||||
size = samples * _parentTrack->sampleSize;
|
||||
}
|
||||
|
||||
// Now, we read in the data for this data and output it
|
||||
byte *data = (byte *)malloc(size);
|
||||
_decoder->_fd->read(data, size);
|
||||
wStream->write(data, size);
|
||||
free(data);
|
||||
sampleCount -= samples;
|
||||
}
|
||||
} else {
|
||||
// New-style audio demuxing
|
||||
|
||||
// Find our starting sample
|
||||
uint32 startSample = 0;
|
||||
for (uint32 i = 0; i < chunk; i++)
|
||||
startSample += getAudioChunkSampleCount(i);
|
||||
|
||||
for (uint32 i = 0; i < sampleCount; i++) {
|
||||
uint32 size = (_parentTrack->sampleSize != 0) ? _parentTrack->sampleSize : _parentTrack->sampleSizes[i + startSample];
|
||||
|
||||
// Now, we read in the data for this data and output it
|
||||
byte *data = (byte *)malloc(size);
|
||||
_decoder->_fd->read(data, size);
|
||||
wStream->write(data, size);
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
AudioStream *audioStream = entry->createAudioStream(new Common::MemoryReadStream(wStream->getData(), wStream->size(), DisposeAfterUse::YES));
|
||||
delete wStream;
|
||||
|
||||
return audioStream;
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::skipSamples(const Timestamp &length, AudioStream *stream) {
|
||||
int32 sampleCount = length.convertToFramerate(getRate()).totalNumberOfFrames();
|
||||
|
||||
if (sampleCount <= 0)
|
||||
return;
|
||||
|
||||
if (isStereo())
|
||||
sampleCount *= 2;
|
||||
|
||||
int16 *tempBuffer = new int16[sampleCount];
|
||||
uint32 result = stream->readBuffer(tempBuffer, sampleCount);
|
||||
delete[] tempBuffer;
|
||||
|
||||
// If this is the queue, make sure we subtract this number from the
|
||||
// amount queued
|
||||
if (stream == _queue)
|
||||
_samplesQueued -= result / (isStereo() ? 2 : 1);
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::findEdit(const Timestamp &position) {
|
||||
// Go through the edits look for where we find out we need to be. As long
|
||||
// as the position is >= to the edit's start time, it is considered to be in that
|
||||
// edit. seek() already figured out if we reached the last edit, so we don't need
|
||||
// to handle that case here.
|
||||
for (_curEdit = 0; _curEdit < _parentTrack->editList.size() - 1; _curEdit++) {
|
||||
Timestamp nextEditTime(0, _parentTrack->editList[_curEdit + 1].timeOffset, _decoder->_timeScale);
|
||||
if (position < nextEditTime)
|
||||
break;
|
||||
}
|
||||
|
||||
enterNewEdit(position);
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::enterNewEdit(const Timestamp &position) {
|
||||
_skipSamples = Timestamp(); // make sure our skip variable doesn't remain around
|
||||
|
||||
// If we're at the end of the edit list, there's nothing else for us to do here
|
||||
if (allDataRead())
|
||||
return;
|
||||
|
||||
// For an empty edit, we may need to adjust the start time
|
||||
if (_parentTrack->editList[_curEdit].mediaTime == -1) {
|
||||
// Just invalidate the current media position (and make sure the scale
|
||||
// is in terms of our rate so it simplifies things later)
|
||||
_curMediaPos = Timestamp(0, 0, getRate());
|
||||
|
||||
// Also handle shortening of the empty edit if needed
|
||||
if (position != Timestamp())
|
||||
_skipSamples = position.convertToFramerate(_decoder->_timeScale) - Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale);
|
||||
return;
|
||||
}
|
||||
|
||||
// I really hope I never need to implement this :P
|
||||
// But, I'll throw in this error just to make sure I catch anything with this...
|
||||
if (_parentTrack->editList[_curEdit].mediaRate != 1)
|
||||
warning("QuickTimeAudioDecoder: Unhandled QuickTime audio rate change");
|
||||
|
||||
// Reinitialize the codec
|
||||
((AudioSampleDesc *)_parentTrack->sampleDescs[0])->initCodec();
|
||||
_skipAACPrimer = true;
|
||||
|
||||
// First, we need to track down what audio sample we need
|
||||
// Convert our variables from the media time (position) and the edit time (based on position)
|
||||
// and the media time
|
||||
Timestamp curAudioTime = Timestamp(0, _parentTrack->editList[_curEdit].mediaTime, _parentTrack->timeScale)
|
||||
+ position.convertToFramerate(_parentTrack->timeScale)
|
||||
- Timestamp(0, _parentTrack->editList[_curEdit].timeOffset, _decoder->_timeScale).convertToFramerate(_parentTrack->timeScale);
|
||||
|
||||
uint32 sample = curAudioTime.totalNumberOfFrames();
|
||||
uint32 seekSample = sample;
|
||||
|
||||
if (!isOldDemuxing()) {
|
||||
// For MPEG-4 style demuxing, we need to track down the sample based on the time
|
||||
// The old style demuxing doesn't require this because each "sample"'s duration
|
||||
// is just 1
|
||||
uint32 curSample = 0;
|
||||
seekSample = 0;
|
||||
|
||||
for (int32 i = 0; i < _parentTrack->timeToSampleCount; i++) {
|
||||
uint32 sampleCount = _parentTrack->timeToSample[i].count * _parentTrack->timeToSample[i].duration;
|
||||
|
||||
if (sample < curSample + sampleCount) {
|
||||
seekSample += (sample - curSample) / _parentTrack->timeToSample[i].duration;
|
||||
break;
|
||||
}
|
||||
|
||||
seekSample += _parentTrack->timeToSample[i].count;
|
||||
curSample += sampleCount;
|
||||
}
|
||||
}
|
||||
|
||||
// Now to track down what chunk it's in
|
||||
uint32 totalSamples = 0;
|
||||
_curChunk = 0;
|
||||
for (uint32 i = 0; i < _parentTrack->chunkCount; i++, _curChunk++) {
|
||||
uint32 chunkSampleCount = getAudioChunkSampleCount(i);
|
||||
|
||||
if (seekSample < totalSamples + chunkSampleCount)
|
||||
break;
|
||||
|
||||
totalSamples += chunkSampleCount;
|
||||
}
|
||||
|
||||
// Now we get to have fun and convert *back* to an actual time
|
||||
// We don't want the sample count to be modified at this point, though
|
||||
if (!isOldDemuxing())
|
||||
totalSamples = getAACSampleTime(totalSamples);
|
||||
|
||||
_curMediaPos = Timestamp(0, totalSamples, getRate());
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueStream(AudioStream *stream, const Timestamp &length) {
|
||||
// If the samples are stereo and the container is mono, force the samples
|
||||
// to be mono.
|
||||
if (stream->isStereo() && !isStereo())
|
||||
_queue->queueAudioStream(new ForcedMonoAudioStream(stream, DisposeAfterUse::YES), DisposeAfterUse::YES);
|
||||
else
|
||||
_queue->queueAudioStream(stream, DisposeAfterUse::YES);
|
||||
|
||||
_samplesQueued += length.convertToFramerate(getRate()).totalNumberOfFrames();
|
||||
}
|
||||
|
||||
uint32 QuickTimeAudioDecoder::QuickTimeAudioTrack::getAudioChunkSampleCount(uint chunk) const {
|
||||
uint32 sampleCount = 0;
|
||||
|
||||
for (uint32 i = 0; i < _parentTrack->sampleToChunkCount; i++)
|
||||
if (chunk >= _parentTrack->sampleToChunk[i].first)
|
||||
sampleCount = _parentTrack->sampleToChunk[i].count;
|
||||
|
||||
return sampleCount;
|
||||
}
|
||||
|
||||
Timestamp QuickTimeAudioDecoder::QuickTimeAudioTrack::getChunkLength(uint chunk, bool skipAACPrimer) const {
|
||||
uint32 chunkSampleCount = getAudioChunkSampleCount(chunk);
|
||||
|
||||
if (isOldDemuxing())
|
||||
return Timestamp(0, chunkSampleCount, getRate());
|
||||
|
||||
// AAC needs some extra handling, of course
|
||||
return Timestamp(0, getAACSampleTime(chunkSampleCount, skipAACPrimer), getRate());
|
||||
}
|
||||
|
||||
uint32 QuickTimeAudioDecoder::QuickTimeAudioTrack::getAACSampleTime(uint32 totalSampleCount, bool skipAACPrimer) const{
|
||||
uint32 curSample = 0;
|
||||
uint32 time = 0;
|
||||
|
||||
for (int32 i = 0; i < _parentTrack->timeToSampleCount; i++) {
|
||||
uint32 sampleCount = _parentTrack->timeToSample[i].count;
|
||||
|
||||
if (totalSampleCount < curSample + sampleCount) {
|
||||
time += (totalSampleCount - curSample) * _parentTrack->timeToSample[i].duration;
|
||||
break;
|
||||
}
|
||||
|
||||
time += _parentTrack->timeToSample[i].count * _parentTrack->timeToSample[i].duration;
|
||||
curSample += sampleCount;
|
||||
}
|
||||
|
||||
// The first chunk of AAC contains "duration" samples that are used as a primer
|
||||
// We need to subtract that number from the duration for the first chunk. See:
|
||||
// https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFAppenG/QTFFAppenG.html#//apple_ref/doc/uid/TP40000939-CH2-SW1
|
||||
// The skipping of both the primer and the remainder are handled by the AAC code,
|
||||
// whereas the timing of the remainder are handled by this time-to-sample chunk
|
||||
// code already.
|
||||
// We have to do this after each time we reinitialize the codec
|
||||
if (skipAACPrimer) {
|
||||
assert(_parentTrack->timeToSampleCount > 0);
|
||||
time -= _parentTrack->timeToSample[0].duration;
|
||||
}
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::AudioSampleDesc::AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) : Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
|
||||
_channels = 0;
|
||||
_sampleRate = 0;
|
||||
_samplesPerFrame = 0;
|
||||
_bytesPerFrame = 0;
|
||||
_bitsPerSample = 0;
|
||||
_codec = nullptr;
|
||||
}
|
||||
|
||||
QuickTimeAudioDecoder::AudioSampleDesc::~AudioSampleDesc() {
|
||||
delete _codec;
|
||||
}
|
||||
|
||||
bool QuickTimeAudioDecoder::AudioSampleDesc::isAudioCodecSupported() const {
|
||||
// Check if the codec is a supported codec
|
||||
if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ') || _codecTag == MKTAG('i', 'm', 'a', '4'))
|
||||
return true;
|
||||
|
||||
#ifdef USE_QDM2
|
||||
if (_codecTag == MKTAG('Q', 'D', 'M', '2'))
|
||||
return true;
|
||||
#endif
|
||||
|
||||
if (_codecTag == MKTAG('m', 'p', '4', 'a')) {
|
||||
Common::String audioType;
|
||||
switch (_objectTypeMP4) {
|
||||
case 0x40: // AAC
|
||||
#ifdef USE_FAAD
|
||||
return true;
|
||||
#else
|
||||
audioType = "AAC";
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
audioType = "Unknown";
|
||||
break;
|
||||
}
|
||||
warning("No MPEG-4 audio (%s) support", audioType.c_str());
|
||||
} else {
|
||||
warning("Audio Codec Not Supported: \'%s\'", tag2str(_codecTag));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AudioStream *QuickTimeAudioDecoder::AudioSampleDesc::createAudioStream(Common::SeekableReadStream *stream) const {
|
||||
if (!stream)
|
||||
return nullptr;
|
||||
|
||||
if (_codec) {
|
||||
// If we've loaded a codec, make sure we use first
|
||||
AudioStream *audioStream = _codec->decodeFrame(*stream);
|
||||
delete stream;
|
||||
return audioStream;
|
||||
} else if (_codecTag == MKTAG('t', 'w', 'o', 's') || _codecTag == MKTAG('r', 'a', 'w', ' ')) {
|
||||
// Fortunately, most of the audio used in Myst videos is raw...
|
||||
uint16 flags = 0;
|
||||
if (_codecTag == MKTAG('r', 'a', 'w', ' '))
|
||||
flags |= FLAG_UNSIGNED;
|
||||
if (_channels == 2)
|
||||
flags |= FLAG_STEREO;
|
||||
if (_bitsPerSample == 16)
|
||||
flags |= FLAG_16BITS;
|
||||
return makeRawStream(stream, _sampleRate, flags);
|
||||
} else if (_codecTag == MKTAG('i', 'm', 'a', '4')) {
|
||||
// Riven uses this codec (as do some Myst ME videos)
|
||||
return makeADPCMStream(stream, DisposeAfterUse::YES, stream->size(), kADPCMApple, _sampleRate, _channels, 34);
|
||||
} else if (_codecTag == MKTAG('a', 'l', 'a', 'w')) {
|
||||
return makeALawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels);
|
||||
} else if (_codecTag == MKTAG('u', 'l', 'a', 'w')) {
|
||||
return makeMuLawStream(stream, DisposeAfterUse::YES, _sampleRate, _channels);
|
||||
}
|
||||
|
||||
error("Unsupported audio codec");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QuickTimeAudioDecoder::AudioSampleDesc::initCodec() {
|
||||
delete _codec; _codec = nullptr;
|
||||
|
||||
switch (_codecTag) {
|
||||
case MKTAG('Q', 'D', 'M', '2'):
|
||||
#ifdef USE_QDM2
|
||||
_codec = makeQDM2Decoder(_extraData);
|
||||
#endif
|
||||
break;
|
||||
case MKTAG('m', 'p', '4', 'a'):
|
||||
#ifdef USE_FAAD
|
||||
if (_objectTypeMP4 == 0x40)
|
||||
_codec = makeAACDecoder(_extraData);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A wrapper around QuickTimeAudioDecoder that implements the SeekableAudioStream API
|
||||
*/
|
||||
class QuickTimeAudioStream : public SeekableAudioStream, public QuickTimeAudioDecoder {
|
||||
public:
|
||||
QuickTimeAudioStream() {}
|
||||
~QuickTimeAudioStream() {}
|
||||
|
||||
bool openFromFile(const Common::Path &filename) {
|
||||
return QuickTimeAudioDecoder::loadAudioFile(filename) && !_audioTracks.empty();
|
||||
}
|
||||
|
||||
bool openFromStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle) {
|
||||
return QuickTimeAudioDecoder::loadAudioStream(stream, disposeFileHandle) && !_audioTracks.empty();
|
||||
}
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples) override {
|
||||
int samples = 0;
|
||||
|
||||
while (samples < numSamples && !endOfData()) {
|
||||
if (!_audioTracks[0]->hasDataInQueue())
|
||||
_audioTracks[0]->queueAudio();
|
||||
samples += _audioTracks[0]->readBuffer(buffer + samples, numSamples - samples);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool isStereo() const override { return _audioTracks[0]->isStereo(); }
|
||||
int getRate() const override { return _audioTracks[0]->getRate(); }
|
||||
bool endOfData() const override { return _audioTracks[0]->endOfData(); }
|
||||
|
||||
// SeekableAudioStream API
|
||||
bool seek(const Timestamp &where) override { return _audioTracks[0]->seek(where); }
|
||||
Timestamp getLength() const override { return _audioTracks[0]->getLength(); }
|
||||
};
|
||||
|
||||
SeekableAudioStream *makeQuickTimeStream(const Common::Path &filename) {
|
||||
QuickTimeAudioStream *audioStream = new QuickTimeAudioStream();
|
||||
|
||||
if (!audioStream->openFromFile(filename)) {
|
||||
delete audioStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioStream;
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeQuickTimeStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
QuickTimeAudioStream *audioStream = new QuickTimeAudioStream();
|
||||
|
||||
if (!audioStream->openFromStream(stream, disposeAfterUse)) {
|
||||
delete audioStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return audioStream;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
68
audio/decoders/quicktime.h
Normal file
68
audio/decoders/quicktime.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - groovie
|
||||
* - mohawk
|
||||
* - pegasus
|
||||
* - sci
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_QUICKTIME_H
|
||||
#define AUDIO_QUICKTIME_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class Path;
|
||||
class SeekableReadStream;
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Try to load a QuickTime sound file from the given file name and create a SeekableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param filename the filename of the file from which to read the data
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeQuickTimeStream(const Common::Path &filename);
|
||||
|
||||
/**
|
||||
* Try to load a QuickTime sound file from the given seekable stream and create a SeekableAudioStream
|
||||
* from that data.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeQuickTimeStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
142
audio/decoders/quicktime_intern.h
Normal file
142
audio/decoders/quicktime_intern.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal interface to the QuickTime audio decoder.
|
||||
*
|
||||
* This is available so that the QuickTimeVideoDecoder can use
|
||||
* this directly.
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_QUICKTIME_INTERN_H
|
||||
#define AUDIO_QUICKTIME_INTERN_H
|
||||
|
||||
#include "common/formats/quicktime.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
class String;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class Codec;
|
||||
|
||||
class QuickTimeAudioDecoder : public Common::QuickTimeParser {
|
||||
public:
|
||||
QuickTimeAudioDecoder();
|
||||
virtual ~QuickTimeAudioDecoder();
|
||||
|
||||
/**
|
||||
* Load a QuickTime audio file
|
||||
* @param filename the filename to load
|
||||
*/
|
||||
bool loadAudioFile(const Common::Path &filename);
|
||||
|
||||
/**
|
||||
* Load a QuickTime audio file from a SeekableReadStream
|
||||
* @param stream the stream to load
|
||||
*/
|
||||
bool loadAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeFileHandle);
|
||||
|
||||
protected:
|
||||
class QuickTimeAudioTrack : public SeekableAudioStream {
|
||||
public:
|
||||
QuickTimeAudioTrack(QuickTimeAudioDecoder *decoder, Track *parentTrack);
|
||||
~QuickTimeAudioTrack();
|
||||
|
||||
// AudioStream API
|
||||
int readBuffer(int16 *buffer, const int numSamples);
|
||||
bool isStereo() const { return _queue->isStereo(); }
|
||||
int getRate() const { return _queue->getRate(); }
|
||||
bool endOfData() const;
|
||||
|
||||
// SeekableAudioStream API
|
||||
bool seek(const Timestamp &where);
|
||||
Timestamp getLength() const;
|
||||
|
||||
// Queue *at least* "length" audio
|
||||
// If length is zero, it queues the next logical block of audio whether
|
||||
// that be a whole edit or just one chunk within an edit
|
||||
void queueAudio(const Timestamp &length = Timestamp());
|
||||
Track *getParent() const { return _parentTrack; }
|
||||
void queueRemainingAudio();
|
||||
bool hasDataInQueue() const { return _samplesQueued != 0; }
|
||||
|
||||
private:
|
||||
QuickTimeAudioDecoder *_decoder;
|
||||
Track *_parentTrack;
|
||||
QueuingAudioStream *_queue;
|
||||
uint _curChunk;
|
||||
Timestamp _curMediaPos, _skipSamples;
|
||||
uint32 _curEdit, _samplesQueued;
|
||||
bool _skipAACPrimer;
|
||||
|
||||
QueuingAudioStream *createStream() const;
|
||||
AudioStream *readAudioChunk(uint chunk);
|
||||
bool isOldDemuxing() const;
|
||||
void skipSamples(const Timestamp &length, AudioStream *stream);
|
||||
void findEdit(const Timestamp &position);
|
||||
bool allDataRead() const;
|
||||
void enterNewEdit(const Timestamp &position);
|
||||
void queueStream(AudioStream *stream, const Timestamp &length);
|
||||
uint32 getAudioChunkSampleCount(uint chunk) const;
|
||||
Timestamp getChunkLength(uint chunk, bool skipAACPrimer = false) const;
|
||||
uint32 getAACSampleTime(uint32 totalSampleCount, bool skipAACPrimer = false) const;
|
||||
Timestamp getCurrentTrackTime() const;
|
||||
};
|
||||
|
||||
class AudioSampleDesc : public Common::QuickTimeParser::SampleDesc {
|
||||
public:
|
||||
AudioSampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
|
||||
~AudioSampleDesc();
|
||||
|
||||
bool isAudioCodecSupported() const;
|
||||
|
||||
AudioStream *createAudioStream(Common::SeekableReadStream *stream) const;
|
||||
void initCodec();
|
||||
|
||||
// TODO: Make private in the long run
|
||||
uint16 _bitsPerSample;
|
||||
uint16 _channels;
|
||||
uint32 _sampleRate;
|
||||
uint32 _samplesPerFrame;
|
||||
uint32 _bytesPerFrame;
|
||||
|
||||
private:
|
||||
Codec *_codec;
|
||||
};
|
||||
|
||||
// Common::QuickTimeParser API
|
||||
virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
|
||||
|
||||
void init();
|
||||
|
||||
Common::Array<QuickTimeAudioTrack *> _audioTracks;
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
245
audio/decoders/raw.cpp
Normal file
245
audio/decoders/raw.cpp
Normal file
@@ -0,0 +1,245 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/endian.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- RawStream ---
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
* This is a stream, which allows for playing raw PCM data from a stream.
|
||||
*/
|
||||
template<int bytesPerSample, bool isUnsigned, bool isLE>
|
||||
class RawStream : public SeekableAudioStream {
|
||||
public:
|
||||
RawStream(int rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream)
|
||||
: _rate(rate), _isStereo(stereo), _playtime(0, rate), _stream(stream, disposeStream), _endOfData(false), _buffer(nullptr) {
|
||||
// Setup our buffer for readBuffer
|
||||
_buffer = new byte[kSampleBufferLength * bytesPerSample];
|
||||
assert(_buffer);
|
||||
|
||||
// Calculate the total playtime of the stream
|
||||
_playtime = Timestamp(0, _stream->size() / (_isStereo ? 2 : 1) / bytesPerSample, rate);
|
||||
}
|
||||
|
||||
~RawStream() {
|
||||
delete[] _buffer;
|
||||
}
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool isStereo() const override { return _isStereo; }
|
||||
bool endOfData() const override { return _endOfData; }
|
||||
|
||||
int getRate() const override { return _rate; }
|
||||
Timestamp getLength() const override { return _playtime; }
|
||||
|
||||
bool seek(const Timestamp &where) override;
|
||||
private:
|
||||
const int _rate; ///< Sample rate of stream
|
||||
const bool _isStereo; ///< Whether this is a stereo stream
|
||||
Timestamp _playtime; ///< Calculated total play time
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _stream; ///< Stream to read data from
|
||||
bool _endOfData; ///< Whether the stream end has been reached
|
||||
|
||||
byte *_buffer; ///< Buffer used in readBuffer
|
||||
enum {
|
||||
/**
|
||||
* How many samples we can buffer at once.
|
||||
*
|
||||
* TODO: Check whether this size suffices
|
||||
* for systems with slow disk I/O.
|
||||
*/
|
||||
kSampleBufferLength = 2048
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill the temporary sample buffer used in readBuffer.
|
||||
*
|
||||
* @param maxSamples Maximum samples to read.
|
||||
* @return actual count of samples read.
|
||||
*/
|
||||
int fillBuffer(int maxSamples);
|
||||
};
|
||||
|
||||
template<int bytesPerSample, bool isUnsigned, bool isLE>
|
||||
int RawStream<bytesPerSample, isUnsigned, isLE>::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesLeft = numSamples;
|
||||
|
||||
while (samplesLeft > 0) {
|
||||
// Try to read up to "samplesLeft" samples.
|
||||
int len = fillBuffer(samplesLeft);
|
||||
|
||||
// In case we were not able to read any samples
|
||||
// we will stop reading here.
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
// Adjust the samples left to read.
|
||||
samplesLeft -= len;
|
||||
|
||||
// Copy the data to the caller's buffer.
|
||||
const byte *src = _buffer;
|
||||
while (len-- > 0) {
|
||||
if (bytesPerSample == 1)
|
||||
*buffer++ = (*src << 8) ^ (isUnsigned ? 0x8000 : 0);
|
||||
else if (bytesPerSample == 2)
|
||||
*buffer++ = ((isLE ? READ_LE_UINT16(src) : READ_BE_UINT16(src)) ^ (isUnsigned ? 0x8000 : 0));
|
||||
else // if (bytesPerSample == 3)
|
||||
*buffer++ = (((int16)((isLE ? READ_LE_UINT24(src) : READ_BE_UINT24(src)) >> 8)) ^ (isUnsigned ? 0x8000 : 0));
|
||||
|
||||
src += bytesPerSample;
|
||||
}
|
||||
}
|
||||
|
||||
return numSamples - samplesLeft;
|
||||
}
|
||||
|
||||
template<int bytesPerSample, bool isUnsigned, bool isLE>
|
||||
int RawStream<bytesPerSample, isUnsigned, isLE>::fillBuffer(int maxSamples) {
|
||||
int bufferedSamples = 0;
|
||||
byte *dst = _buffer;
|
||||
|
||||
// We can only read up to "kSampleBufferLength" samples
|
||||
// so we take this into consideration, when trying to
|
||||
// read up to maxSamples.
|
||||
maxSamples = MIN<int>(kSampleBufferLength, maxSamples);
|
||||
|
||||
// We will only read up to maxSamples
|
||||
while (maxSamples > 0 && !endOfData()) {
|
||||
// Try to read all the sample data and update the
|
||||
// destination pointer.
|
||||
const int bytesRead = _stream->read(dst, maxSamples * bytesPerSample);
|
||||
dst += bytesRead;
|
||||
|
||||
// Calculate how many samples we actually read.
|
||||
const int samplesRead = bytesRead / bytesPerSample;
|
||||
|
||||
// Update all status variables
|
||||
bufferedSamples += samplesRead;
|
||||
maxSamples -= samplesRead;
|
||||
|
||||
// We stop stream playback, when we reached the end of the data stream.
|
||||
// We also stop playback when an error occures.
|
||||
if (_stream->pos() == _stream->size() || _stream->err() || _stream->eos())
|
||||
_endOfData = true;
|
||||
}
|
||||
|
||||
return bufferedSamples;
|
||||
}
|
||||
|
||||
template<int bytesPerSample, bool isUnsigned, bool isLE>
|
||||
bool RawStream<bytesPerSample, isUnsigned, isLE>::seek(const Timestamp &where) {
|
||||
_endOfData = true;
|
||||
|
||||
if (where > _playtime)
|
||||
return false;
|
||||
|
||||
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
|
||||
_stream->seek(seekSample * bytesPerSample, SEEK_SET);
|
||||
|
||||
// In case of an error we will not continue stream playback.
|
||||
if (!_stream->err() && !_stream->eos() && _stream->pos() != _stream->size())
|
||||
_endOfData = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Raw stream factories ---
|
||||
#pragma mark -
|
||||
|
||||
/**
|
||||
* The following templated function is a helper to simplify the public makeRawStream function
|
||||
*/
|
||||
template <bool isUnsigned>
|
||||
static FORCEINLINE SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream, int rate, bool isStereo, DisposeAfterUse::Flag disposeAfterUse, bool isLE, int bytesPerSample) {
|
||||
switch (bytesPerSample) {
|
||||
case 3:
|
||||
if (isLE) {
|
||||
return new RawStream<3, isUnsigned, true>(rate, isStereo, disposeAfterUse, stream);
|
||||
} else {
|
||||
return new RawStream<3, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
|
||||
}
|
||||
case 2:
|
||||
if (isLE) {
|
||||
return new RawStream<2, isUnsigned, true>(rate, isStereo, disposeAfterUse, stream);
|
||||
} else {
|
||||
return new RawStream<2, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
|
||||
}
|
||||
default:
|
||||
return new RawStream<1, isUnsigned, false>(rate, isStereo, disposeAfterUse, stream);
|
||||
}
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||
int rate, byte flags,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
const bool isStereo = (flags & Audio::FLAG_STEREO) != 0;
|
||||
const int bytesPerSample = (flags & Audio::FLAG_24BITS ? 3 : (flags & Audio::FLAG_16BITS ? 2 : 1));
|
||||
const bool isUnsigned = (flags & Audio::FLAG_UNSIGNED) != 0;
|
||||
const bool isLE = (flags & Audio::FLAG_LITTLE_ENDIAN) != 0;
|
||||
|
||||
assert(stream->size() % (bytesPerSample * (isStereo ? 2 : 1)) == 0);
|
||||
|
||||
if (isUnsigned) {
|
||||
return makeRawStream<true>(stream, rate, isStereo, disposeAfterUse, isLE, bytesPerSample);
|
||||
} else {
|
||||
return makeRawStream<false>(stream, rate, isStereo, disposeAfterUse, isLE, bytesPerSample);
|
||||
}
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
|
||||
int rate, byte flags,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
return makeRawStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, flags, DisposeAfterUse::YES);
|
||||
}
|
||||
|
||||
class PacketizedRawStream : public StatelessPacketizedAudioStream {
|
||||
public:
|
||||
PacketizedRawStream(int rate, byte flags) :
|
||||
StatelessPacketizedAudioStream(rate, ((flags & FLAG_STEREO) != 0) ? 2 : 1), _flags(flags) {}
|
||||
|
||||
protected:
|
||||
AudioStream *makeStream(Common::SeekableReadStream *data) override;
|
||||
|
||||
private:
|
||||
byte _flags;
|
||||
};
|
||||
|
||||
AudioStream *PacketizedRawStream::makeStream(Common::SeekableReadStream *data) {
|
||||
return makeRawStream(data, getRate(), _flags);
|
||||
}
|
||||
|
||||
PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags) {
|
||||
return new PacketizedRawStream(rate, flags);
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
108
audio/decoders/raw.h
Normal file
108
audio/decoders/raw.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_RAW_H
|
||||
#define AUDIO_RAW_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#include "common/list.h"
|
||||
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class PacketizedAudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Various flags which can be bit-ORed and then passed to
|
||||
* makeRawStream and some other AudioStream factories
|
||||
* to control their behavior.
|
||||
*
|
||||
* Engine authors are advised not to rely on a certain value or
|
||||
* order of these flags (in particular, do not store them verbatim
|
||||
* in savestates).
|
||||
*/
|
||||
enum RawFlags {
|
||||
/** unsigned samples (default: signed) */
|
||||
FLAG_UNSIGNED = 1 << 0,
|
||||
|
||||
/** sound is 16 bits wide (default: 8bit) */
|
||||
FLAG_16BITS = 1 << 1,
|
||||
|
||||
/** sound is 24 bits wide (default: 8bit) */
|
||||
FLAG_24BITS = 1 << 2,
|
||||
|
||||
/** samples are little endian (default: big endian) */
|
||||
FLAG_LITTLE_ENDIAN = 1 << 3,
|
||||
|
||||
/** sound is in stereo (default: mono) */
|
||||
FLAG_STEREO = 1 << 4
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates an audio stream, which plays from the given buffer.
|
||||
*
|
||||
* @param buffer Buffer to play from.
|
||||
* @param size Size of the buffer in bytes.
|
||||
* @param rate Rate of the sound data.
|
||||
* @param flags Audio flags combination.
|
||||
* @see RawFlags
|
||||
* @param disposeAfterUse Whether to free the buffer after use (with free!).
|
||||
* @return The new SeekableAudioStream (or 0 on failure).
|
||||
*/
|
||||
SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size,
|
||||
int rate, byte flags,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
/**
|
||||
* Creates an audio stream, which plays from the given stream.
|
||||
*
|
||||
* @param stream Stream object to play from.
|
||||
* @param rate Rate of the sound data.
|
||||
* @param flags Audio flags combination.
|
||||
* @see RawFlags
|
||||
* @param disposeAfterUse Whether to delete the stream after use.
|
||||
* @return The new SeekableAudioStream (or 0 on failure).
|
||||
*/
|
||||
SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream,
|
||||
int rate, byte flags,
|
||||
DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
|
||||
|
||||
/**
|
||||
* Creates a PacketizedAudioStream that will automatically queue
|
||||
* packets as individual AudioStreams like returned by makeRawStream.
|
||||
*
|
||||
* @param rate Rate of the sound data.
|
||||
* @param flags Audio flags combination.
|
||||
* @see RawFlags
|
||||
* @return The new PacketizedAudioStream.
|
||||
*/
|
||||
PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
52
audio/decoders/util.h
Normal file
52
audio/decoders/util.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_UTIL_H
|
||||
#define AUDIO_DECODERS_UTIL_H
|
||||
|
||||
#include "common/types.h"
|
||||
#include "common/util.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// Convert one float sample into a int16 sample
|
||||
static inline int16 floatToInt16(float src) {
|
||||
return (int16) CLIP<int>((int) floor(src + 0.5), -32768, 32767);
|
||||
}
|
||||
|
||||
// Convert planar float samples into interleaved int16 samples
|
||||
static inline void floatToInt16Interleave(int16 *dst, const float **src,
|
||||
uint32 length, uint8 channels) {
|
||||
if (channels == 2) {
|
||||
for (uint32 i = 0; i < length; i++) {
|
||||
dst[2 * i ] = floatToInt16(src[0][i]);
|
||||
dst[2 * i + 1] = floatToInt16(src[1][i]);
|
||||
}
|
||||
} else {
|
||||
for (uint8 c = 0; c < channels; c++)
|
||||
for (uint32 i = 0, j = c; i < length; i++, j += channels)
|
||||
dst[j] = floatToInt16(src[c][i]);
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // AUDIO_DECODERS_UTIL_H
|
||||
490
audio/decoders/voc.cpp
Normal file
490
audio/decoders/voc.cpp
Normal file
@@ -0,0 +1,490 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "audio/decoders/voc.h"
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/endian.h"
|
||||
#include "common/util.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/list.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/voc.h"
|
||||
|
||||
namespace Audio {
|
||||
|
||||
bool checkVOCHeader(Common::ReadStream &stream) {
|
||||
VocFileHeader fileHeader;
|
||||
|
||||
if (stream.read(&fileHeader, 8) != 8)
|
||||
return false;
|
||||
|
||||
if (!memcmp(&fileHeader, "VTLK", 4)) {
|
||||
if (stream.read(&fileHeader, sizeof(VocFileHeader)) != sizeof(VocFileHeader))
|
||||
return false;
|
||||
} else if (!memcmp(&fileHeader, "Creative", 8)) {
|
||||
if (stream.read(((byte *)&fileHeader) + 8, sizeof(VocFileHeader) - 8) != sizeof(VocFileHeader) - 8)
|
||||
return false;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (memcmp(fileHeader.desc, "Creative Voice File", 19) != 0)
|
||||
return false;
|
||||
//if (fileHeader.desc[19] != 0x1A)
|
||||
// debug(3, "checkVOCHeader: Partially invalid header");
|
||||
|
||||
int32 offset = FROM_LE_16(fileHeader.datablock_offset);
|
||||
int16 version = FROM_LE_16(fileHeader.version);
|
||||
int16 code = FROM_LE_16(fileHeader.id);
|
||||
|
||||
if (offset != sizeof(VocFileHeader))
|
||||
return false;
|
||||
|
||||
// 0x100 is an invalid VOC version used by German version of DOTT (Disk) and
|
||||
// French version of Simon the Sorcerer 2 (CD)
|
||||
if (version != 0x010A && version != 0x0114 && version != 0x0100)
|
||||
return false;
|
||||
|
||||
if (code != ~version + 0x1234)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
VocStream::VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse)
|
||||
: _stream(stream), _disposeAfterUse(disposeAfterUse), _isUnsigned(isUnsigned), _rate(0),
|
||||
_length(), _blocks(), _curBlock(_blocks.end()), _blockLeft(0), _buffer() {
|
||||
preProcess();
|
||||
}
|
||||
|
||||
VocStream::~VocStream() {
|
||||
if (_disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete _stream;
|
||||
}
|
||||
|
||||
int VocStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samplesLeft = numSamples;
|
||||
while (samplesLeft > 0) {
|
||||
// Try to read up to "samplesLeft" samples.
|
||||
int len = fillBuffer(samplesLeft);
|
||||
|
||||
// In case we were not able to read any samples
|
||||
// we will stop reading here.
|
||||
if (!len)
|
||||
break;
|
||||
|
||||
// Adjust the samples left to read.
|
||||
samplesLeft -= len;
|
||||
|
||||
// Copy the data to the caller's buffer.
|
||||
const byte *src = _buffer;
|
||||
while (len-- > 0)
|
||||
*buffer++ = (*src++ << 8) ^ (_isUnsigned ? 0x8000 : 0);
|
||||
}
|
||||
|
||||
return numSamples - samplesLeft;
|
||||
}
|
||||
|
||||
void VocStream::updateBlockIfNeeded() {
|
||||
// Have we now finished this block? If so, read the next block
|
||||
if (_blockLeft == 0 && _curBlock != _blocks.end()) {
|
||||
// Find the next sample block
|
||||
while (true) {
|
||||
// Next block
|
||||
++_curBlock;
|
||||
|
||||
// Check whether we reached the end of the stream
|
||||
// yet.
|
||||
if (_curBlock == _blocks.end())
|
||||
return;
|
||||
|
||||
// Skip all none sample blocks for now
|
||||
if (_curBlock->code != 1 && _curBlock->code != 9)
|
||||
continue;
|
||||
|
||||
_stream->seek(_curBlock->sampleBlock.offset, SEEK_SET);
|
||||
|
||||
// In case of an error we will stop
|
||||
// stream playback.
|
||||
if (_stream->err()) {
|
||||
_blockLeft = 0;
|
||||
_curBlock = _blocks.end();
|
||||
} else {
|
||||
_blockLeft = _curBlock->sampleBlock.samples;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int VocStream::fillBuffer(int maxSamples) {
|
||||
int bufferedSamples = 0;
|
||||
byte *dst = _buffer;
|
||||
|
||||
// We can only read up to "kSampleBufferLength" samples
|
||||
// so we take this into consideration, when trying to
|
||||
// read up to maxSamples.
|
||||
maxSamples = MIN<int>(kSampleBufferLength, maxSamples);
|
||||
|
||||
// We will only read up to maxSamples
|
||||
while (maxSamples > 0 && !endOfData()) {
|
||||
// Calculate how many samples we can safely read
|
||||
// from the current block.
|
||||
const int len = MIN<int>(maxSamples, _blockLeft);
|
||||
|
||||
// Try to read all the sample data and update the
|
||||
// destination pointer.
|
||||
const int bytesRead = _stream->read(dst, len);
|
||||
dst += bytesRead;
|
||||
|
||||
// Calculate how many samples we actually read.
|
||||
const int samplesRead = bytesRead;
|
||||
|
||||
// Update all status variables
|
||||
bufferedSamples += samplesRead;
|
||||
maxSamples -= samplesRead;
|
||||
_blockLeft -= samplesRead;
|
||||
|
||||
// In case of an error or end of stream we will stop
|
||||
// stream playback.
|
||||
if (_stream->err() || _stream->eos()) {
|
||||
_blockLeft = 0;
|
||||
_curBlock = _blocks.end();
|
||||
break;
|
||||
}
|
||||
|
||||
// Advance to the next block in case the current
|
||||
// one is already finished.
|
||||
updateBlockIfNeeded();
|
||||
}
|
||||
|
||||
return bufferedSamples;
|
||||
}
|
||||
|
||||
bool VocStream::seek(const Timestamp &where) {
|
||||
// Invalidate stream
|
||||
_blockLeft = 0;
|
||||
_curBlock = _blocks.end();
|
||||
|
||||
if (where > _length)
|
||||
return false;
|
||||
|
||||
// Search for the block containing the requested sample
|
||||
const uint32 seekSample = convertTimeToStreamPos(where, getRate(), isStereo()).totalNumberOfFrames();
|
||||
uint32 curSample = 0;
|
||||
|
||||
for (_curBlock = _blocks.begin(); _curBlock != _blocks.end(); ++_curBlock) {
|
||||
// Skip all none sample blocks for now
|
||||
if (_curBlock->code != 1 && _curBlock->code != 9)
|
||||
continue;
|
||||
|
||||
uint32 nextBlockSample = curSample + _curBlock->sampleBlock.samples;
|
||||
|
||||
if (nextBlockSample > seekSample)
|
||||
break;
|
||||
|
||||
curSample = nextBlockSample;
|
||||
}
|
||||
|
||||
if (_curBlock == _blocks.end()) {
|
||||
return ((seekSample - curSample) == 0);
|
||||
} else {
|
||||
const uint32 offset = seekSample - curSample;
|
||||
|
||||
_stream->seek(_curBlock->sampleBlock.offset + offset, SEEK_SET);
|
||||
|
||||
// In case of an error we will stop
|
||||
// stream playback.
|
||||
if (_stream->err()) {
|
||||
_blockLeft = 0;
|
||||
_curBlock = _blocks.end();
|
||||
} else {
|
||||
_blockLeft = _curBlock->sampleBlock.samples - offset;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void VocStream::preProcess() {
|
||||
Block block;
|
||||
|
||||
// Scan through the file and collect all blocks
|
||||
while (true) {
|
||||
block.code = _stream->readByte();
|
||||
block.length = 0;
|
||||
|
||||
// If we hit EOS here we found the end of the VOC file.
|
||||
// According to https://wiki.multimedia.cx/index.php?title=Creative_Voice
|
||||
// there is no need for an "Terminator" block to be present.
|
||||
// In case we hit a "Terminator" block we also break here.
|
||||
if (_stream->eos() || block.code == 0)
|
||||
break;
|
||||
// We will allow invalid block numbers as terminators. This is needed,
|
||||
// since some games ship broken VOC files. The following occasions are
|
||||
// known:
|
||||
// - 128 is used as terminator in Simon 1 Amiga CD32
|
||||
// - Full Throttle contains a VOC file with an incorrect block length
|
||||
// resulting in a sample (127) to be read as block code.
|
||||
if (block.code > 9) {
|
||||
warning("VocStream::preProcess: Caught %d as terminator", block.code);
|
||||
break;
|
||||
}
|
||||
|
||||
block.length = _stream->readByte();
|
||||
block.length |= _stream->readByte() << 8;
|
||||
block.length |= _stream->readByte() << 16;
|
||||
|
||||
// Premature end of stream => error!
|
||||
if (_stream->eos() || _stream->err()) {
|
||||
warning("VocStream::preProcess: Reading failed");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 skip = 0;
|
||||
|
||||
switch (block.code) {
|
||||
// Sound data
|
||||
case 1:
|
||||
// Sound data (New format)
|
||||
case 9:
|
||||
if (block.code == 1) {
|
||||
if (block.length < 2) {
|
||||
warning("Invalid sound data block length %d in VOC file", block.length);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read header data
|
||||
int freqDiv = _stream->readByte();
|
||||
// Prevent division through 0
|
||||
if (freqDiv == 256) {
|
||||
warning("Invalid frequency divisor 256 in VOC file");
|
||||
return;
|
||||
}
|
||||
block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
|
||||
|
||||
int codec = _stream->readByte();
|
||||
// We only support 8bit PCM
|
||||
if (codec != 0) {
|
||||
warning("Unhandled codec %d in VOC file", codec);
|
||||
return;
|
||||
}
|
||||
|
||||
block.sampleBlock.samples = skip = block.length - 2;
|
||||
block.sampleBlock.offset = _stream->pos();
|
||||
|
||||
// Check the last block if there is any
|
||||
if (_blocks.size() > 0) {
|
||||
BlockList::iterator lastBlock = _blocks.end();
|
||||
--lastBlock;
|
||||
// When we have found a block 8 as predecessor
|
||||
// we need to use its settings
|
||||
if (lastBlock->code == 8) {
|
||||
block.sampleBlock.rate = lastBlock->sampleBlock.rate;
|
||||
// Remove the block since we don't need it anymore
|
||||
_blocks.erase(lastBlock);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (block.length < 12) {
|
||||
warning("Invalid sound data (wew format) block length %d in VOC file", block.length);
|
||||
return;
|
||||
}
|
||||
|
||||
block.sampleBlock.rate = _stream->readUint32LE();
|
||||
int bitsPerSample = _stream->readByte();
|
||||
// We only support 8bit PCM
|
||||
if (bitsPerSample != 8) {
|
||||
warning("Unhandled bits per sample %d in VOC file", bitsPerSample);
|
||||
return;
|
||||
}
|
||||
int channels = _stream->readByte();
|
||||
// We only support mono
|
||||
if (channels != 1) {
|
||||
warning("Unhandled channel count %d in VOC file", channels);
|
||||
return;
|
||||
}
|
||||
int codec = _stream->readUint16LE();
|
||||
// We only support 8bit PCM
|
||||
if (codec != 0) {
|
||||
warning("Unhandled codec %d in VOC file", codec);
|
||||
return;
|
||||
}
|
||||
/*uint32 reserved = */_stream->readUint32LE();
|
||||
block.sampleBlock.offset = _stream->pos();
|
||||
block.sampleBlock.samples = skip = block.length - 12;
|
||||
}
|
||||
|
||||
// Check whether we found a new highest rate
|
||||
if (_rate < block.sampleBlock.rate)
|
||||
_rate = block.sampleBlock.rate;
|
||||
break;
|
||||
|
||||
// Silence
|
||||
case 3: {
|
||||
if (block.length != 3) {
|
||||
warning("Invalid silence block length %d in VOC file", block.length);
|
||||
return;
|
||||
}
|
||||
|
||||
block.sampleBlock.offset = 0;
|
||||
|
||||
block.sampleBlock.samples = _stream->readUint16LE() + 1;
|
||||
int freqDiv = _stream->readByte();
|
||||
// Prevent division through 0
|
||||
if (freqDiv == 256) {
|
||||
warning("Invalid frequency divisor 256 in VOC file");
|
||||
return;
|
||||
}
|
||||
block.sampleBlock.rate = getSampleRateFromVOCRate(freqDiv);
|
||||
} break;
|
||||
|
||||
// Repeat start
|
||||
case 6:
|
||||
if (block.length != 2) {
|
||||
warning("Invalid repeat start block length %d in VOC file", block.length);
|
||||
return;
|
||||
}
|
||||
|
||||
block.loopBlock.count = _stream->readUint16LE() + 1;
|
||||
break;
|
||||
|
||||
// Repeat end
|
||||
case 7:
|
||||
break;
|
||||
|
||||
// Extra info
|
||||
case 8: {
|
||||
if (block.length != 4)
|
||||
return;
|
||||
|
||||
int freqDiv = _stream->readUint16LE();
|
||||
// Prevent division through 0
|
||||
if (freqDiv == 65536) {
|
||||
warning("Invalid frequency divisor 65536 in VOC file");
|
||||
return;
|
||||
}
|
||||
|
||||
int codec = _stream->readByte();
|
||||
// We only support RAW 8bit PCM.
|
||||
if (codec != 0) {
|
||||
warning("Unhandled codec %d in VOC file", codec);
|
||||
return;
|
||||
}
|
||||
|
||||
int channels = _stream->readByte() + 1;
|
||||
// We only support mono sound right now
|
||||
if (channels != 1) {
|
||||
warning("Unhandled channel count %d in VOC file", channels);
|
||||
return;
|
||||
}
|
||||
|
||||
block.sampleBlock.offset = 0;
|
||||
block.sampleBlock.samples = 0;
|
||||
block.sampleBlock.rate = 256000000L / (65536L - freqDiv);
|
||||
} break;
|
||||
|
||||
default:
|
||||
warning("Unhandled code %d in VOC file (len %d)", block.code, block.length);
|
||||
// Skip the whole block and try to use the next one.
|
||||
skip = block.length;
|
||||
}
|
||||
|
||||
// Premature end of stream => error!
|
||||
if (_stream->eos() || _stream->err()) {
|
||||
warning("VocStream::preProcess: Reading failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip the rest of the block
|
||||
if (skip)
|
||||
_stream->skip(skip);
|
||||
|
||||
_blocks.push_back(block);
|
||||
}
|
||||
|
||||
// Since we determined the sample rate we need for playback now, we will
|
||||
// initialize the play length.
|
||||
_length = Timestamp(0, _rate);
|
||||
|
||||
// Calculate the total play time and do some more sanity checks
|
||||
for (const auto &curBlock : _blocks) {
|
||||
// Check whether we found a block 8 which survived, this is not
|
||||
// allowed to happen!
|
||||
if (curBlock.code == 8) {
|
||||
warning("VOC file contains unused block 8");
|
||||
return;
|
||||
}
|
||||
|
||||
// For now only use blocks with actual samples
|
||||
if (curBlock.code != 1 && curBlock.code != 9)
|
||||
continue;
|
||||
|
||||
// Check the sample rate
|
||||
if (curBlock.sampleBlock.rate != _rate) {
|
||||
warning("VOC file contains chunks with different sample rates (%d != %d)", _rate, curBlock.sampleBlock.rate);
|
||||
return;
|
||||
}
|
||||
|
||||
_length = _length.addFrames(curBlock.sampleBlock.samples);
|
||||
}
|
||||
|
||||
// Set the current block to the first block in the stream
|
||||
rewind();
|
||||
}
|
||||
|
||||
int getSampleRateFromVOCRate(int vocSR) {
|
||||
if (vocSR == 0xa5 || vocSR == 0xa6) {
|
||||
return 11025;
|
||||
} else if (vocSR == 0xd2 || vocSR == 0xd3) {
|
||||
return 22050;
|
||||
} else {
|
||||
int sr = 1000000L / (256L - vocSR);
|
||||
// inexact sampling rates occur e.g. in the kitchen in Monkey Island,
|
||||
// very easy to reach right from the start of the game.
|
||||
//warning("inexact sample rate used: %i (0x%x)", sr, vocSR);
|
||||
return sr;
|
||||
}
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
if (!checkVOCHeader(*stream)) {
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SeekableAudioStream *audioStream = new VocStream(stream, (flags & Audio::FLAG_UNSIGNED) != 0, disposeAfterUse);
|
||||
|
||||
if (audioStream->endOfData()) {
|
||||
delete audioStream;
|
||||
return nullptr;
|
||||
} else {
|
||||
return audioStream;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
172
audio/decoders/voc.h
Normal file
172
audio/decoders/voc.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - chewy (subclass)
|
||||
* - kyra
|
||||
* - saga
|
||||
* - scumm
|
||||
* - touche
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_VOC_H
|
||||
#define AUDIO_VOC_H
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#include "common/list.h"
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class ReadStream;
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class AudioStream;
|
||||
class SeekableAudioStream;
|
||||
|
||||
|
||||
#include "common/pack-start.h" // START STRUCT PACKING
|
||||
|
||||
struct VocFileHeader {
|
||||
uint8 desc[20];
|
||||
uint16 datablock_offset;
|
||||
uint16 version;
|
||||
uint16 id;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
struct VocBlockHeader {
|
||||
uint8 blocktype;
|
||||
uint8 size[3];
|
||||
uint8 sr;
|
||||
uint8 pack;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
#include "common/pack-end.h" // END STRUCT PACKING
|
||||
|
||||
class VocStream : public SeekableAudioStream {
|
||||
public:
|
||||
VocStream(Common::SeekableReadStream *stream, bool isUnsigned, DisposeAfterUse::Flag disposeAfterUse);
|
||||
virtual ~VocStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool isStereo() const override { return false; }
|
||||
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
bool endOfData() const override { return (_curBlock == _blocks.end()) && (_blockLeft == 0); }
|
||||
|
||||
bool seek(const Timestamp &where) override;
|
||||
|
||||
Timestamp getLength() const override { return _length; }
|
||||
|
||||
protected:
|
||||
void preProcess();
|
||||
|
||||
Common::SeekableReadStream *const _stream;
|
||||
const DisposeAfterUse::Flag _disposeAfterUse;
|
||||
|
||||
const bool _isUnsigned;
|
||||
|
||||
int _rate;
|
||||
Timestamp _length;
|
||||
|
||||
struct Block {
|
||||
uint8 code;
|
||||
uint32 length;
|
||||
|
||||
union {
|
||||
struct {
|
||||
uint32 offset;
|
||||
int rate;
|
||||
int samples;
|
||||
} sampleBlock;
|
||||
|
||||
struct {
|
||||
int count;
|
||||
} loopBlock;
|
||||
};
|
||||
};
|
||||
|
||||
typedef Common::List<Block> BlockList;
|
||||
BlockList _blocks;
|
||||
|
||||
BlockList::const_iterator _curBlock;
|
||||
uint32 _blockLeft;
|
||||
|
||||
/**
|
||||
* Advance one block in the stream in case
|
||||
* the current one is empty.
|
||||
*/
|
||||
void updateBlockIfNeeded();
|
||||
|
||||
// Do some internal buffering for systems with really slow slow disk i/o
|
||||
enum {
|
||||
/**
|
||||
* How many samples we can buffer at once.
|
||||
*
|
||||
* TODO: Check whether this size suffices
|
||||
* for systems with slow disk I/O.
|
||||
*/
|
||||
kSampleBufferLength = 2048
|
||||
};
|
||||
byte _buffer[kSampleBufferLength];
|
||||
|
||||
/**
|
||||
* Fill the temporary sample buffer used in readBuffer.
|
||||
*
|
||||
* @param maxSamples Maximum samples to read.
|
||||
* @return actual count of samples read.
|
||||
*/
|
||||
int fillBuffer(int maxSamples);
|
||||
};
|
||||
|
||||
/**
|
||||
* Take a sample rate parameter as it occurs in a VOC sound header, and
|
||||
* return the corresponding sample frequency.
|
||||
*
|
||||
* This method has special cases for the standard rates of 11025 and 22050 kHz,
|
||||
* which due to limitations of the format, cannot be encoded exactly in a VOC
|
||||
* file. As a consequence, many game files have sound data sampled with those
|
||||
* rates, but the VOC marks them incorrectly as 11111 or 22222 kHz. This code
|
||||
* works around that and "unrounds" the sampling rates.
|
||||
*/
|
||||
extern int getSampleRateFromVOCRate(int vocSR);
|
||||
|
||||
/**
|
||||
* Try to load a VOC from the given seekable stream and create an AudioStream
|
||||
* from that data. Currently this function only supports uncompressed raw PCM
|
||||
* data.
|
||||
*
|
||||
* This does not use any of the looping features of VOC files!
|
||||
*/
|
||||
SeekableAudioStream *makeVOCStream(Common::SeekableReadStream *stream, byte flags, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::NO);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
252
audio/decoders/vorbis.cpp
Normal file
252
audio/decoders/vorbis.cpp
Normal file
@@ -0,0 +1,252 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Disable symbol overrides for FILE and fseek as those are used in the
|
||||
// Vorbis headers.
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_FILE
|
||||
#define FORBIDDEN_SYMBOL_EXCEPTION_fseek
|
||||
|
||||
#include "audio/decoders/vorbis.h"
|
||||
|
||||
#ifdef USE_VORBIS
|
||||
|
||||
#include "common/ptr.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/util.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
|
||||
#ifdef USE_TREMOR
|
||||
#include <tremor/ivorbisfile.h>
|
||||
#else
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#include <vorbis/vorbisfile.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// These are wrapper functions to allow using a SeekableReadStream object to
|
||||
// provide data to the OggVorbis_File object.
|
||||
|
||||
static size_t read_stream_wrap(void *ptr, size_t size, size_t nmemb, void *datasource) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
|
||||
uint32 result = stream->read(ptr, size * nmemb);
|
||||
|
||||
return result / size;
|
||||
}
|
||||
|
||||
static int seek_stream_wrap(void *datasource, ogg_int64_t offset, int whence) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
stream->seek((int32)offset, whence);
|
||||
return stream->pos();
|
||||
}
|
||||
|
||||
static int close_stream_wrap(void *datasource) {
|
||||
// Do nothing -- we leave it up to the VorbisStream to free memory as appropriate.
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long tell_stream_wrap(void *datasource) {
|
||||
Common::SeekableReadStream *stream = (Common::SeekableReadStream *)datasource;
|
||||
return stream->pos();
|
||||
}
|
||||
|
||||
static const ov_callbacks g_stream_wrap = {
|
||||
read_stream_wrap, seek_stream_wrap, close_stream_wrap, tell_stream_wrap
|
||||
};
|
||||
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Ogg Vorbis stream ---
|
||||
#pragma mark -
|
||||
|
||||
|
||||
class VorbisStream : public SeekableAudioStream {
|
||||
protected:
|
||||
Common::DisposablePtr<Common::SeekableReadStream> _inStream;
|
||||
|
||||
bool _isStereo;
|
||||
int _rate;
|
||||
|
||||
Timestamp _length;
|
||||
|
||||
OggVorbis_File _ovFile;
|
||||
|
||||
int16 _buffer[4096];
|
||||
const int16 *_bufferEnd;
|
||||
const int16 *_pos;
|
||||
|
||||
public:
|
||||
// startTime / duration are in milliseconds
|
||||
VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose);
|
||||
~VorbisStream();
|
||||
|
||||
int readBuffer(int16 *buffer, const int numSamples) override;
|
||||
|
||||
bool endOfData() const override { return _pos >= _bufferEnd; }
|
||||
bool isStereo() const override { return _isStereo; }
|
||||
int getRate() const override { return _rate; }
|
||||
|
||||
bool seek(const Timestamp &where) override;
|
||||
Timestamp getLength() const override { return _length; }
|
||||
protected:
|
||||
bool refill();
|
||||
};
|
||||
|
||||
VorbisStream::VorbisStream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) :
|
||||
_inStream(inStream, dispose),
|
||||
_length(0, 1000),
|
||||
_bufferEnd(ARRAYEND(_buffer)) {
|
||||
|
||||
int res = ov_open_callbacks(inStream, &_ovFile, nullptr, 0, g_stream_wrap);
|
||||
if (res < 0) {
|
||||
warning("Could not create Vorbis stream (%d)", res);
|
||||
_pos = _bufferEnd;
|
||||
return;
|
||||
}
|
||||
|
||||
// Read in initial data
|
||||
if (!refill())
|
||||
return;
|
||||
|
||||
// Setup some header information
|
||||
_isStereo = ov_info(&_ovFile, -1)->channels >= 2;
|
||||
_rate = ov_info(&_ovFile, -1)->rate;
|
||||
|
||||
#ifdef USE_TREMOR
|
||||
_length = Timestamp(ov_time_total(&_ovFile, -1), getRate());
|
||||
#else
|
||||
_length = Timestamp(uint32(ov_time_total(&_ovFile, -1) * 1000.0), getRate());
|
||||
#endif
|
||||
}
|
||||
|
||||
VorbisStream::~VorbisStream() {
|
||||
ov_clear(&_ovFile);
|
||||
}
|
||||
|
||||
int VorbisStream::readBuffer(int16 *buffer, const int numSamples) {
|
||||
int samples = 0;
|
||||
while (samples < numSamples && _pos < _bufferEnd) {
|
||||
const int len = MIN(numSamples - samples, (int)(_bufferEnd - _pos));
|
||||
memcpy(buffer, _pos, len * 2);
|
||||
buffer += len;
|
||||
_pos += len;
|
||||
samples += len;
|
||||
if (_pos >= _bufferEnd) {
|
||||
if (!refill())
|
||||
break;
|
||||
}
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
bool VorbisStream::seek(const Timestamp &where) {
|
||||
// Vorbisfile uses the sample pair number, thus we always use "false" for the isStereo parameter
|
||||
// of the convertTimeToStreamPos helper.
|
||||
int res = ov_pcm_seek(&_ovFile, convertTimeToStreamPos(where, getRate(), false).totalNumberOfFrames());
|
||||
if (res) {
|
||||
warning("Error seeking in Vorbis stream (%d)", res);
|
||||
_pos = _bufferEnd;
|
||||
return false;
|
||||
}
|
||||
|
||||
return refill();
|
||||
}
|
||||
|
||||
bool VorbisStream::refill() {
|
||||
// Read the samples
|
||||
uint len_left = sizeof(_buffer);
|
||||
char *read_pos = (char *)_buffer;
|
||||
|
||||
while (len_left > 0) {
|
||||
long result;
|
||||
|
||||
#ifdef USE_TREMOR
|
||||
// Tremor ov_read() always returns data as signed 16 bit interleaved PCM
|
||||
// in host byte order. As such, it does not take arguments to request
|
||||
// specific signedness, byte order or bit depth as in Vorbisfile.
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
NULL);
|
||||
#else
|
||||
#ifdef SCUMM_BIG_ENDIAN
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
1,
|
||||
2, // 16 bit
|
||||
1, // signed
|
||||
NULL);
|
||||
#else
|
||||
result = ov_read(&_ovFile, read_pos, len_left,
|
||||
0,
|
||||
2, // 16 bit
|
||||
1, // signed
|
||||
nullptr);
|
||||
#endif
|
||||
#endif
|
||||
if (result == OV_HOLE) {
|
||||
// Possibly recoverable, just warn about it
|
||||
warning("Corrupted data in Vorbis file");
|
||||
} else if (result == 0) {
|
||||
//warning("End of file while reading from Vorbis file");
|
||||
//_pos = _bufferEnd;
|
||||
//return false;
|
||||
break;
|
||||
} else if (result < 0) {
|
||||
warning("Error reading from Vorbis stream (%d)", int(result));
|
||||
_pos = _bufferEnd;
|
||||
// Don't delete it yet, that causes problems in
|
||||
// the CD player emulation code.
|
||||
return false;
|
||||
} else {
|
||||
len_left -= result;
|
||||
read_pos += result;
|
||||
}
|
||||
}
|
||||
|
||||
_pos = _buffer;
|
||||
_bufferEnd = (int16 *)read_pos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark --- Ogg Vorbis factory functions ---
|
||||
#pragma mark -
|
||||
|
||||
SeekableAudioStream *makeVorbisStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse) {
|
||||
SeekableAudioStream *s = new VorbisStream(stream, disposeAfterUse);
|
||||
if (s && s->endOfData()) {
|
||||
delete s;
|
||||
return nullptr;
|
||||
} else {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_VORBIS
|
||||
73
audio/decoders/vorbis.h
Normal file
73
audio/decoders/vorbis.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - agos
|
||||
* - draci
|
||||
* - kyra
|
||||
* - qdengine
|
||||
* - queen
|
||||
* - saga
|
||||
* - sci
|
||||
* - scumm
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - sword25
|
||||
* - touche
|
||||
* - tucker
|
||||
* - vcruise
|
||||
* - wintermute
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_VORBIS_H
|
||||
#define AUDIO_VORBIS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
#ifdef USE_VORBIS
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Create a new SeekableAudioStream from the Ogg Vorbis data in the given stream.
|
||||
* Allows for seeking (which is why we require a SeekableReadStream).
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the Ogg Vorbis data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeVorbisStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // #ifdef USE_VORBIS
|
||||
#endif // #ifndef AUDIO_VORBIS_H
|
||||
280
audio/decoders/wave.cpp
Normal file
280
audio/decoders/wave.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "common/debug.h"
|
||||
#include "common/textconsole.h"
|
||||
#include "common/stream.h"
|
||||
#include "common/substream.h"
|
||||
|
||||
#include "audio/audiostream.h"
|
||||
#include "audio/decoders/wave_types.h"
|
||||
#include "audio/decoders/wave.h"
|
||||
#include "audio/decoders/adpcm.h"
|
||||
#include "audio/decoders/mp3.h"
|
||||
#include "audio/decoders/raw.h"
|
||||
#include "audio/decoders/g711.h"
|
||||
|
||||
#define EXT_CHUNKS 8
|
||||
|
||||
namespace Audio {
|
||||
|
||||
bool loadWAVFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags, uint16 *wavType, int *blockAlign_, int *samplesPerBlock_) {
|
||||
const int32 initialPos = stream.pos();
|
||||
byte buf[4+1];
|
||||
|
||||
buf[4] = 0;
|
||||
|
||||
const char *extensionChunks[EXT_CHUNKS] = {
|
||||
"JUNK",
|
||||
"bext",
|
||||
"iXML",
|
||||
"qlty",
|
||||
"mext",
|
||||
"levl",
|
||||
"link",
|
||||
"axml"
|
||||
};
|
||||
|
||||
stream.read(buf, 4);
|
||||
if (memcmp(buf, "RIFF", 4) != 0) {
|
||||
warning("getWavInfo: No 'RIFF' header");
|
||||
return false;
|
||||
}
|
||||
|
||||
int32 wavLength = stream.readUint32LE();
|
||||
|
||||
stream.read(buf, 4);
|
||||
if (memcmp(buf, "WAVE", 4) != 0) {
|
||||
warning("getWavInfo: No 'WAVE' header");
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.read(buf, 4);
|
||||
if (memcmp(buf, "fact", 4) == 0) {
|
||||
// Initial fact chunk, so skip over it
|
||||
uint32 factLen = stream.readUint32LE();
|
||||
stream.skip(factLen);
|
||||
stream.read(buf, 4);
|
||||
}
|
||||
|
||||
while (1) { // skip junk/bext... chunks
|
||||
int i;
|
||||
for (i = 0; (i < EXT_CHUNKS) && (memcmp(buf, extensionChunks[i], 4) != 0); i++)
|
||||
;
|
||||
if (i != EXT_CHUNKS) { // found a known chunk
|
||||
uint32 chunkSize = stream.readUint32LE();
|
||||
// skip junk/ext chunk (add 1 byte if odd)
|
||||
stream.skip(chunkSize + (chunkSize % 2));
|
||||
stream.read(buf, 4);
|
||||
debug(0, "Skipped %s chunk in wav file!", extensionChunks[i]);
|
||||
} else // skipped all chunks, or found something unexpected
|
||||
break;
|
||||
}
|
||||
|
||||
if (memcmp(buf, "fmt ", 4) != 0) {
|
||||
warning("getWavInfo: No 'fmt' header! Found %s", buf);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 fmtLength = stream.readUint32LE();
|
||||
if (fmtLength < 16) {
|
||||
// A valid fmt chunk always contains at least 16 bytes
|
||||
warning("getWavInfo: 'fmt' header is too short");
|
||||
return false;
|
||||
}
|
||||
uint32 fmtRemaining = fmtLength;
|
||||
|
||||
// Next comes the "type" field of the fmt header. Some typical
|
||||
// values for it:
|
||||
// 1 -> uncompressed PCM
|
||||
// 17 -> IMA ADPCM compressed WAVE
|
||||
// See <https://mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html>
|
||||
// for a more complete list of common WAVE compression formats...
|
||||
uint16 type = stream.readUint16LE(); // == 1 for PCM data
|
||||
uint16 numChannels = stream.readUint16LE(); // 1 for mono, 2 for stereo
|
||||
uint32 samplesPerSec = stream.readUint32LE(); // in Hz
|
||||
uint32 avgBytesPerSec = stream.readUint32LE(); // == SampleRate * NumChannels * BitsPerSample/8
|
||||
|
||||
uint16 blockAlign = stream.readUint16LE(); // == NumChannels * BitsPerSample/8
|
||||
uint16 bitsPerSample = stream.readUint16LE(); // 8, 16 ...
|
||||
// 8 bit data is unsigned, 16 bit data signed
|
||||
fmtRemaining -= 16;
|
||||
|
||||
uint16 samplesPerBlock = 1;
|
||||
if (type == kWaveFormatMSADPCM) {
|
||||
// TODO: There is a samplesPerBlock in this header. It should be parsed and the below warning removed.
|
||||
// (NB: The FMT header for MSADPCM has different information from the MSIMAADPCM)
|
||||
warning("getWavInfo: 'fmt' header not parsed in entirety for MSADPCM");
|
||||
} else if (type == kWaveFormatMSIMAADPCM) {
|
||||
if (fmtRemaining != 4) {
|
||||
// A valid IMA ADPCM fmt chunk is always 20 bytes long
|
||||
warning("getWavInfo: 'fmt' header is wrong length for IMA ADPCM");
|
||||
return false;
|
||||
}
|
||||
stream.readUint16LE(); // cbSize
|
||||
samplesPerBlock = stream.readUint16LE();
|
||||
fmtRemaining -= 4;
|
||||
}
|
||||
|
||||
if (wavType != nullptr)
|
||||
*wavType = type;
|
||||
|
||||
if (blockAlign_ != nullptr)
|
||||
*blockAlign_ = blockAlign;
|
||||
|
||||
if (samplesPerBlock_ != nullptr)
|
||||
*samplesPerBlock_ = samplesPerBlock;
|
||||
#if 0
|
||||
debug("WAVE information:");
|
||||
debug(" total size: %d", wavLength);
|
||||
debug(" fmt size: %d", fmtLength);
|
||||
debug(" type: %d", type);
|
||||
debug(" numChannels: %d", numChannels);
|
||||
debug(" samplesPerSec: %d", samplesPerSec);
|
||||
debug(" avgBytesPerSec: %d", avgBytesPerSec);
|
||||
debug(" blockAlign: %d", blockAlign);
|
||||
debug(" bitsPerSample: %d", bitsPerSample);
|
||||
#endif
|
||||
|
||||
switch (type) {
|
||||
case kWaveFormatPCM:
|
||||
case kWaveFormatMSADPCM:
|
||||
case kWaveFormatALawPCM:
|
||||
case kWaveFormatMuLawPCM:
|
||||
case kWaveFormatMSIMAADPCM:
|
||||
#ifdef USE_MAD
|
||||
case kWaveFormatMP3:
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
warning("getWavInfo: unsupported format (type %d)", type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type == kWaveFormatMP3) {
|
||||
bitsPerSample = 8;
|
||||
} else if (type != kWaveFormatMSADPCM && type != kWaveFormatMSIMAADPCM) {
|
||||
if (blockAlign != numChannels * bitsPerSample / 8) {
|
||||
debug(0, "getWavInfo: blockAlign is invalid");
|
||||
}
|
||||
|
||||
if (avgBytesPerSec != samplesPerSec * blockAlign) {
|
||||
debug(0, "getWavInfo: avgBytesPerSec is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
// Prepare the return values.
|
||||
rate = samplesPerSec;
|
||||
|
||||
flags = 0;
|
||||
if (bitsPerSample == 8) // 8 bit data is unsigned
|
||||
flags |= Audio::FLAG_UNSIGNED;
|
||||
else if (bitsPerSample == 16) // 16 bit data is signed little endian
|
||||
flags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
|
||||
else if (bitsPerSample == 24) // 24 bit data is signed little endian
|
||||
flags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
|
||||
else if (bitsPerSample == 4 && (type == kWaveFormatMSADPCM || type == kWaveFormatMSIMAADPCM))
|
||||
flags |= Audio::FLAG_16BITS;
|
||||
else {
|
||||
warning("getWavInfo: unsupported bitsPerSample %d", bitsPerSample);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (numChannels == 2)
|
||||
flags |= Audio::FLAG_STEREO;
|
||||
else if (numChannels != 1) {
|
||||
warning("getWavInfo: unsupported number of channels %d", numChannels);
|
||||
return false;
|
||||
}
|
||||
|
||||
// It's almost certainly a WAV file, but we still need to find its
|
||||
// 'data' chunk.
|
||||
|
||||
// Skip over the rest of the fmt chunk.
|
||||
int offset = fmtRemaining;
|
||||
|
||||
do {
|
||||
stream.seek(offset, SEEK_CUR);
|
||||
if (stream.pos() >= initialPos + wavLength + 8) {
|
||||
warning("getWavInfo: Can't find 'data' chunk");
|
||||
return false;
|
||||
}
|
||||
stream.read(buf, 4);
|
||||
offset = stream.readUint32LE();
|
||||
|
||||
#if 0
|
||||
debug(" found a '%s' tag of size %d", buf, offset);
|
||||
#endif
|
||||
} while (memcmp(buf, "data", 4) != 0);
|
||||
|
||||
// Stream now points at 'offset' bytes of sample data...
|
||||
size = offset;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SeekableAudioStream *makeWAVStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
|
||||
int size, rate;
|
||||
byte flags;
|
||||
uint16 type;
|
||||
int blockAlign;
|
||||
|
||||
if (!loadWAVFromStream(*stream, size, rate, flags, &type, &blockAlign)) {
|
||||
if (disposeAfterUse == DisposeAfterUse::YES)
|
||||
delete stream;
|
||||
return nullptr;
|
||||
}
|
||||
int channels = (flags & Audio::FLAG_STEREO) ? 2 : 1;
|
||||
int bytesPerSample = (flags & Audio::FLAG_24BITS) ? 3 : ((flags & Audio::FLAG_16BITS) ? 2 : 1);
|
||||
|
||||
// Raw PCM, make sure the last packet is complete
|
||||
if (type == kWaveFormatPCM) {
|
||||
uint sampleSize = bytesPerSample * channels;
|
||||
if (size % sampleSize != 0) {
|
||||
warning("makeWAVStream: Trying to play a WAVE file with an incomplete PCM packet");
|
||||
size &= ~(sampleSize - 1);
|
||||
}
|
||||
}
|
||||
Common::SeekableReadStream *dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + size, disposeAfterUse);
|
||||
|
||||
switch (type) {
|
||||
case kWaveFormatMSIMAADPCM:
|
||||
return makeADPCMStream(dataStream, DisposeAfterUse::YES, 0, Audio::kADPCMMSIma, rate, channels, blockAlign);
|
||||
case kWaveFormatMSADPCM:
|
||||
return makeADPCMStream(dataStream, DisposeAfterUse::YES, 0, Audio::kADPCMMS, rate, channels, blockAlign);
|
||||
#ifdef USE_MAD
|
||||
case kWaveFormatMP3:
|
||||
return makeMP3Stream(dataStream, DisposeAfterUse::YES);
|
||||
#endif
|
||||
case kWaveFormatALawPCM:
|
||||
return makeALawStream(dataStream, DisposeAfterUse::YES, rate, channels);
|
||||
case kWaveFormatMuLawPCM:
|
||||
return makeMuLawStream(dataStream, DisposeAfterUse::YES, rate, channels);
|
||||
case kWaveFormatPCM:
|
||||
return makeRawStream(dataStream, rate, flags);
|
||||
}
|
||||
|
||||
// If the format is unsupported, we already returned earlier, but just in case
|
||||
delete dataStream;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // End of namespace Audio
|
||||
98
audio/decoders/wave.h
Normal file
98
audio/decoders/wave.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Sound decoder used in engines:
|
||||
* - access
|
||||
* - agos
|
||||
* - buried
|
||||
* - cge
|
||||
* - cge2
|
||||
* - fullpipe
|
||||
* - glk
|
||||
* - gob
|
||||
* - hopkins
|
||||
* - mohawk
|
||||
* - prince
|
||||
* - qdengine
|
||||
* - saga
|
||||
* - sci
|
||||
* - scumm
|
||||
* - sherlock
|
||||
* - sword1
|
||||
* - sword2
|
||||
* - titanic
|
||||
* - tony
|
||||
* - trecision
|
||||
* - tucker
|
||||
* - wintermute
|
||||
* - zvision
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_WAVE_H
|
||||
#define AUDIO_WAVE_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "common/types.h"
|
||||
|
||||
namespace Common {
|
||||
class SeekableReadStream;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
class SeekableAudioStream;
|
||||
|
||||
/**
|
||||
* Try to load a WAVE from the given seekable stream. Returns true if
|
||||
* successful. In that case, the stream's seek position will be set to the
|
||||
* start of the audio data, and size, rate and flags contain information
|
||||
* necessary for playback. Currently this function supports uncompressed
|
||||
* raw PCM data, MS IMA ADPCM and MS ADPCM (uses makeADPCMStream internally).
|
||||
*/
|
||||
extern bool loadWAVFromStream(
|
||||
Common::SeekableReadStream &stream,
|
||||
int &size,
|
||||
int &rate,
|
||||
byte &flags,
|
||||
uint16 *wavType = 0,
|
||||
int *blockAlign = 0,
|
||||
int *samplesPerBlock = 0);
|
||||
|
||||
/**
|
||||
* Try to load a WAVE from the given seekable stream and create an AudioStream
|
||||
* from that data. Currently this function supports uncompressed
|
||||
* raw PCM data, MS IMA ADPCM and MS ADPCM (uses makeADPCMStream internally).
|
||||
*
|
||||
* This function uses loadWAVFromStream() internally.
|
||||
*
|
||||
* @param stream the SeekableReadStream from which to read the WAVE data
|
||||
* @param disposeAfterUse whether to delete the stream after use
|
||||
* @return a new SeekableAudioStream, or NULL, if an error occurred
|
||||
*/
|
||||
SeekableAudioStream *makeWAVStream(
|
||||
Common::SeekableReadStream *stream,
|
||||
DisposeAfterUse::Flag disposeAfterUse);
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
44
audio/decoders/wave_types.h
Normal file
44
audio/decoders/wave_types.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef AUDIO_DECODERS_WAVE_TYPES_H
|
||||
#define AUDIO_DECODERS_WAVE_TYPES_H
|
||||
|
||||
namespace Audio {
|
||||
|
||||
// Audio Codecs
|
||||
enum WaveCompressionType {
|
||||
kWaveFormatNone = 0x0000,
|
||||
kWaveFormatPCM = 0x0001,
|
||||
kWaveFormatMSADPCM = 0x0002,
|
||||
kWaveFormatALawPCM = 0x0006,
|
||||
kWaveFormatMuLawPCM = 0x0007,
|
||||
kWaveFormatMSIMAADPCM = 0x0011,
|
||||
kWaveFormatMP3 = 0x0055,
|
||||
kWaveFormatDK3 = 0x0062, // rogue format number
|
||||
kWaveFormatMSIMAADPCM2 = 0x0069,
|
||||
kWaveFormatWMAv2 = 0x0161,
|
||||
kWaveFormatXanDPCM = 0x594a // 'JY', Crusader: No Regret videos
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif
|
||||
1512
audio/decoders/wma.cpp
Normal file
1512
audio/decoders/wma.cpp
Normal file
File diff suppressed because it is too large
Load Diff
225
audio/decoders/wma.h
Normal file
225
audio/decoders/wma.h
Normal file
@@ -0,0 +1,225 @@
|
||||
/* ScummVM - Graphic Adventure Engine
|
||||
*
|
||||
* ScummVM is the legal property of its developers, whose names
|
||||
* are too numerous to list here. Please refer to the COPYRIGHT
|
||||
* file distributed with this source distribution.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Based on xoreos' WMA code which is in turn
|
||||
// Largely based on the WMA implementation found in FFmpeg.
|
||||
|
||||
#ifndef AUDIO_DECODERS_WMA_H
|
||||
#define AUDIO_DECODERS_WMA_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/bitstream.h"
|
||||
|
||||
#include "audio/decoders/codec.h"
|
||||
|
||||
namespace Common {
|
||||
template <class BITSTREAM>
|
||||
class Huffman;
|
||||
}
|
||||
|
||||
namespace Math {
|
||||
class MDCT;
|
||||
}
|
||||
|
||||
namespace Audio {
|
||||
|
||||
struct WMACoefHuffmanParam;
|
||||
|
||||
class WMACodec : public Codec {
|
||||
public:
|
||||
WMACodec(int version, uint32 sampleRate, uint8 channels,
|
||||
uint32 bitRate, uint32 blockAlign, Common::SeekableReadStream *extraData = 0);
|
||||
~WMACodec();
|
||||
|
||||
AudioStream *decodeFrame(Common::SeekableReadStream &data);
|
||||
|
||||
private:
|
||||
static const int kChannelsMax = 2; ///< Max number of channels we support.
|
||||
|
||||
static const int kBlockBitsMin = 7; ///< Min number of bits in a block.
|
||||
static const int kBlockBitsMax = 11; ///< Max number of bits in a block.
|
||||
|
||||
/** Max number of bytes in a block. */
|
||||
static const int kBlockSizeMax = (1 << kBlockBitsMax);
|
||||
|
||||
static const int kBlockNBSizes = (kBlockBitsMax - kBlockBitsMin + 1);
|
||||
|
||||
/** Max size of a superframe. */
|
||||
static const int kSuperframeSizeMax = 16384;
|
||||
|
||||
/** Max size of a high band. */
|
||||
static const int kHighBandSizeMax = 16;
|
||||
|
||||
/** Size of the noise table. */
|
||||
static const int kNoiseTabSize = 8192;
|
||||
|
||||
/** Number of bits for the LSP power value. */
|
||||
static const int kLSPPowBits = 7;
|
||||
|
||||
int _version; ///< WMA version.
|
||||
|
||||
uint32 _sampleRate; ///< Output sample rate.
|
||||
uint8 _channels; ///< Output channel count.
|
||||
uint32 _bitRate; ///< Input bit rate.
|
||||
uint32 _blockAlign; ///< Input block align.
|
||||
byte _audioFlags; ///< Output flags.
|
||||
|
||||
bool _useExpHuffman; ///< Exponents in Huffman code? Otherwise, in LSP.
|
||||
bool _useBitReservoir; ///< Is each frame packet a "superframe"?
|
||||
bool _useVariableBlockLen; ///< Are the block lengths variable?
|
||||
bool _useNoiseCoding; ///< Should perceptual noise be added?
|
||||
|
||||
bool _resetBlockLengths; ///< Do we need new block lengths?
|
||||
|
||||
int _curFrame; ///< The number of the frame we're currently in.
|
||||
int _frameLen; ///< The frame length.
|
||||
int _frameLenBits; ///< log2 of the frame length.
|
||||
int _blockSizeCount; ///< Number of block sizes.
|
||||
int _framePos; ///< The position within the frame we're currently in.
|
||||
|
||||
int _curBlock; ///< The number of the block we're currently in.
|
||||
int _blockLen; ///< Current block length.
|
||||
int _blockLenBits; ///< log2 of current block length.
|
||||
int _nextBlockLenBits; ///< log2 of next block length.
|
||||
int _prevBlockLenBits; ///< log2 of previous block length.
|
||||
|
||||
int _byteOffsetBits;
|
||||
|
||||
// Coefficients
|
||||
int _coefsStart; ///< First coded coef
|
||||
int _coefsEnd[kBlockNBSizes]; ///< Max number of coded coefficients
|
||||
int _exponentSizes[kBlockNBSizes];
|
||||
uint16 _exponentBands[kBlockNBSizes][25];
|
||||
int _highBandStart[kBlockNBSizes]; ///< Index of first coef in high band
|
||||
int _exponentHighSizes[kBlockNBSizes];
|
||||
int _exponentHighBands[kBlockNBSizes][kHighBandSizeMax];
|
||||
|
||||
typedef Common::Huffman<Common::BitStream8MSB> HuffmanDecoder;
|
||||
HuffmanDecoder *_coefHuffman[2]; ///< Coefficients Huffman codes.
|
||||
const WMACoefHuffmanParam *_coefHuffmanParam[2]; ///< Params for coef Huffman codes.
|
||||
|
||||
uint16 *_coefHuffmanRunTable[2]; ///< Run table for the coef Huffman.
|
||||
float *_coefHuffmanLevelTable[2]; ///< Level table for the coef Huffman.
|
||||
uint16 *_coefHuffmanIntTable[2]; ///< Int tablre for the coef Huffman.
|
||||
|
||||
// Noise
|
||||
float _noiseMult; ///< Noise multiplier.
|
||||
float _noiseTable[kNoiseTabSize]; ///< Noise table.
|
||||
int _noiseIndex;
|
||||
|
||||
HuffmanDecoder *_hgainHuffman; ///< Perceptual noise huffman code.
|
||||
|
||||
// Exponents
|
||||
int _exponentsBSize[kChannelsMax];
|
||||
float _exponents[kChannelsMax][kBlockSizeMax];
|
||||
float _maxExponent[kChannelsMax];
|
||||
|
||||
HuffmanDecoder *_expHuffman; ///< Exponents huffman code.
|
||||
|
||||
// Coded values in high bands
|
||||
bool _highBandCoded [kChannelsMax][kHighBandSizeMax];
|
||||
int _highBandValues[kChannelsMax][kHighBandSizeMax];
|
||||
|
||||
// Coefficients
|
||||
float _coefs1[kChannelsMax][kBlockSizeMax];
|
||||
float _coefs [kChannelsMax][kBlockSizeMax];
|
||||
|
||||
// Line spectral pairs
|
||||
float _lspCosTable[kBlockSizeMax];
|
||||
float _lspPowETable[256];
|
||||
float _lspPowMTable1[(1 << kLSPPowBits)];
|
||||
float _lspPowMTable2[(1 << kLSPPowBits)];
|
||||
|
||||
// MDCT
|
||||
Common::Array<Math::MDCT *> _mdct; ///< MDCT contexts.
|
||||
Common::Array<const float *> _mdctWindow; ///< MDCT window functions.
|
||||
|
||||
/** Overhang from the last superframe. */
|
||||
byte _lastSuperframe[kSuperframeSizeMax + 4];
|
||||
int _lastSuperframeLen; ///< Size of the overhang data. */
|
||||
int _lastBitoffset; ///< Bit position within the overhang. */
|
||||
|
||||
// Output
|
||||
float _output[kBlockSizeMax * 2];
|
||||
float _frameOut[kChannelsMax][kBlockSizeMax * 2];
|
||||
|
||||
|
||||
// Init helpers
|
||||
|
||||
void init(Common::SeekableReadStream *extraData);
|
||||
|
||||
uint16 getFlags(Common::SeekableReadStream *extraData);
|
||||
void evalFlags(uint16 flags, Common::SeekableReadStream *extraData);
|
||||
int getFrameBitLength();
|
||||
int getBlockSizeCount(uint16 flags);
|
||||
uint32 getNormalizedSampleRate();
|
||||
bool useNoiseCoding(float &highFreq, float &bps);
|
||||
void evalMDCTScales(float highFreq);
|
||||
void initNoise();
|
||||
void initCoefHuffman(float bps);
|
||||
void initMDCT();
|
||||
void initExponents();
|
||||
|
||||
HuffmanDecoder *initCoefHuffman(uint16 *&runTable, float *&levelTable,
|
||||
uint16 *&intTable, const WMACoefHuffmanParam ¶ms);
|
||||
void initLSPToCurve();
|
||||
|
||||
// Decoding
|
||||
|
||||
Common::SeekableReadStream *decodeSuperFrame(Common::SeekableReadStream &data);
|
||||
bool decodeFrame(Common::BitStream8MSB &bits, int16 *outputData);
|
||||
int decodeBlock(Common::BitStream8MSB &bits);
|
||||
|
||||
// Decoding helpers
|
||||
|
||||
bool evalBlockLength(Common::BitStream8MSB &bits);
|
||||
bool decodeChannels(Common::BitStream8MSB &bits, int bSize, bool msStereo, bool *hasChannel);
|
||||
bool calculateIMDCT(int bSize, bool msStereo, bool *hasChannel);
|
||||
|
||||
void calculateCoefCount(int *coefCount, int bSize) const;
|
||||
bool decodeNoise(Common::BitStream8MSB &bits, int bSize, bool *hasChannel, int *coefCount);
|
||||
bool decodeExponents(Common::BitStream8MSB &bits, int bSize, bool *hasChannel);
|
||||
bool decodeSpectralCoef(Common::BitStream8MSB &bits, bool msStereo, bool *hasChannel,
|
||||
int *coefCount, int coefBitCount);
|
||||
float getNormalizedMDCTLength() const;
|
||||
void calculateMDCTCoefficients(int bSize, bool *hasChannel,
|
||||
int *coefCount, int totalGain, float mdctNorm);
|
||||
|
||||
bool decodeExpHuffman(Common::BitStream8MSB &bits, int ch);
|
||||
bool decodeExpLSP(Common::BitStream8MSB &bits, int ch);
|
||||
bool decodeRunLevel(Common::BitStream8MSB &bits, const HuffmanDecoder &huffman,
|
||||
const float *levelTable, const uint16 *runTable, int version, float *ptr,
|
||||
int offset, int numCoefs, int blockLen, int frameLenBits, int coefNbBits);
|
||||
|
||||
void lspToCurve(float *out, float *val_max_ptr, int n, float *lsp);
|
||||
|
||||
void window(float *out) const;
|
||||
|
||||
float pow_m1_4(float x) const;
|
||||
|
||||
static int readTotalGain(Common::BitStream8MSB &bits);
|
||||
static int totalGainToBits(int totalGain);
|
||||
static uint32 getLargeVal(Common::BitStream8MSB &bits);
|
||||
};
|
||||
|
||||
} // End of namespace Audio
|
||||
|
||||
#endif // AUDIO_DECODERS_WMA_H
|
||||
1434
audio/decoders/wmadata.h
Normal file
1434
audio/decoders/wmadata.h
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user