Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

23
.clang-format Normal file
View 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
View 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
View 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
View 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
View File

@@ -0,0 +1 @@
custom: "https://www.scummvm.org/donate-with-paypal"

34
.github/pull_request_template.md vendored Normal file
View 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
```
--->

View 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

View 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")

View 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
View 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
View 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
View 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
View 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
View 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
View 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

1315
AUTHORS Normal file

File diff suppressed because it is too large Load Diff

12
CONTRIBUTING.md Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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.

View 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
View 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
View 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

4544
NEWS.md Normal file

File diff suppressed because it is too large Load Diff

80
README.md Normal file
View File

@@ -0,0 +1,80 @@
# [ScummVM README](https://www.scummvm.org/) · [![CI](https://github.com/scummvm/scummvm/actions/workflows/ci.yml/badge.svg)](https://github.com/scummvm/scummvm/actions/workflows/ci.yml) [![Translation status](https://translations.scummvm.org/widgets/scummvm/-/scummvm/svg-badge.svg)](https://translations.scummvm.org/engage/scummvm/?utm_source=widget) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md#pull-requests) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/e06e5b18f8464fef859b5a7f78d10357)](https://www.codacy.com/gh/scummvm/scummvm/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=scummvm/scummvm&amp;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
View 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

File diff suppressed because it is too large Load Diff

575
audio/adlib_ctmidi.cpp Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

1290
audio/adlib_ms.h Normal file

File diff suppressed because it is too large Load Diff

358
audio/alsa_opl.cpp Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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;
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

53
audio/decoders/qdm2.h Normal file
View 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
View 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

View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

File diff suppressed because it is too large Load Diff

225
audio/decoders/wma.h Normal file
View 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 &params);
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

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